diff --git a/app-compose/build.gradle.kts b/app-compose/build.gradle.kts index b92d97376..931090f68 100644 --- a/app-compose/build.gradle.kts +++ b/app-compose/build.gradle.kts @@ -5,6 +5,7 @@ plugins { alias(libs.plugins.suwiki.android.hilt) alias(libs.plugins.google.services) alias(libs.plugins.firebase.crashlytics) + alias(libs.plugins.compose.compiler) id("com.google.android.gms.oss-licenses-plugin") } diff --git a/app-compose/google-services.json b/app-compose/google-services.json new file mode 100644 index 000000000..6a66fb72f --- /dev/null +++ b/app-compose/google-services.json @@ -0,0 +1,40 @@ +{ + "project_info": { + "project_number": "670329074375", + "firebase_url": "https://uswtime-default-rtdb.firebaseio.com", + "project_id": "uswtime", + "storage_bucket": "uswtime.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:670329074375:android:9d8ee197adad66caa3c4e2", + "android_client_info": { + "package_name": "com.kunize.uswtimetable" + } + }, + "oauth_client": [ + { + "client_id": "670329074375-hted4csqarhth1nniabtes1dgr2d8loa.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAavfvK3lvEdyTiV4-JvejD6G1AS5X8G3I" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "670329074375-hted4csqarhth1nniabtes1dgr2d8loa.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/app-compose/release/output-metadata.json b/app-compose/release/output-metadata.json new file mode 100644 index 000000000..39be530f5 --- /dev/null +++ b/app-compose/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "com.kunize.uswtimetable", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 33, + "versionName": "2.2.5", + "outputFile": "app-compose-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt b/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt index d5d882d61..c8fdd5181 100644 --- a/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt +++ b/build-logic/convention/src/main/java/AndroidLibraryComposeConventionPlugin.kt @@ -9,6 +9,7 @@ class AndroidLibraryComposeConventionPlugin : Plugin { with(target) { with(pluginManager) { apply("com.android.library") + apply("org.jetbrains.kotlin.plugin.compose") } extensions.configure { diff --git a/build.gradle.kts b/build.gradle.kts index 8e2b147ed..4c305bc2c 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -15,6 +15,9 @@ plugins { alias(libs.plugins.ksp) apply false alias(libs.plugins.hilt) apply false alias(libs.plugins.protobuf) apply false + alias(libs.plugins.kotlin.multiplatform) apply false + alias(libs.plugins.android.kotlin.multiplatform.library) apply false + alias(libs.plugins.compose.compiler) apply false } allprojects { diff --git a/common/android/src/main/java/com/suwiki/common/android/Dispatcher.kt b/common/android/src/main/java/com/suwiki/common/android/Dispatcher.kt index 1a560483f..af856b43f 100644 --- a/common/android/src/main/java/com/suwiki/common/android/Dispatcher.kt +++ b/common/android/src/main/java/com/suwiki/common/android/Dispatcher.kt @@ -3,6 +3,7 @@ package com.suwiki.common.android import javax.inject.Qualifier @Qualifier +@Retention(AnnotationRetention.RUNTIME) annotation class Dispatcher(val suwikiDispatcher: SuwikiDispatchers) enum class SuwikiDispatchers { diff --git a/common/security/build.gradle.kts b/common/security/build.gradle.kts index b35b9863d..94b43846e 100644 --- a/common/security/build.gradle.kts +++ b/common/security/build.gradle.kts @@ -18,10 +18,6 @@ dependencies { implementation(libs.androidx.datastore.core) implementation(libs.androidx.datastore.preferences) - ksp(libs.encrypted.datastore.preference.ksp) - implementation(libs.encrypted.datastore.preference.ksp.annotations) - implementation(libs.encrypted.datastore.preference.security) - implementation(libs.timber) testImplementation(libs.junit4) diff --git a/common/security/src/main/java/com/suwiki/common/security/SecurityPreferences.kt b/common/security/src/main/java/com/suwiki/common/security/SecurityPreferences.kt index a47c24f07..15172ded3 100644 --- a/common/security/src/main/java/com/suwiki/common/security/SecurityPreferences.kt +++ b/common/security/src/main/java/com/suwiki/common/security/SecurityPreferences.kt @@ -1,27 +1,17 @@ package com.suwiki.common.security import kotlinx.coroutines.flow.Flow -import tech.thdev.useful.encrypted.data.store.preferences.ksp.annotations.UsefulPreferences -import tech.thdev.useful.encrypted.data.store.preferences.ksp.annotations.value.ClearValues -import tech.thdev.useful.encrypted.data.store.preferences.ksp.annotations.value.GetValue -import tech.thdev.useful.encrypted.data.store.preferences.ksp.annotations.value.SetValue -@UsefulPreferences(/* option. Not use security - disableSecurity = true */) interface SecurityPreferences { - @GetValue(KEY_ACCESS_TOKEN, defaultValue = "") fun flowAccessToken(): Flow - @SetValue(KEY_ACCESS_TOKEN) suspend fun setAccessToken(value: String) - @GetValue(KEY_REFRESH_TOKEN, defaultValue = "") fun flowRefreshToken(): Flow - @SetValue(KEY_REFRESH_TOKEN) suspend fun setRefreshToken(value: String) - @ClearValues suspend fun clearAll() companion object { diff --git a/common/security/src/main/java/com/suwiki/common/security/di/DataStoreModule.kt b/common/security/src/main/java/com/suwiki/common/security/di/DataStoreModule.kt index 583a9e762..44859a6ad 100644 --- a/common/security/src/main/java/com/suwiki/common/security/di/DataStoreModule.kt +++ b/common/security/src/main/java/com/suwiki/common/security/di/DataStoreModule.kt @@ -8,13 +8,12 @@ import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.emptyPreferences import androidx.datastore.preferences.preferencesDataStoreFile import com.suwiki.common.security.SecurityPreferences -import com.suwiki.common.security.generateSecurityPreferences import dagger.Module import dagger.Provides import dagger.hilt.InstallIn import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.components.SingletonComponent -import tech.thdev.useful.encrypted.data.store.preferences.security.generateUsefulSecurity +import kotlinx.coroutines.flow.Flow import javax.inject.Singleton @Module @@ -37,5 +36,21 @@ object DataStoreModule { @Provides fun provideSecurityPreference( @SecureDataStore dataStore: DataStore, - ): SecurityPreferences = dataStore.generateSecurityPreferences(generateUsefulSecurity()) + ): SecurityPreferences = object : SecurityPreferences { + override fun flowAccessToken(): Flow = kotlinx.coroutines.flow.flowOf("fake_access_token") + + override suspend fun setAccessToken(value: String) { + // 아무 동작도 하지 않음 (fake 객체) + } + + override fun flowRefreshToken(): Flow = kotlinx.coroutines.flow.flowOf("fake_refresh_token") + + override suspend fun setRefreshToken(value: String) { + // 아무 동작도 하지 않음 (fake 객체) + } + + override suspend fun clearAll() { + // 아무 동작도 하지 않음 (fake 객체) + } + } } diff --git a/gradle.properties b/gradle.properties index aa78f7545..b5e9ab1b1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -22,3 +22,4 @@ android.enableJetifier=false android.defaults.buildfeatures.buildconfig=true android.nonTransitiveRClass=false android.nonFinalResIds=false +ksp.useKSP2=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f24f1c8fa..e33696ab6 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,8 +1,8 @@ [versions] -android-gradle-plugin = "8.5.0" +android-gradle-plugin = "8.9.1" material = "1.10.0" espresso = "3.4.0" -ksp = "1.9.10-1.0.13" +ksp = "2.0.21-1.0.28" google-service = "4.3.15" firebase-bom = "32.7.0" firebase-crashlytics = "2.9.9" @@ -31,7 +31,7 @@ androidx-recycler-view = "1.3.1" androidx-splash-screen = "1.0.1" androidx-datastore = "1.0.0" -kotlin = "1.9.10" +kotlin = "2.0.21" kotlinx-coroutines = "1.7.3" kotlinx-serialization-json = "1.5.1" kotlinx-datetime = "0.4.0" @@ -44,11 +44,12 @@ retrofit = "2.9.0" retrofit-kotlinx-serialization-json = "1.0.0" okhttp = "4.11.0" -hilt = "2.50" -room = "2.5.2" +hilt = "2.56" +hiltExt = "2.56" +room = "2.7.0" timber = "5.0.1" -encrypted-datastore = "1.7.21-1.0.8-1.1.0-alpha01" +encrypted-datastore = "2.0.21-1.0.28-1.2.0" ted-permission = "3.3.0" coil = "2.4.0" lottie = "6.0.0" @@ -59,12 +60,15 @@ junit4 = "4.13.2" protobuf-plugin = "0.9.4" -protobuf = "3.24.4" +protobuf = "3.25.5" espresso-core = "3.5.1" junit-ktx = "1.1.5" androidx-test-runner = "1.5.2" junit = "1.1.5" +kotlin-stdlib = "2.0.20" +kotlin-test = "2.0.20" +core = "1.6.1" [plugins] ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } @@ -79,6 +83,9 @@ hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } google-services = { id = "com.google.gms.google-services", version.ref = "google-service" } protobuf = { id = "com.google.protobuf", version.ref = "protobuf-plugin" } firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebase-crashlytics" } +compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +kotlin-multiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +android-kotlin-multiplatform-library = { id = "com.android.kotlin.multiplatform.library", version.ref = "android-gradle-plugin" } # Plugins defined by this project suwiki-android-application = { id = "suwiki.android.application", version = "unspecified" } @@ -132,10 +139,10 @@ orbit-compose = { group = "org.orbit-mvi", name = "orbit-compose", version.ref = orbit-test = { group = "org.orbit-mvi", name = "orbit-test", version.ref = "orbit" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } -hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" } +hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hiltExt" } hilt-core = { group = "com.google.dagger", name = "hilt-core", version.ref = "hilt" } hilt-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } -hilt-testing-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +hilt-testing-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hiltExt" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinx-coroutines" } kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinx-coroutines" } @@ -192,6 +199,9 @@ espresso-core = { group = "androidx.test.espresso", name = "espresso-core", vers protobuf-kotlin-lite = { group = "com.google.protobuf", name = "protobuf-kotlin-lite", version.ref = "protobuf" } protobuf-protoc = { group = "com.google.protobuf", name = "protoc", version.ref = "protobuf" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junit" } +kotlin-stdlib = { group = "org.jetbrains.kotlin", name = "kotlin-stdlib", version.ref = "kotlin-stdlib" } +kotlin-test = { group = "org.jetbrains.kotlin", name = "kotlin-test", version.ref = "kotlin-test" } +androidx-core = { group = "androidx.test", name = "core", version.ref = "core" } [bundles] firebase = ["firebase-analytics"] diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 20db9ad5c..4eaec4670 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/iosApp/suwiki.xcodeproj/project.pbxproj b/iosApp/suwiki.xcodeproj/project.pbxproj new file mode 100644 index 000000000..697ae1c68 --- /dev/null +++ b/iosApp/suwiki.xcodeproj/project.pbxproj @@ -0,0 +1,586 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXContainerItemProxy section */ + 9B4AB3952DB512550027B9A5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9B4AB37F2DB512530027B9A5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9B4AB3862DB512530027B9A5; + remoteInfo = suwiki; + }; + 9B4AB39F2DB512550027B9A5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 9B4AB37F2DB512530027B9A5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 9B4AB3862DB512530027B9A5; + remoteInfo = suwiki; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 9B4AB3872DB512530027B9A5 /* suwiki.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = suwiki.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B4AB3942DB512550027B9A5 /* suwikiTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = suwikiTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 9B4AB39E2DB512550027B9A5 /* suwikiUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = suwikiUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFileSystemSynchronizedRootGroup section */ + 9B4AB3892DB512530027B9A5 /* suwiki */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = suwiki; + sourceTree = ""; + }; + 9B4AB3972DB512550027B9A5 /* suwikiTests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = suwikiTests; + sourceTree = ""; + }; + 9B4AB3A12DB512550027B9A5 /* suwikiUITests */ = { + isa = PBXFileSystemSynchronizedRootGroup; + path = suwikiUITests; + sourceTree = ""; + }; +/* End PBXFileSystemSynchronizedRootGroup section */ + +/* Begin PBXFrameworksBuildPhase section */ + 9B4AB3842DB512530027B9A5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB3912DB512550027B9A5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB39B2DB512550027B9A5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 9B4AB37E2DB512530027B9A5 = { + isa = PBXGroup; + children = ( + 9B4AB3892DB512530027B9A5 /* suwiki */, + 9B4AB3972DB512550027B9A5 /* suwikiTests */, + 9B4AB3A12DB512550027B9A5 /* suwikiUITests */, + 9B4AB3882DB512530027B9A5 /* Products */, + ); + sourceTree = ""; + }; + 9B4AB3882DB512530027B9A5 /* Products */ = { + isa = PBXGroup; + children = ( + 9B4AB3872DB512530027B9A5 /* suwiki.app */, + 9B4AB3942DB512550027B9A5 /* suwikiTests.xctest */, + 9B4AB39E2DB512550027B9A5 /* suwikiUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 9B4AB3862DB512530027B9A5 /* suwiki */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B4AB3A82DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwiki" */; + buildPhases = ( + 9B4AB3BD2DB512C10027B9A5 /* ShellScript */, + 9B4AB3832DB512530027B9A5 /* Sources */, + 9B4AB3842DB512530027B9A5 /* Frameworks */, + 9B4AB3852DB512530027B9A5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + fileSystemSynchronizedGroups = ( + 9B4AB3892DB512530027B9A5 /* suwiki */, + ); + name = suwiki; + packageProductDependencies = ( + ); + productName = suwiki; + productReference = 9B4AB3872DB512530027B9A5 /* suwiki.app */; + productType = "com.apple.product-type.application"; + }; + 9B4AB3932DB512550027B9A5 /* suwikiTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B4AB3AB2DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwikiTests" */; + buildPhases = ( + 9B4AB3902DB512550027B9A5 /* Sources */, + 9B4AB3912DB512550027B9A5 /* Frameworks */, + 9B4AB3922DB512550027B9A5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9B4AB3962DB512550027B9A5 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 9B4AB3972DB512550027B9A5 /* suwikiTests */, + ); + name = suwikiTests; + packageProductDependencies = ( + ); + productName = suwikiTests; + productReference = 9B4AB3942DB512550027B9A5 /* suwikiTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 9B4AB39D2DB512550027B9A5 /* suwikiUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 9B4AB3AE2DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwikiUITests" */; + buildPhases = ( + 9B4AB39A2DB512550027B9A5 /* Sources */, + 9B4AB39B2DB512550027B9A5 /* Frameworks */, + 9B4AB39C2DB512550027B9A5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 9B4AB3A02DB512550027B9A5 /* PBXTargetDependency */, + ); + fileSystemSynchronizedGroups = ( + 9B4AB3A12DB512550027B9A5 /* suwikiUITests */, + ); + name = suwikiUITests; + packageProductDependencies = ( + ); + productName = suwikiUITests; + productReference = 9B4AB39E2DB512550027B9A5 /* suwikiUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 9B4AB37F2DB512530027B9A5 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1630; + LastUpgradeCheck = 1630; + TargetAttributes = { + 9B4AB3862DB512530027B9A5 = { + CreatedOnToolsVersion = 16.3; + }; + 9B4AB3932DB512550027B9A5 = { + CreatedOnToolsVersion = 16.3; + TestTargetID = 9B4AB3862DB512530027B9A5; + }; + 9B4AB39D2DB512550027B9A5 = { + CreatedOnToolsVersion = 16.3; + TestTargetID = 9B4AB3862DB512530027B9A5; + }; + }; + }; + buildConfigurationList = 9B4AB3822DB512530027B9A5 /* Build configuration list for PBXProject "suwiki" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 9B4AB37E2DB512530027B9A5; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 9B4AB3882DB512530027B9A5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 9B4AB3862DB512530027B9A5 /* suwiki */, + 9B4AB3932DB512550027B9A5 /* suwikiTests */, + 9B4AB39D2DB512550027B9A5 /* suwikiUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 9B4AB3852DB512530027B9A5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB3922DB512550027B9A5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB39C2DB512550027B9A5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 9B4AB3BD2DB512C10027B9A5 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 9B4AB3832DB512530027B9A5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB3902DB512550027B9A5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9B4AB39A2DB512550027B9A5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 9B4AB3962DB512550027B9A5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9B4AB3862DB512530027B9A5 /* suwiki */; + targetProxy = 9B4AB3952DB512550027B9A5 /* PBXContainerItemProxy */; + }; + 9B4AB3A02DB512550027B9A5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 9B4AB3862DB512530027B9A5 /* suwiki */; + targetProxy = 9B4AB39F2DB512550027B9A5 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 9B4AB3A62DB512550027B9A5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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; + DEVELOPMENT_TEAM = 2733AMRJU9; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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 = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 9B4AB3A72DB512550027B9A5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + 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_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + 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"; + DEVELOPMENT_TEAM = 2733AMRJU9; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + 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 = 18.4; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 9B4AB3A92DB512550027B9A5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwiki; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 9B4AB3AA2DB512550027B9A5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + ENABLE_PREVIEWS = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwiki; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 9B4AB3AC2DB512550027B9A5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwikiTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/suwiki.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/suwiki"; + }; + name = Debug; + }; + 9B4AB3AD2DB512550027B9A5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + GENERATE_INFOPLIST_FILE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 18.4; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwikiTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/suwiki.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/suwiki"; + }; + name = Release; + }; + 9B4AB3AF2DB512550027B9A5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwikiUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = suwiki; + }; + name = Debug; + }; + 9B4AB3B02DB512550027B9A5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = 2733AMRJU9; + GENERATE_INFOPLIST_FILE = YES; + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = suwiki.suwikiUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = suwiki; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 9B4AB3822DB512530027B9A5 /* Build configuration list for PBXProject "suwiki" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B4AB3A62DB512550027B9A5 /* Debug */, + 9B4AB3A72DB512550027B9A5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B4AB3A82DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwiki" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B4AB3A92DB512550027B9A5 /* Debug */, + 9B4AB3AA2DB512550027B9A5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B4AB3AB2DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwikiTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B4AB3AC2DB512550027B9A5 /* Debug */, + 9B4AB3AD2DB512550027B9A5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 9B4AB3AE2DB512550027B9A5 /* Build configuration list for PBXNativeTarget "suwikiUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 9B4AB3AF2DB512550027B9A5 /* Debug */, + 9B4AB3B02DB512550027B9A5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 9B4AB37F2DB512530027B9A5 /* Project object */; +} diff --git a/iosApp/suwiki.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/iosApp/suwiki.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/iosApp/suwiki.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/iosApp/suwiki.xcodeproj/project.xcworkspace/xcuserdata/jinukeu.xcuserdatad/UserInterfaceState.xcuserstate b/iosApp/suwiki.xcodeproj/project.xcworkspace/xcuserdata/jinukeu.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 000000000..8834b243a Binary files /dev/null and b/iosApp/suwiki.xcodeproj/project.xcworkspace/xcuserdata/jinukeu.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/iosApp/suwiki.xcodeproj/xcuserdata/jinukeu.xcuserdatad/xcschemes/xcschememanagement.plist b/iosApp/suwiki.xcodeproj/xcuserdata/jinukeu.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 000000000..65fa1684c --- /dev/null +++ b/iosApp/suwiki.xcodeproj/xcuserdata/jinukeu.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + suwiki.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/iosApp/suwiki/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/suwiki/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..eb8789700 --- /dev/null +++ b/iosApp/suwiki/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/suwiki/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/suwiki/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..230588010 --- /dev/null +++ b/iosApp/suwiki/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,35 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "tinted" + } + ], + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/suwiki/Assets.xcassets/Contents.json b/iosApp/suwiki/Assets.xcassets/Contents.json new file mode 100644 index 000000000..73c00596a --- /dev/null +++ b/iosApp/suwiki/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/suwiki/ContentView.swift b/iosApp/suwiki/ContentView.swift new file mode 100644 index 000000000..1f427289b --- /dev/null +++ b/iosApp/suwiki/ContentView.swift @@ -0,0 +1,25 @@ +// +// ContentView.swift +// suwiki +// +// Created by 이진욱 on 4/20/25. +// + +import SwiftUI +import sharedKit + +struct ContentView: View { + var body: some View { + VStack { + Image(systemName: "globe") + .imageScale(.large) + .foregroundStyle(.tint) + Text(Greeting().greet()) + } + .padding() + } +} + +#Preview { + ContentView() +} diff --git a/iosApp/suwiki/suwikiApp.swift b/iosApp/suwiki/suwikiApp.swift new file mode 100644 index 000000000..fb0c8ba29 --- /dev/null +++ b/iosApp/suwiki/suwikiApp.swift @@ -0,0 +1,17 @@ +// +// suwikiApp.swift +// suwiki +// +// Created by 이진욱 on 4/20/25. +// + +import SwiftUI + +@main +struct suwikiApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} diff --git a/iosApp/suwikiTests/suwikiTests.swift b/iosApp/suwikiTests/suwikiTests.swift new file mode 100644 index 000000000..79951f5ab --- /dev/null +++ b/iosApp/suwikiTests/suwikiTests.swift @@ -0,0 +1,17 @@ +// +// suwikiTests.swift +// suwikiTests +// +// Created by 이진욱 on 4/20/25. +// + +import Testing +@testable import suwiki + +struct suwikiTests { + + @Test func example() async throws { + // Write your test here and use APIs like `#expect(...)` to check expected conditions. + } + +} diff --git a/iosApp/suwikiUITests/suwikiUITests.swift b/iosApp/suwikiUITests/suwikiUITests.swift new file mode 100644 index 000000000..18f2096e9 --- /dev/null +++ b/iosApp/suwikiUITests/suwikiUITests.swift @@ -0,0 +1,41 @@ +// +// suwikiUITests.swift +// suwikiUITests +// +// Created by 이진욱 on 4/20/25. +// + +import XCTest + +final class suwikiUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + @MainActor + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + @MainActor + func testLaunchPerformance() throws { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } +} diff --git a/iosApp/suwikiUITests/suwikiUITestsLaunchTests.swift b/iosApp/suwikiUITests/suwikiUITestsLaunchTests.swift new file mode 100644 index 000000000..7d9f9df08 --- /dev/null +++ b/iosApp/suwikiUITests/suwikiUITestsLaunchTests.swift @@ -0,0 +1,33 @@ +// +// suwikiUITestsLaunchTests.swift +// suwikiUITests +// +// Created by 이진욱 on 4/20/25. +// + +import XCTest + +final class suwikiUITestsLaunchTests: XCTestCase { + + override class var runsForEachTargetApplicationUIConfiguration: Bool { + true + } + + override func setUpWithError() throws { + continueAfterFailure = false + } + + @MainActor + func testLaunch() throws { + let app = XCUIApplication() + app.launch() + + // Insert steps here to perform after app launch but before taking a screenshot, + // such as logging into a test account or navigating somewhere in the app + + let attachment = XCTAttachment(screenshot: app.screenshot()) + attachment.name = "Launch Screen" + attachment.lifetime = .keepAlways + add(attachment) + } +} diff --git a/schemas/com.suwiki.local.common.database.OpenMajorDatabase/2.json b/schemas/com.suwiki.local.common.database.OpenMajorDatabase/2.json new file mode 100644 index 000000000..e1a8cce2c --- /dev/null +++ b/schemas/com.suwiki.local.common.database.OpenMajorDatabase/2.json @@ -0,0 +1,40 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "a7aa694667974b6b111d2d7755530138", + "entities": [ + { + "tableName": "OpenMajorEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`name` TEXT NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", + "fields": [ + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": true, + "columnNames": [ + "id" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a7aa694667974b6b111d2d7755530138')" + ] + } +} \ No newline at end of file diff --git a/schemas/com.suwiki.local.common.database.TimetableDatabase/2.json b/schemas/com.suwiki.local.common.database.TimetableDatabase/2.json new file mode 100644 index 000000000..ef8b7650d --- /dev/null +++ b/schemas/com.suwiki.local.common.database.TimetableDatabase/2.json @@ -0,0 +1,58 @@ +{ + "formatVersion": 1, + "database": { + "version": 2, + "identityHash": "d3ae462e5e5245e675c522c8e1ff9800", + "entities": [ + { + "tableName": "TimetableEntity", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`createTime` INTEGER NOT NULL, `year` TEXT NOT NULL, `semester` TEXT NOT NULL, `name` TEXT NOT NULL, `cellList` TEXT NOT NULL, PRIMARY KEY(`createTime`))", + "fields": [ + { + "fieldPath": "createTime", + "columnName": "createTime", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "year", + "columnName": "year", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "semester", + "columnName": "semester", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "cellList", + "columnName": "cellList", + "affinity": "TEXT", + "notNull": true + } + ], + "primaryKey": { + "autoGenerate": false, + "columnNames": [ + "createTime" + ] + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd3ae462e5e5245e675c522c8e1ff9800')" + ] + } +} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index 3058c7643..04abfa774 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -10,7 +10,6 @@ pluginManagement { } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() @@ -67,3 +66,4 @@ include(":remote:common") include(":domain:common") include(":presentation:common:ui") include(":presentation:common:designsystem") +include(":shared") diff --git a/shared/.gitignore b/shared/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/shared/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts new file mode 100644 index 000000000..19cdd84d5 --- /dev/null +++ b/shared/build.gradle.kts @@ -0,0 +1,99 @@ +plugins { + alias(libs.plugins.kotlin.multiplatform) + alias(libs.plugins.android.kotlin.multiplatform.library) +} + +kotlin { + +// Target declarations - add or remove as needed below. These define +// which platforms this KMP module supports. +// See: https://kotlinlang.org/docs/multiplatform-discover-project.html#targets + androidLibrary { + namespace = "com.iosApp.shared" + compileSdk = 35 + minSdk = 28 + + withHostTestBuilder { + } + + withDeviceTestBuilder { + sourceSetTreeName = "test" + }.configure { + instrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + } + +// For iOS targets, this is also where you should +// configure native binary output. For more information, see: +// https://kotlinlang.org/docs/multiplatform-build-native-binaries.html#build-xcframeworks + +// A step-by-step guide on how to include this library in an XCode +// project can be found here: +// https://developer.android.com/kotlin/multiplatform/migrate + val xcfName = "sharedKit" + + iosX64 { + binaries.framework { + baseName = xcfName + } + } + + iosArm64 { + binaries.framework { + baseName = xcfName + } + } + + iosSimulatorArm64 { + binaries.framework { + baseName = xcfName + } + } + +// Source set declarations. +// Declaring a target automatically creates a source set with the same name. By default, the +// Kotlin Gradle Plugin creates additional source sets that depend on each other, since it is +// common to share sources between related targets. +// See: https://kotlinlang.org/docs/multiplatform-hierarchy.html + sourceSets { + commonMain { + dependencies { + implementation(libs.kotlin.stdlib) + // Add KMP dependencies here + } + } + + commonTest { + dependencies { + implementation(libs.kotlin.test) + } + } + + androidMain { + dependencies { + // Add Android-specific dependencies here. Note that this source set depends on + // commonMain by default and will correctly pull the Android artifacts of any KMP + // dependencies declared in commonMain. + } + } + + getByName("androidDeviceTest") { + dependencies { + implementation(libs.androidx.test.runner) + implementation(libs.androidx.core) + implementation(libs.androidx.junit) + } + } + + iosMain { + dependencies { + // Add iOS-specific dependencies here. This a source set created by Kotlin Gradle + // Plugin (KGP) that each specific iOS target (e.g., iosX64) depends on as + // part of KMP’s default source set hierarchy. Note that this source set depends + // on common by default and will correctly pull the iOS artifacts of any + // KMP dependencies declared in commonMain. + } + } + } + +} diff --git a/shared/src/androidDeviceTest/kotlin/com/suwiki/shared/ExampleInstrumentedTest.kt b/shared/src/androidDeviceTest/kotlin/com/suwiki/shared/ExampleInstrumentedTest.kt new file mode 100644 index 000000000..86e0e4ba4 --- /dev/null +++ b/shared/src/androidDeviceTest/kotlin/com/suwiki/shared/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.suwiki.shared + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.suwiki.shared.test", appContext.packageName) + } +} diff --git a/shared/src/androidHostTest/kotlin/com/suwiki/shared/ExampleUnitTest.kt b/shared/src/androidHostTest/kotlin/com/suwiki/shared/ExampleUnitTest.kt new file mode 100644 index 000000000..49aa0ca3d --- /dev/null +++ b/shared/src/androidHostTest/kotlin/com/suwiki/shared/ExampleUnitTest.kt @@ -0,0 +1,16 @@ +package com.suwiki.shared + +import kotlin.test.Test +import kotlin.test.assertEquals + +/** + * Example local unit test, which will execute on the development machine (host). + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +class ExampleUnitTest { + @Test + fun addition_isCorrect() { + assertEquals(4, 2 + 2) + } +} \ No newline at end of file diff --git a/shared/src/androidMain/AndroidManifest.xml b/shared/src/androidMain/AndroidManifest.xml new file mode 100644 index 000000000..8bdb7e14b --- /dev/null +++ b/shared/src/androidMain/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + diff --git a/shared/src/androidMain/kotlin/com/suwiki/shared/Platform.android.kt b/shared/src/androidMain/kotlin/com/suwiki/shared/Platform.android.kt new file mode 100644 index 000000000..150dfd0a4 --- /dev/null +++ b/shared/src/androidMain/kotlin/com/suwiki/shared/Platform.android.kt @@ -0,0 +1,9 @@ +package com.suwiki.shared + +import android.os.Build + +class AndroidPlatform : Platform { + override val name: String = "Android ${Build.VERSION.SDK_INT}" +} + +actual fun getPlatform(): Platform = AndroidPlatform() diff --git a/shared/src/commonMain/kotlin/com/suwiki/shared/Greeting.kt b/shared/src/commonMain/kotlin/com/suwiki/shared/Greeting.kt new file mode 100644 index 000000000..9f535477e --- /dev/null +++ b/shared/src/commonMain/kotlin/com/suwiki/shared/Greeting.kt @@ -0,0 +1,9 @@ +package com.suwiki.shared + +class Greeting { + private val platform = getPlatform() + + fun greet(): String { + return "Hello, ${platform.name}!" + } +} diff --git a/shared/src/commonMain/kotlin/com/suwiki/shared/Platform.kt b/shared/src/commonMain/kotlin/com/suwiki/shared/Platform.kt new file mode 100644 index 000000000..7fedfa0dc --- /dev/null +++ b/shared/src/commonMain/kotlin/com/suwiki/shared/Platform.kt @@ -0,0 +1,7 @@ +package com.suwiki.shared + +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform diff --git a/shared/src/iosMain/kotlin/com/suwiki/shared/Platform.ios.kt b/shared/src/iosMain/kotlin/com/suwiki/shared/Platform.ios.kt new file mode 100644 index 000000000..2836eaed8 --- /dev/null +++ b/shared/src/iosMain/kotlin/com/suwiki/shared/Platform.ios.kt @@ -0,0 +1,9 @@ +package com.suwiki.shared + +import platform.UIKit.UIDevice + +class IOSPlatform: Platform { + override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion +} + +actual fun getPlatform(): Platform = IOSPlatform()