diff --git a/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt b/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt index 73db95c..3275b72 100644 --- a/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt +++ b/Examples/DemoApp/buildSrc/src/main/kotlin/Config.kt @@ -37,31 +37,16 @@ object Libs { private object Versions { const val timber = "4.7.1" const val koin = "2.0.1" - const val uniflow = "0.9.3" const val coil = "0.9.2" const val jsr310 = "1.2.1" } const val timber = "com.jakewharton.timber:timber:${Versions.timber}" const val koinAndroid = "org.koin:koin-androidx-viewmodel:${Versions.koin}" - const val uniflow = "io.uniflow:uniflow-androidx:${Versions.uniflow}" const val coil = "io.coil-kt:coil:${Versions.coil}" const val jsr310 = "com.jakewharton.threetenabp:threetenabp:${Versions.jsr310}" } -object Google { - - private object Versions { - const val material = "1.1.0-beta02" - const val firebaseCore = "17.2.1" - const val firebaseMessaging = "20.0.1" - } - - const val material = "com.google.android.material:material:${Versions.material}" - const val firebaseCore = "com.google.firebase:firebase-core:${Versions.firebaseCore}" - const val firebaseMessaging = "com.google.firebase:firebase-messaging:${Versions.firebaseMessaging}" -} - object AndroidX { diff --git a/KomposeCli/Templates/.gitignore b/KomposeCli/Templates/.gitignore new file mode 100644 index 0000000..e09966d --- /dev/null +++ b/KomposeCli/Templates/.gitignore @@ -0,0 +1,36 @@ +# built application files +*.apk +*.aab + +# files for the dex VM +*.dex + +# Java class files +*.class + +# generated files +bin/ +gen/ + +# Local configuration file +local.properties + +# Intellij project files +*.iml +*.ipr +*.iws +.idea/ +*.prefs + +# Gradle +build/ +.gradle + +# Keys +*.jks +google-services.json + +# iOS Frameworks +iosApp/Frameworks +xcuserdata/ + diff --git a/KomposeCli/Templates/androidApp/.gitignore b/KomposeCli/Templates/androidApp/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/KomposeCli/Templates/androidApp/.gitignore @@ -0,0 +1 @@ +/build diff --git a/KomposeCli/Templates/androidApp/build.gradle.kts b/KomposeCli/Templates/androidApp/build.gradle.kts new file mode 100644 index 0000000..5f13d90 --- /dev/null +++ b/KomposeCli/Templates/androidApp/build.gradle.kts @@ -0,0 +1,99 @@ +/* + * Copyright $$YEAR$$ $$AUTHOR$$ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + id("com.android.application") + kotlin("android") +} + +val versionMajor = 0 +val versionMinor = 0 +val versionPatch = 1 + +android { + compileSdkVersion(Android.compileSdkVersion) + + defaultConfig { + applicationId = "$$PROJECT_PACKAGE$$.app" + versionCode = versionMajor * 100 + versionMinor * 10 + versionPatch + versionName = "$versionMajor.$versionMinor.$versionPatch" + vectorDrawables.useSupportLibrary = true + minSdkVersion(Android.minSdkVersion) + targetSdkVersion(Android.targetSdkVersion) + resConfigs("fr") + } + + buildTypes { + getByName("release") { + isShrinkResources = true + isMinifyEnabled = true + isDebuggable = false + isJniDebuggable = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + + viewBinding { isEnabled = true } + + bundle { + language { + enableSplit = true + } + density { + enableSplit = true + } + abi { + enableSplit = true + } + } + + sourceSets { + getByName("main").java.srcDirs("src/main/kotlin") + } + + packagingOptions { + exclude("**/*.kotlin_module") + exclude("**/*.version") + exclude("**/kotlin/**") + exclude("**/*.txt") + exclude("**/*.xml") + exclude("**/*.properties") + } + + buildFeatures { + compose = true + } +} + +dependencies { + implementation(project(":shared")) + implementation(kotlin("stdlib", Build.Versions.kotlin)) + implementation(Coroutines.core) + implementation(Coroutines.android) + implementation(AndroidX.appCompat) + implementation(AndroidX.coreKtx) + implementation(Libs.koinAndroid) + implementation(Libs.timber) + implementation(Libs.coil) + implementation(AndroidX.composeFoundation) + implementation(AndroidX.composeFramework) + implementation(AndroidX.composeLayout) + implementation(AndroidX.composeMaterial) + implementation(AndroidX.composeTooling) +} diff --git a/KomposeCli/Templates/androidApp/proguard-rules.pro b/KomposeCli/Templates/androidApp/proguard-rules.pro new file mode 100644 index 0000000..37e622b --- /dev/null +++ b/KomposeCli/Templates/androidApp/proguard-rules.pro @@ -0,0 +1,47 @@ +-dontwarn java.lang.invoke.* + + -keep public class * implements com.bumptech.glide.module.GlideModule +-keep public class * extends com.bumptech.glide.module.AppGlideModule +-keep public enum com.bumptech.glide.load.ImageHeaderParser$** { + **[] $VALUES; + public *; +} + + # Most of volatile fields are updated with AFU and should not be mangled +-keepclassmembernames class kotlinx.** { + volatile ; +} +# ServiceLoader support +-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} +-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} + + # OkHttp +-keepattributes Signature +-keepattributes *Annotation* +-keep class okhttp3.** { *; } +-keep interface okhttp3.** { *; } +-dontwarn okhttp3.** + + # Retrofit does reflection on generic parameters and InnerClass is required to use Signature. +-keepattributes Signature, InnerClasses + + # Retain service method parameters when optimizing. +-keepclassmembers,allowshrinking,allowobfuscation interface * { + @retrofit2.http.* ; +} + + # Ignore annotation used for build tooling. +-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement + + # Ignore JSR 305 annotations for embedding nullability information. +-dontwarn javax.annotation.** + + # Guarded by a NoClassDefFoundError try/catch and only used when on the classpath. +-dontwarn kotlin.Unit + + # Top-level functions that can only be used by Kotlin. +-dontwarn retrofit2.-KotlinExtensions + + # ThreeTen ABP +-keep class org.threeten.bp.** { *; } +-keep interface org.threeten.bp.** { *; } diff --git a/KomposeCli/Templates/androidApp/src/main/AndroidManifest.xml b/KomposeCli/Templates/androidApp/src/main/AndroidManifest.xml new file mode 100644 index 0000000..80b38e8 --- /dev/null +++ b/KomposeCli/Templates/androidApp/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + diff --git a/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/App.kt b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/App.kt new file mode 100644 index 0000000..5030f4c --- /dev/null +++ b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/App.kt @@ -0,0 +1,18 @@ +package $$PROJECT_PACKAGE$$.app + +import android.app.Application +import $$PROJECT_PACKAGE$$.app.di.appModule +import org.koin.android.ext.koin.androidContext +import org.koin.core.context.startKoin + +class App : Application() { + + override fun onCreate() { + super.onCreate() + + startKoin { + androidContext(this@App) + modules(appModule) + } + } +} diff --git a/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/di/appModule.kt b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/di/appModule.kt new file mode 100644 index 0000000..86351ff --- /dev/null +++ b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/di/appModule.kt @@ -0,0 +1,6 @@ +package $$PROJECT_PACKAGE$$.app.di + +import org.koin.dsl.module + +val appModule = module { +} diff --git a/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/MainActivity.kt b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/MainActivity.kt new file mode 100644 index 0000000..8489fa2 --- /dev/null +++ b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/MainActivity.kt @@ -0,0 +1,22 @@ +package $$PROJECT_PACKAGE$$.app.ui + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import androidx.compose.Composable +import androidx.ui.core.setContent +import androidx.ui.material.MaterialTheme + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContent { App() } + } + + @Composable + private fun App() { + MaterialTheme(colors = themeColors) { + TODO("Your app here") + } + } +} diff --git a/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/ObservableStore.kt b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/ObservableStore.kt new file mode 100644 index 0000000..076070e --- /dev/null +++ b/KomposeCli/Templates/androidApp/src/main/kotlin/package/app/ui/ObservableStore.kt @@ -0,0 +1,8 @@ +package $$PROJECT_PACKAGE$$.app.ui + +import androidx.compose.Model +import $$PROJECT_PACKAGE$$.ui.Store +import $$PROJECT_PACKAGE$$.ui.ViewState + +@Model +class ObservableStore(override var viewState: T) : Store diff --git a/KomposeCli/Templates/build.gradle.kts b/KomposeCli/Templates/build.gradle.kts new file mode 100644 index 0000000..67bc9f9 --- /dev/null +++ b/KomposeCli/Templates/build.gradle.kts @@ -0,0 +1,70 @@ +/* + * Copyright $$YEAR$$ $$AUTHOR$$ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + import com.github.benmanes.gradle.versions.updates.DependencyUpdatesTask + import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("com.github.ben-manes.versions") version "0.27.0" +} + +buildscript { + repositories { + jcenter() + google() + maven("https://plugins.gradle.org/m2/") + } + + dependencies { + classpath(kotlin("gradle-plugin", Build.Versions.kotlin)) + classpath(kotlin("serialization", Build.Versions.kotlin)) + classpath(Build.androidGradle) + classpath(Build.xcodeSync) + } +} + +allprojects { + repositories { + jcenter() + google() + maven("https://oss.sonatype.org/content/repositories/snapshots") + } + + tasks.withType { + kotlinOptions { + freeCompilerArgs = listOf("-progressive", "-Xuse-experimental=kotlin.Experimental") + jvmTarget = "1.8" + } + } +} + +subprojects { + afterEvaluate { + extensions.configure { + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + } + } +} + +tasks.named("dependencyUpdates") { + checkForGradleUpdate = true + outputFormatter = "json" + outputDir = "build/dependencyUpdates" + reportfileName = "report" +} diff --git a/KomposeCli/Templates/buildSrc/build.gradle.kts b/KomposeCli/Templates/buildSrc/build.gradle.kts new file mode 100644 index 0000000..ab2cd15 --- /dev/null +++ b/KomposeCli/Templates/buildSrc/build.gradle.kts @@ -0,0 +1,23 @@ +/* + * Copyright $$YEAR$$ $$AUTHOR$$ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +plugins { + `kotlin-dsl` +} + +repositories { + jcenter() +} diff --git a/KomposeCli/Templates/buildSrc/src/main/kotlin/Config.kt b/KomposeCli/Templates/buildSrc/src/main/kotlin/Config.kt new file mode 100644 index 0000000..4bf9b4c --- /dev/null +++ b/KomposeCli/Templates/buildSrc/src/main/kotlin/Config.kt @@ -0,0 +1,107 @@ +/* + * Copyright $$YEAR$$ $$AUTHOR$$ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +object Build { + + object Versions { + const val kotlin = "$$VERSION_KOTLIN$$" + const val androidGradle = "$$VERSION_ANDROID_GRADLE$$" + const val xcodeSync = "$$VERSION_XCODE_SYNC$$" + } + + const val androidGradle = "com.android.tools.build:gradle:${Versions.androidGradle}" + const val xcodeSync = "co.touchlab:kotlinxcodesync:${Versions.xcodeSync}" +} + +object Android { + const val minSdkVersion = $$VERSION_ANDROID_MIN_SDK$$ + const val targetSdkVersion = $$VERSION_ANDROID_TARGET_SDK$$ + const val compileSdkVersion = $$VERSION_ANDROID_COMPILE_SDK$$ +} + +object Libs { + + private object Versions { + const val timber = "$$VERSION_LIBS_TIMBER$$" + const val koin = "$$VERSION_LIBS_KOIN$$" + const val coil = "$$VERSION_LIBS_COIL$$" + const val jsr310 = "$$VERSION_LIBS_JSR310$$" + } + + const val timber = "com.jakewharton.timber:timber:${Versions.timber}" + const val koinAndroid = "org.koin:koin-androidx-viewmodel:${Versions.koin}" + const val coil = "io.coil-kt:coil:${Versions.coil}" + const val jsr310 = "com.jakewharton.threetenabp:threetenabp:${Versions.jsr310}" +} + + +object AndroidX { + + private object Versions { + const val appCompat = "$$VERSION_ANDROIDX_APPCOMPAT$$" + const val coreKtx = "$$VERSION_ANDROIDX_COREKTX$$" + const val compose = "$$VERSION_ANDROIDX_COMPOSE$$" + } + + const val appCompat = "androidx.appcompat:appcompat:${Versions.appCompat}" + const val coreKtx = "androidx.core:core-ktx:${Versions.coreKtx}" + + const val composeFoundation = "androidx.ui:ui-foundation:${Versions.compose}" + const val composeFramework = "androidx.ui:ui-framework:${Versions.compose}" + const val composeLayout = "androidx.ui:ui-layout:${Versions.compose}" + const val composeMaterial = "androidx.ui:ui-material:${Versions.compose}" + const val composeTooling = "androidx.ui:ui-tooling:${Versions.compose}" +} + + +object Coroutines { + + private const val version = "$$VERSION_KOTLINX_COROUTINES$$" + + const val core = "org.jetbrains.kotlinx:kotlinx-coroutines-core:$version" + const val android = "org.jetbrains.kotlinx:kotlinx-coroutines-android:$version" + const val native = "org.jetbrains.kotlinx:kotlinx-coroutines-core-native:$version" +} + +object Serialization { + + private const val version = "$$VERSION_KOTLINX_SERIALIZATION$$" + + const val common = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-common:$version" + const val runtime = "org.jetbrains.kotlinx:kotlinx-serialization-runtime:$version" + const val native = "org.jetbrains.kotlinx:kotlinx-serialization-runtime-native:$version" +} + +object Ktor { + + private const val version = "$$VERSION_KTOR$$" + + // Common + const val clientCore = "io.ktor:ktor-client-core:$version" + const val clientJson = "io.ktor:ktor-client-json:$version" + const val clientSerialization = "io.ktor:ktor-client-serialization:$version" + const val clientLogging = "io.ktor:ktor-client-logging:$version" + // Android + const val clientOkttp = "io.ktor:ktor-client-okhttp:$version" + const val clientJsonJvm = "io.ktor:ktor-client-json-jvm:$version" + const val clientSerializationJvm = "io.ktor:ktor-client-serialization-jvm:$version" + const val clientLoggingJvm = "io.ktor:ktor-client-logging-jvm:$version" + // IOS + const val clientIos = "io.ktor:ktor-client-ios:$version" + const val clientJsonNative = "io.ktor:ktor-client-json-native:$version" + const val clientSerializationIos = "io.ktor:ktor-client-serialization-iosx64:$version" + const val clientLoggingIos = "io.ktor:ktor-client-logging-native:$version" +} diff --git a/KomposeCli/Templates/gradle.properties b/KomposeCli/Templates/gradle.properties new file mode 100644 index 0000000..3082833 --- /dev/null +++ b/KomposeCli/Templates/gradle.properties @@ -0,0 +1,20 @@ +# +# Copyright $$YEAR$$ $$AUTHOR$$ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +org.gradle.jvmargs=-Xmx2048M +org.gradle.daemon=true +android.useAndroidX=true +android.enableJetifier=true diff --git a/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.jar b/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..87b738c Binary files /dev/null and b/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.jar differ diff --git a/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.properties b/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..1fc8bb1 --- /dev/null +++ b/KomposeCli/Templates/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,21 @@ +# +# Copyright $$YEAR$$ $$AUTHOR$$ +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-6.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/KomposeCli/Templates/gradlew b/KomposeCli/Templates/gradlew new file mode 100755 index 0000000..af6708f --- /dev/null +++ b/KomposeCli/Templates/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/KomposeCli/Templates/gradlew.bat b/KomposeCli/Templates/gradlew.bat new file mode 100644 index 0000000..6d57edc --- /dev/null +++ b/KomposeCli/Templates/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/KomposeCli/Templates/iosApp/App.xcodeproj/project.pbxproj b/KomposeCli/Templates/iosApp/App.xcodeproj/project.pbxproj new file mode 100644 index 0000000..2ea333e --- /dev/null +++ b/KomposeCli/Templates/iosApp/App.xcodeproj/project.pbxproj @@ -0,0 +1,434 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 5308874823EC17B3004BA4C2 /* BaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5308874723EC17B3004BA4C2 /* BaseView.swift */; }; + 53B6BF7E23D848B400A717C0 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B6BF7D23D848B400A717C0 /* AppDelegate.swift */; }; + 53B6BF8023D848B400A717C0 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B6BF7F23D848B400A717C0 /* SceneDelegate.swift */; }; + 53B6BF8423D848B700A717C0 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 53B6BF8323D848B700A717C0 /* Assets.xcassets */; }; + 53B6BF8A23D848B700A717C0 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 53B6BF8823D848B700A717C0 /* LaunchScreen.storyboard */; }; + 53B6BFB223D854E500A717C0 /* shared.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 53B6BFB023D854DB00A717C0 /* shared.framework */; }; + 53B6BFB323D854E500A717C0 /* shared.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 53B6BFB023D854DB00A717C0 /* shared.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 53B6BFBB23D883CB00A717C0 /* ObservableStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 53B6BFBA23D883CB00A717C0 /* ObservableStore.swift */; }; + 53C60EF623DF792E004537F8 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 53C60EF523DF792E004537F8 /* Localizable.strings */; }; +/* End PBXBuildFile section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 53B6BFB423D854E500A717C0 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 53B6BFB323D854E500A717C0 /* shared.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 5308874723EC17B3004BA4C2 /* BaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseView.swift; sourceTree = ""; }; + 53B6BF7A23D848B400A717C0 /* $$PROJECT_IDENTIFIER$$.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = $$PROJECT_IDENTIFIER$$.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 53B6BF7D23D848B400A717C0 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 53B6BF7F23D848B400A717C0 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 53B6BF8323D848B700A717C0 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 53B6BF8923D848B700A717C0 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 53B6BF8B23D848B700A717C0 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 53B6BFB023D854DB00A717C0 /* shared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = shared.framework; sourceTree = ""; }; + 53B6BFBA23D883CB00A717C0 /* ObservableStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObservableStore.swift; sourceTree = ""; }; + 53C60EF523DF792E004537F8 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 53B6BF7723D848B400A717C0 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 53B6BFB223D854E500A717C0 /* shared.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 53B6BF7123D848B400A717C0 = { + isa = PBXGroup; + children = ( + 53B6BF7C23D848B400A717C0 /* $$PROJECT_IDENTIFIER$$ */, + 53B6BFAF23D84D4F00A717C0 /* Frameworks */, + 53B6BF7B23D848B400A717C0 /* Products */, + ); + sourceTree = ""; + }; + 53B6BF7B23D848B400A717C0 /* Products */ = { + isa = PBXGroup; + children = ( + 53B6BF7A23D848B400A717C0 /* $$PROJECT_IDENTIFIER$$.app */, + ); + name = Products; + sourceTree = ""; + }; + 53B6BF7C23D848B400A717C0 /* $$PROJECT_IDENTIFIER$$ */ = { + isa = PBXGroup; + children = ( + 53B6BF8B23D848B700A717C0 /* Info.plist */, + 53C60EF523DF792E004537F8 /* Localizable.strings */, + 53B6BF7D23D848B400A717C0 /* AppDelegate.swift */, + 53B6BF7F23D848B400A717C0 /* SceneDelegate.swift */, + 53B6BF8323D848B700A717C0 /* Assets.xcassets */, + 53B6BF8823D848B700A717C0 /* LaunchScreen.storyboard */, + 53B6BFB923D883AB00A717C0 /* Modules */, + 53B6BFB823D883A500A717C0 /* UI */, + ); + path = $$PROJECT_IDENTIFIER$$; + sourceTree = ""; + }; + 53B6BFAF23D84D4F00A717C0 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 53B6BFB023D854DB00A717C0 /* shared.framework */, + ); + path = Frameworks; + sourceTree = ""; + }; + 53B6BFB823D883A500A717C0 /* UI */ = { + isa = PBXGroup; + children = ( + 53B6BFBA23D883CB00A717C0 /* ObservableStore.swift */, + ); + path = UI; + sourceTree = ""; + }; + 53B6BFB923D883AB00A717C0 /* Modules */ = { + isa = PBXGroup; + children = ( + 5308874723EC17B3004BA4C2 /* BaseView.swift */, + ); + path = Modules; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 53B6BF7923D848B400A717C0 /* $$PROJECT_IDENTIFIER$$ */ = { + isa = PBXNativeTarget; + buildConfigurationList = 53B6BFA423D848B700A717C0 /* Build configuration list for PBXNativeTarget "$$PROJECT_IDENTIFIER$$" */; + buildPhases = ( + 53B6BFAD23D84A6C00A717C0 /* [SCRIPT] Generate frameworks from Kotlin MPP */, + 53C60EF723DF79D5004537F8 /* [SCRIPT] Generate Localizable file */, + 53B6BF7623D848B400A717C0 /* Sources */, + 53B6BF7723D848B400A717C0 /* Frameworks */, + 53B6BF7823D848B400A717C0 /* Resources */, + 53B6BFB423D854E500A717C0 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = $$PROJECT_IDENTIFIER$$; + productName = $$PROJECT_IDENTIFIER$$; + productReference = 53B6BF7A23D848B400A717C0 /* $$PROJECT_IDENTIFIER$$.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 53B6BF7223D848B400A717C0 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1110; + LastUpgradeCheck = 1110; + ORGANIZATIONNAME = $$AUTHOR$$; + TargetAttributes = { + 53B6BF7923D848B400A717C0 = { + CreatedOnToolsVersion = 11.1; + }; + }; + }; + buildConfigurationList = 53B6BF7523D848B400A717C0 /* Build configuration list for PBXProject "$$PROJECT_IDENTIFIER$$" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 53B6BF7123D848B400A717C0; + productRefGroup = 53B6BF7B23D848B400A717C0 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 53B6BF7923D848B400A717C0 /* $$PROJECT_IDENTIFIER$$ */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 53B6BF7823D848B400A717C0 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53B6BF8A23D848B700A717C0 /* LaunchScreen.storyboard in Resources */, + 53C60EF623DF792E004537F8 /* Localizable.strings in Resources */, + 53B6BF8423D848B700A717C0 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 53B6BFAD23D84A6C00A717C0 /* [SCRIPT] Generate frameworks from Kotlin MPP */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[SCRIPT] Generate frameworks from Kotlin MPP"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../scripts/build_ios_frameworks.sh $CONFIGURATION\n"; + }; + 53C60EF723DF79D5004537F8 /* [SCRIPT] Generate Localizable file */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "[SCRIPT] Generate Localizable file"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "../scripts/generate_locales_ios.sh\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 53B6BF7623D848B400A717C0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 53B6BF7E23D848B400A717C0 /* AppDelegate.swift in Sources */, + 53B6BF8023D848B400A717C0 /* SceneDelegate.swift in Sources */, + 53B6BFBB23D883CB00A717C0 /* ObservableStore.swift in Sources */, + 5308874823EC17B3004BA4C2 /* BaseView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 53B6BF8823D848B700A717C0 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 53B6BF8923D848B700A717C0 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 53B6BFA223D848B700A717C0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 53B6BFA323D848B700A717C0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 53B6BFA523D848B700A717C0 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 23A3PS5QB5; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + INFOPLIST_FILE = $$PROJECT_IDENTIFIER$$/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = $$PROJECT_PACKAGE$$.$$PROJECT_IDENTIFIER$$; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 53B6BFA623D848B700A717C0 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = 23A3PS5QB5; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(PROJECT_DIR)/Frameworks", + ); + INFOPLIST_FILE = $$PROJECT_IDENTIFIER$$/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = $$PROJECT_PACKAGE$$.$$PROJECT_IDENTIFIER$$; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 53B6BF7523D848B400A717C0 /* Build configuration list for PBXProject "$$PROJECT_IDENTIFIER$$" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53B6BFA223D848B700A717C0 /* Debug */, + 53B6BFA323D848B700A717C0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 53B6BFA423D848B700A717C0 /* Build configuration list for PBXNativeTarget "$$PROJECT_IDENTIFIER$$" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 53B6BFA523D848B700A717C0 /* Debug */, + 53B6BFA623D848B700A717C0 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 53B6BF7223D848B400A717C0 /* Project object */; +} diff --git a/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..c84789c --- /dev/null +++ b/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/KomposeCli/Templates/iosApp/App/AppDelegate.swift b/KomposeCli/Templates/iosApp/App/AppDelegate.swift new file mode 100644 index 0000000..4b6d170 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/AppDelegate.swift @@ -0,0 +1,28 @@ +// +// AppDelegate.swift +// $$PROJECT_IDENTIFIER$$ +// +// Created by $$AUTHOR$$ on $$YEAR$$. +// Copyright © $$YEAR$$ $$AUTHOR$$. All rights reserved. +// + +import UIKit + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + typealias LaunchOptions = [UIApplication.LaunchOptionsKey: Any] + + func application(_ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: LaunchOptions?) -> Bool { + return true + } + + func application(_ application: UIApplication, + configurationForConnecting connectingSceneSession: UISceneSession, + options: UIScene.ConnectionOptions) -> UISceneConfiguration { + return UISceneConfiguration( + name: "Default Configuration", + sessionRole: connectingSceneSession.role + ) + } +} diff --git a/KomposeCli/Templates/iosApp/App/Assets.xcassets/AppIcon.appiconset/Contents.json b/KomposeCli/Templates/iosApp/App/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..d8db8d6 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/KomposeCli/Templates/iosApp/App/Assets.xcassets/Contents.json b/KomposeCli/Templates/iosApp/App/Assets.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/KomposeCli/Templates/iosApp/App/Base.lproj/LaunchScreen.storyboard b/KomposeCli/Templates/iosApp/App/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000..865e932 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/KomposeCli/Templates/iosApp/App/Info.plist b/KomposeCli/Templates/iosApp/App/Info.plist new file mode 100644 index 0000000..9742bf0 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Info.plist @@ -0,0 +1,60 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + + + + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/KomposeCli/Templates/iosApp/App/Localizable.strings b/KomposeCli/Templates/iosApp/App/Localizable.strings new file mode 100644 index 0000000..e65c802 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Localizable.strings @@ -0,0 +1,5 @@ +/** + * Apple Strings File + * Generated by Twine 1.0.6 + * Language: en + */ diff --git a/KomposeCli/Templates/iosApp/App/Modules/BaseView.swift b/KomposeCli/Templates/iosApp/App/Modules/BaseView.swift new file mode 100644 index 0000000..3702423 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/Modules/BaseView.swift @@ -0,0 +1,15 @@ +// +// BaseView.swift +// $$PROJECT_IDENTIFIER$$ +// +// Created by $$AUTHOR$$ on $$YEAR$$. +// Copyright © $$YEAR$$ $$AUTHOR$$. All rights reserved. +// + +import SwiftUI + +struct BaseView: View { + var body: some View { + Text("Hello World!") + } +} diff --git a/KomposeCli/Templates/iosApp/App/SceneDelegate.swift b/KomposeCli/Templates/iosApp/App/SceneDelegate.swift new file mode 100644 index 0000000..ebf9377 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/SceneDelegate.swift @@ -0,0 +1,28 @@ +// +// SceneDelegate.swift +// $$PROJECT_IDENTIFIER$$ +// +// Created by $$AUTHOR$$ on $$YEAR$$. +// Copyright © $$YEAR$$ $$AUTHOR$$. All rights reserved. +// + +import UIKit +import SwiftUI + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + var window: UIWindow? + + func scene(_ scene: UIScene, + willConnectTo session: UISceneSession, + options connectionOptions: UIScene.ConnectionOptions) { + // HERE YOU NEED TO DEFINE YOUR FIRST VIEW + let baseView = BaseView() + + if let windowScene = scene as? UIWindowScene { + let window = UIWindow(windowScene: windowScene) + window.rootViewController = UIHostingController(rootView: baseView) + self.window = window + window.makeKeyAndVisible() + } + } +} diff --git a/KomposeCli/Templates/iosApp/App/UI/ObservableStore.swift b/KomposeCli/Templates/iosApp/App/UI/ObservableStore.swift new file mode 100644 index 0000000..de0bd54 --- /dev/null +++ b/KomposeCli/Templates/iosApp/App/UI/ObservableStore.swift @@ -0,0 +1,26 @@ +// +// ObservableStore.swift +// $$PROJECT_IDENTIFIER$$ +// +// Created by $$AUTHOR$$ on $$YEAR$$. +// Copyright © $$YEAR$$ $$AUTHOR$$. All rights reserved. +// + +import shared + +class ObservableStore: Store, ObservableObject { + var viewState: ViewState { + didSet { + guard let newState = viewState as? T else { + fatalError("Cannot be casted") + } + state = newState + } + } + @Published var state: T + + init(baseState: T) { + viewState = baseState + state = baseState + } +} diff --git a/KomposeCli/Templates/scripts/build_ios_frameworks.sh b/KomposeCli/Templates/scripts/build_ios_frameworks.sh new file mode 100755 index 0000000..4d31837 --- /dev/null +++ b/KomposeCli/Templates/scripts/build_ios_frameworks.sh @@ -0,0 +1,26 @@ +#!/bin/sh + +# Build IOS Frameworks on Xcode build +# First param must be the Xcode configuration param $CONFIGURATION + +# Variables +shell_dir=$(dirname "$0") +frameworkName=shared.framework +xcode_configuration=$1 + +# Move to parent project +cd "$shell_dir"/../ + +# Generate xcode frameworks by gradlew +./gradlew :shared:packForXCode -PXCODE_CONFIGURATION=${xcode_configuration} + +# Move to destination project +cd iosApp + +# Clean old framework + dSYM +rm -rf ./Frameworks/"$frameworkName" +rm -rf ./Frameworks/"$frameworkName".dSYM + +# Copy generated framework + dSYM +cp -R ../shared/build/xcode-frameworks/"$frameworkName" ./Frameworks/. +cp -R ../shared/build/xcode-frameworks/"$frameworkName".dSYM ./Frameworks/. diff --git a/KomposeCli/Templates/scripts/generate_locales_ios.sh b/KomposeCli/Templates/scripts/generate_locales_ios.sh new file mode 100755 index 0000000..6d03904 --- /dev/null +++ b/KomposeCli/Templates/scripts/generate_locales_ios.sh @@ -0,0 +1,15 @@ +#!/bin/sh + +# Generate locales for iOS app + +# Variables +shell_dir=$(dirname "$0") +locales_dir="$shell_dir"/../shared/locales +ios_destination="$shell_dir"/../iosApp/$$PROJECT_IDENTIFIER$$ + +# Twine launch +twine generate-localization-file \ + "$locales_dir"/locales.twine \ + "$ios_destination"/Localizable.strings \ + --format apple \ + --lang en diff --git a/KomposeCli/Templates/settings.gradle.kts b/KomposeCli/Templates/settings.gradle.kts new file mode 100644 index 0000000..8ace241 --- /dev/null +++ b/KomposeCli/Templates/settings.gradle.kts @@ -0,0 +1,20 @@ +/* + * Copyright $$YEAR$$ $$AUTHOR$$ + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +include( + ":androidApp", + ":shared" +) diff --git a/KomposeCli/Templates/shared/build.gradle.kts b/KomposeCli/Templates/shared/build.gradle.kts new file mode 100644 index 0000000..5be2b08 --- /dev/null +++ b/KomposeCli/Templates/shared/build.gradle.kts @@ -0,0 +1,112 @@ +import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTarget + +plugins { + kotlin("multiplatform") + id("com.android.library") + id("org.jetbrains.kotlin.plugin.serialization") + id("co.touchlab.kotlinxcodesync") +} + +android { + compileSdkVersion(Android.compileSdkVersion) + + defaultConfig { + minSdkVersion(Android.minSdkVersion) + targetSdkVersion(Android.targetSdkVersion) + resConfigs("fr") + } + + sourceSets { + getByName("main").apply { + manifest.srcFile("src/androidMain/AndroidManifest.xml") + java.srcDirs("src/androidMain/kotlin") + } + } +} + + +kotlin { + targets { + android() + val iOSTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget = + if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true) + ::iosArm64 + else + ::iosX64 + + iOSTarget("ios") { + binaries { + framework { + baseName = "shared" + } + } + } + } + + sourceSets { + getByName("commonMain").dependencies { + implementation(kotlin("stdlib-common", Build.Versions.kotlin)) + implementation(Coroutines.core) + implementation(Serialization.common) + implementation(Ktor.clientCore) + implementation(Ktor.clientJson) + implementation(Ktor.clientSerialization) + implementation(Ktor.clientLogging) + } + + getByName("androidMain").dependencies { + implementation(kotlin("stdlib", Build.Versions.kotlin)) + implementation(Coroutines.android) + implementation(Serialization.runtime) + implementation(Ktor.clientOkttp) + implementation(Ktor.clientJsonJvm) + implementation(Ktor.clientSerializationJvm) + implementation(Ktor.clientLoggingJvm) + implementation(AndroidX.appCompat) + implementation(AndroidX.coreKtx) + implementation(Libs.jsr310) + } + + getByName("iosMain").dependencies { + implementation(Coroutines.native) + implementation(Serialization.native) + implementation(Ktor.clientIos) + implementation(Ktor.clientJsonNative) + implementation(Ktor.clientSerializationIos) + implementation(Ktor.clientLoggingIos) + } + } +} + +xcodeSync { + projectPath = "../../iosApp/iosApp.xcodeproj" + target = "iosApp" +} + +val packForXcode by tasks.creating(Sync::class) { + val targetDir = File(buildDir, "xcode-frameworks") + + val mode = System.getenv("CONFIGURATION") ?: "DEBUG" + val framework = kotlin.targets + .getByName("ios") + .binaries.getFramework(mode) + inputs.property("mode", mode) + dependsOn(framework.linkTask) + + from({ framework.outputDirectory }) + into(targetDir) + + /// generate a helpful ./gradlew wrapper with embedded Java path + doLast { + val gradlew = File(targetDir, "gradlew") + gradlew.writeText( + "#!/bin/bash\n" + + "export 'JAVA_HOME=${System.getProperty("java.home")}'\n" + + "cd '${rootProject.rootDir}'\n" + + "./gradlew \$@\n" + ) + gradlew.setExecutable(true) + } +} + +tasks.getByName("build").dependsOn(packForXcode) diff --git a/KomposeCli/Templates/shared/locales/locales.twine b/KomposeCli/Templates/shared/locales/locales.twine new file mode 100644 index 0000000..5466973 --- /dev/null +++ b/KomposeCli/Templates/shared/locales/locales.twine @@ -0,0 +1,3 @@ +[[General]] + [app_name] + en = $$PROJECT_IDENTIFIER$$ diff --git a/KomposeCli/Templates/shared/src/androidMain/AndroidManifest.xml b/KomposeCli/Templates/shared/src/androidMain/AndroidManifest.xml new file mode 100644 index 0000000..1116f9a --- /dev/null +++ b/KomposeCli/Templates/shared/src/androidMain/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/KomposeCli/Templates/shared/src/androidMain/kotlin/package/utils/coroutines.kt b/KomposeCli/Templates/shared/src/androidMain/kotlin/package/utils/coroutines.kt new file mode 100644 index 0000000..20ac261 --- /dev/null +++ b/KomposeCli/Templates/shared/src/androidMain/kotlin/package/utils/coroutines.kt @@ -0,0 +1,7 @@ +package $$PROJECT_PACKAGE$$.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Dispatchers + +actual val mainDispatcher: CoroutineDispatcher = Dispatchers.Main +actual val ioDispatcher: CoroutineDispatcher = Dispatchers.IO diff --git a/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/Store.kt b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/Store.kt new file mode 100644 index 0000000..a88106b --- /dev/null +++ b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/Store.kt @@ -0,0 +1,5 @@ +package $$PROJECT_PACKAGE$$.ui + +interface Store { + var viewState: T +} diff --git a/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/ViewState.kt b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/ViewState.kt new file mode 100644 index 0000000..ab08ea6 --- /dev/null +++ b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/ui/ViewState.kt @@ -0,0 +1,3 @@ +package $$PROJECT_PACKAGE$$.ui + +interface ViewState diff --git a/KomposeCli/Templates/shared/src/commonMain/kotlin/package/utils/coroutines.kt b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/utils/coroutines.kt new file mode 100644 index 0000000..584f465 --- /dev/null +++ b/KomposeCli/Templates/shared/src/commonMain/kotlin/package/utils/coroutines.kt @@ -0,0 +1,6 @@ +package $$PROJECT_PACKAGE$$.utils + +import kotlinx.coroutines.CoroutineDispatcher + +expect val mainDispatcher: CoroutineDispatcher +expect val ioDispatcher: CoroutineDispatcher diff --git a/KomposeCli/Templates/shared/src/iosMain/kotlin/package/utils/coroutines.kt b/KomposeCli/Templates/shared/src/iosMain/kotlin/package/utils/coroutines.kt new file mode 100644 index 0000000..9894b4f --- /dev/null +++ b/KomposeCli/Templates/shared/src/iosMain/kotlin/package/utils/coroutines.kt @@ -0,0 +1,19 @@ +package $$PROJECT_PACKAGE$$.utils + +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.Runnable +import platform.darwin.dispatch_async +import platform.darwin.dispatch_get_main_queue +import platform.darwin.dispatch_queue_t +import kotlin.coroutines.CoroutineContext + +actual val mainDispatcher: CoroutineDispatcher = NsQueueDispatcher(dispatch_get_main_queue()) +actual val ioDispatcher: CoroutineDispatcher = mainDispatcher + +internal class NsQueueDispatcher(private val dispatchQueue: dispatch_queue_t) : CoroutineDispatcher() { + override fun dispatch(context: CoroutineContext, block: Runnable) { + dispatch_async(dispatchQueue) { + block.run() + } + } +} diff --git a/KomposeCli/main.py b/KomposeCli/main.py new file mode 100755 index 0000000..750cb67 --- /dev/null +++ b/KomposeCli/main.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 + +import os +import sys +import shutil +import datetime + + +# #################### +# General utils +# #################### + +class Colors: + BOLD = '\033[1m' + GREEN = '\033[92m' + FAIL = '\033[91m' + END = '\033[0m' + + +def exit_with_error(message): + print(Colors.FAIL + "ERROR: " + message + "." + Colors.END) + print("") + sys.exit(1) + + +# #################### +# Scripts utils +# #################### + +zip_extract_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "Templates") + + +class ProjectData: + def __init__(self, identifier, package, path): + self.identifier = identifier + self.package = package + '.' + identifier.lower() + self.path = path + + +class ProjectFilesMaker: + def __init__(self, project_data): + self.project_data = project_data + + def movefile_to_project(self, file, new_file, replacements=True, enable_permissions=False): + destination_file = os.path.join(self.project_data.path, new_file) + os.makedirs(os.path.dirname(destination_file), exist_ok=True) + shutil.copyfile(os.path.join(zip_extract_path, file), destination_file) + if replacements: + self.do_replacements_infile(destination_file) + if enable_permissions: + os.chmod(destination_file, 0o777) + + def copyfile_to_project(self, file, replacements=True, enable_permissions=False): + self.movefile_to_project(file, file, replacements, enable_permissions) + + def movefolder_to_project(self, folder, new_folder): + destination_folder = os.path.join(self.project_data.path, new_folder) + shutil.copytree(os.path.join(zip_extract_path, folder), destination_folder) + + def do_replacements_infile(self, destination_file): + file_data = ProjectFilesMaker.get_file_data(destination_file) + file_data = self.make_replacements(file_data) + ProjectFilesMaker.write_file_data(destination_file, file_data) + + def make_replacements(self, file_data): + data = file_data + # Generic data + data = data.replace('$$PROJECT_IDENTIFIER$$', self.project_data.identifier) + data = data.replace('$$PROJECT_PACKAGE$$', self.project_data.package) + data = data.replace('$$AUTHOR$$', self.project_data.identifier) + data = data.replace('$$YEAR$$', str(datetime.datetime.now().year)) + # Versions: Build + data = data.replace('$$VERSION_KOTLIN$$', '1.3.61') + data = data.replace('$$VERSION_ANDROID_GRADLE$$', '4.0.0-alpha09') + data = data.replace('$$VERSION_XCODE_SYNC$$', '0.2') + # Versions: Android + data = data.replace('$$VERSION_ANDROID_MIN_SDK$$', '21') + data = data.replace('$$VERSION_ANDROID_TARGET_SDK$$', '29') + data = data.replace('$$VERSION_ANDROID_COMPILE_SDK$$', '29') + # Versions: Libs + data = data.replace('$$VERSION_LIBS_TIMBER$$', '4.7.1') + data = data.replace('$$VERSION_LIBS_KOIN$$', '2.0.1') + data = data.replace('$$VERSION_LIBS_COIL$$', '0.9.2') + data = data.replace('$$VERSION_LIBS_JSR310$$', '1.2.1') + # Versions: AndroidX + data = data.replace('$$VERSION_ANDROIDX_APPCOMPAT$$', '1.1.0') + data = data.replace('$$VERSION_ANDROIDX_COREKTX$$', '1.1.0') + data = data.replace('$$VERSION_ANDROIDX_COMPOSE$$', '0.1.0-dev03') + # Versions: Coroutines + data = data.replace('$$VERSION_KOTLINX_COROUTINES$$', '1.3.3') + # Versions: Serialization + data = data.replace('$$VERSION_KOTLINX_SERIALIZATION$$', '0.14.0') + # Versions: Ktor + data = data.replace('$$VERSION_KTOR$$', '1.2.6') + return data + + @staticmethod + def get_file_data(destination_file): + with open(destination_file) as file: + file_data = file.read() + return file_data + + @staticmethod + def write_file_data(destination_file, data): + with open(destination_file, 'w') as file: + file.write(data) + + +def recursive_ask_for_input(message): + response = input(message) + if not response: + return recursive_ask_for_input(message) + return response + + +# #################### +# Script steps +# #################### + +def print_title(): + print("") + print(Colors.GREEN + Colors.BOLD + ">> Creating a new multi-platform project with Kompose" + Colors.END) + print("") + + +def ask_for_options(): + project_identifier = recursive_ask_for_input("[1] Your project identifier (example: DemoApp): ") + project_package = recursive_ask_for_input("[2] Your project package (example: com.jtouzy): ") + project_path = input("[2] Your project absolute path ('Enter' for this location): ") + if not project_path: + project_path = os.getcwd() + return ProjectData(project_identifier, project_package, os.path.join(project_path, project_identifier)) + + +def create_project_directory(path): + print("") + print("Creating project directory... " + path) + if os.path.exists(path): + exit_with_error("Project directory already exists") + else: + os.mkdir(path) + + +def copy_structure_files(project_data): + print("Generating files...") + project_maker = ProjectFilesMaker(project_data) + project_package_path_items = project_data.package.split('.') + # # BASE FOLDER + # Gradle binary + conf files + project_maker.copyfile_to_project("gradlew", replacements=False, enable_permissions=True) + project_maker.copyfile_to_project("gradlew.bat", replacements=False, enable_permissions=True) + project_maker.copyfile_to_project("gradle.properties") + project_maker.copyfile_to_project(os.path.join("gradle", "wrapper", "gradle-wrapper.jar"), replacements=False) + project_maker.copyfile_to_project(os.path.join("gradle", "wrapper", "gradle-wrapper.properties")) + # Project modules definition + project_maker.copyfile_to_project("settings.gradle.kts") + # Build options + project_maker.copyfile_to_project("build.gradle.kts") + # Gitignore + project_maker.copyfile_to_project(".gitignore") + # # FOLDER: buildSrc + project_maker.copyfile_to_project(os.path.join("buildSrc", "build.gradle.kts")) + project_maker.copyfile_to_project(os.path.join("buildSrc", "src", "main", "kotlin", "Config.kt")) + # # FOLDER: scripts + project_maker.copyfile_to_project(os.path.join("scripts", "generate_locales_ios.sh"), enable_permissions=True) + project_maker.copyfile_to_project(os.path.join("scripts", "build_ios_frameworks.sh"), enable_permissions=True) + # # FOLDER: shared + project_maker.copyfile_to_project(os.path.join("shared", "locales", "locales.twine")) + project_maker.copyfile_to_project(os.path.join("shared", "build.gradle.kts")) + # # FOLDER: shared/common + base_path = ["shared", "src", "commonMain", "kotlin"] + move_coroutines_file(project_maker, base_path, project_package_path_items) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "utils", "coroutines.kt"])), + os.path.join(*(base_path + project_package_path_items + ["utils", "coroutines.kt"])) + ) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "ui", "Store.kt"])), + os.path.join(*(base_path + project_package_path_items + ["ui", "Store.kt"])) + ) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "ui", "ViewState.kt"])), + os.path.join(*(base_path + project_package_path_items + ["ui", "ViewState.kt"])) + ) + # # FOLDER: shared/ios + base_path = ["shared", "src", "iosMain", "kotlin"] + move_coroutines_file(project_maker, base_path, project_package_path_items) + # # FOLDER: shared/android + base_path = ["shared", "src", "androidMain", "kotlin"] + move_coroutines_file(project_maker, base_path, project_package_path_items) + project_maker.copyfile_to_project(os.path.join("shared", "src", "androidMain", "AndroidManifest.xml")) + # # FOLDER: androidApp + copy_android_app_files(project_maker, project_package_path_items) + # # FOLDER: iosApp + copy_ios_app_files(project_maker) + + +def move_coroutines_file(project_maker, base_path, project_package_path_items): + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "utils", "coroutines.kt"])), + os.path.join(*(base_path + project_package_path_items + ["utils", "coroutines.kt"])) + ) + + +def copy_android_app_files(project_maker, project_package_path_items): + project_maker.copyfile_to_project(os.path.join("androidApp", "build.gradle.kts")) + project_maker.copyfile_to_project(os.path.join("androidApp", "proguard-rules.pro")) + project_maker.copyfile_to_project(os.path.join("androidApp", ".gitignore")) + project_maker.copyfile_to_project(os.path.join("androidApp", "src", "main", "AndroidManifest.xml")) + base_path = ["androidApp", "src", "main", "kotlin"] + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "app", "App.kt"])), + os.path.join(*(base_path + project_package_path_items + ["app", "App.kt"])) + ) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "app", "di", "appModule.kt"])), + os.path.join(*(base_path + project_package_path_items + ["app", "di", "appModule.kt"])) + ) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "app", "ui", "MainActivity.kt"])), + os.path.join(*(base_path + project_package_path_items + ["app", "ui", "MainActivity.kt"])) + ) + project_maker.movefile_to_project( + os.path.join(*(base_path + ["package", "app", "ui", "ObservableStore.kt"])), + os.path.join(*(base_path + project_package_path_items + ["app", "ui", "ObservableStore.kt"])) + ) + + +def copy_ios_app_files(project_maker): + project_identifier = project_maker.project_data.identifier + move_ios_file(project_maker, ["AppDelegate.swift"]) + project_maker.movefolder_to_project( + os.path.join("iosApp", "App", "Assets.xcassets"), + os.path.join("iosApp", project_identifier, "Assets.xcassets") + ) + project_maker.movefolder_to_project( + os.path.join("iosApp", "App", "Base.lproj"), + os.path.join("iosApp", project_identifier, "Base.lproj") + ) + move_ios_file(project_maker, ["Info.plist"]) + move_ios_file(project_maker, ["Localizable.strings"]) + move_ios_file(project_maker, ["SceneDelegate.swift"]) + move_ios_file(project_maker, ["UI", "ObservableStore.swift"]) + move_ios_file(project_maker, ["Modules", "BaseView.swift"]) + project_maker.movefolder_to_project( + os.path.join("iosApp", "App.xcodeproj"), + os.path.join("iosApp", project_identifier + ".xcodeproj") + ) + project_maker.do_replacements_infile( + os.path.join(project_maker.project_data.path, "iosApp", project_identifier + ".xcodeproj", "project.pbxproj") + ) + project_maker.do_replacements_infile( + os.path.join( + project_maker.project_data.path, + "iosApp", + project_identifier + ".xcodeproj", + "project.xcworkspace", + "contents.xcworkspacedata" + ) + ) + os.mkdir(os.path.join(project_maker.project_data.path, "iosApp", "Frameworks")) + + +def move_ios_file(project_maker, file_name): + project_maker.movefile_to_project( + os.path.join(*(["iosApp", "App"] + file_name)), + os.path.join(*(["iosApp", project_maker.project_data.identifier] + file_name)) + ) + + +def print_end(project_data): + print("") + print(Colors.GREEN + Colors.BOLD + ">> Your project is ready. Cd here: " + Colors.END + project_data.path) + print("") + + +# #################### +# Main +# #################### + +print_title() +project_options = ask_for_options() +create_project_directory(project_options.path) +copy_structure_files(project_options) +print_end(project_options) diff --git a/README.md b/README.md index c85fbd8..282c5d6 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,8 @@ Reusable architecture pattern for multiplatform mobile projects with Kotlin Mult overview

+**[Try now](#tryit) ⬇️**. + ## Purpose This repository describes an architecture pattern & best practises for a mobile application project. Main advantages below : @@ -66,8 +68,6 @@ But you can also implements another version of the Store, that will handle a Del * **Assembler (iOS)** : An assembler is here to merge it all together. The assembler will create the whole module, initializing the presenter, the view, and the store, then linking it together. -*This layer is used because in Swift we don't have yet a proper DI like [Koin](https://github.com/InsertKoinIO/koin) for Android.* - ### Speed up your multiplatform development #### 🚀 [iOS] Automatically generate a new framework with the last Kotlin classes in each build ? @@ -91,3 +91,40 @@ You can use [Twine](https://github.com/scelis/twine) to have a single file refer Again, for iOS, with a simple `Run Script` in your `Build Phases`, you can generate your strings in each build. You can find an example in the demo app. + +### Try it yourself + +You want to try it ? But doesn't want to loose time with configuration ? +We just provide an utility tool for you to generate the structure of the project, so you can start clean. + +For now, it's a bit trivial, until we've published the tool on `brew`. +Clone the repo, execute the `main.py` file, then give your informations, like below : + +```sh +JTO @ tmp $ git clone https://github.com/jtouzy/Kompose + +Cloning into 'Kompose'... +remote: Enumerating objects: 810, done. +remote: Counting objects: 100% (810/810), done. +remote: Compressing objects: 100% (355/355), done. +remote: Total 1505 (delta 249), reused 759 (delta 211), pack-reused 695 +Receiving objects: 100% (1505/1505), 962.43 KiB | 2.47 MiB/s, done. +Resolving deltas: 100% (448/448), done. + +JTO @ tmp $ ./Kompose/KomposeCli/main.py + +>> Creating a new multi-platform project with Kompose + +[1] Your project identifier (example: DemoApp): MyApp +[2] Your project package (example: com.jtouzy): com.myname +[2] Your project absolute path ('Enter' for this location): + +Creating project directory... /private/tmp/MyApp +Generating files... + +>> Your project is ready. Cd here: /private/tmp/MyApp +``` + +### Issues, ideas + +If you have any recommandations, ideas, or issues, please feel free to open a new issue.