diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 0568025..74074ee 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -13,18 +13,9 @@ permissions: contents: write jobs: - deploy-sdk: - name: Deploy SDK - strategy: - matrix: - include: - # - target: publishIosArm64PublicationToSonatypeRepository - # os: macos-latest - - target: :shared:publishAllPublicationsToMavenCentralRepository - os: ubuntu-latest - - target: :komoju-android-sdk:publishAllPublicationsToMavenCentralRepository - os: ubuntu-latest - runs-on: ${{ matrix.os }} + deploy-android-sdk: + name: Deploy Android SDK + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Validate Gradle Wrapper @@ -50,8 +41,8 @@ jobs: echo Version: TAG echo "SDK_VERSION=${TAG#v}" >> $GITHUB_ENV - - name: Deploy - run: chmod +x gradlew && ./gradlew ${{ matrix.target }} --no-configuration-cache --no-daemon + - name: Build & Deploy + run: chmod +x gradlew && ./gradlew :shared:publishAllPublicationsToMavenCentralRepository :komoju-android-sdk:publishAllPublicationsToMavenCentralRepository --no-configuration-cache --no-daemon env: ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.OSSRH_USERNAME }} ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.OSSRH_PASSWORD }} @@ -59,15 +50,19 @@ jobs: ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.OSSRH_GPG_SECRET_KEY_PASSWORD }} SDK_VERSION: ${{ env.SDK_VERSION }} - - deploy-app: + deploy-android-app: name: Deploy Android App - needs: deploy-sdk + needs: deploy-android-sdk runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Validate Gradle Wrapper uses: gradle/actions/wrapper-validation@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.konan + key: ${{ runner.os }}-${{ hashFiles('**/.lock') }} - name: Set up JDK 17 uses: actions/setup-java@v4 with: @@ -93,4 +88,89 @@ jobs: asset_name: soundBud-release.apk make_latest: false env: - SDK_VERSION: ${{ env.SDK_VERSION }} \ No newline at end of file + SDK_VERSION: ${{ env.SDK_VERSION }} + + deploy-ios-sdk: + name: Deploy iOS SDK + runs-on: macos-latest-xlarge + steps: + - uses: actions/checkout@v4 + - name: Validate Gradle Wrapper + uses: gradle/actions/wrapper-validation@v4 + - uses: actions/cache@v4 + with: + path: | + ~/.konan + key: ${{ runner.os }}-${{ hashFiles('**/.lock') }} + - uses: maxim-lobanov/setup-xcode@v1 + with: + xcode-version: '16.0.0' + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + distribution: zulu + java-version: 17 + - name: Write release version + run: | + TAG=${{ github.event.release.tag_name }} + echo Version: TAG + echo "SDK_VERSION=${TAG#v}" >> $GITHUB_ENV + - name: Build Shared SDK framework + run: | + chmod +x gradlew && ./gradlew :shared:linkReleaseFrameworkIosArm64 :shared:linkReleaseFrameworkIosSimulatorArm64 --no-configuration-cache --no-daemon + + rm -rf ./build/outputs/xcframework/KomojuSharedSDK.xcframework + + xcodebuild -create-xcframework \ + -framework ./shared/build/bin/iosArm64/releaseFramework/komojuShared.framework \ + -framework ./shared/build/bin/iosSimulatorArm64/releaseFramework/komojuShared.framework \ + -output ./build/outputs/xcframework/KomojuSharedSDK.xcframework + + zip -r KomojuSharedSDK.zip ./build/outputs/xcframework/KomojuSharedSDK.xcframework + + - name: Build Shared SDK Swift Wrapper + working-directory: ./komoju-ios-sdk + run: | + # Build & Archive for iOS + xcodebuild archive \ + -scheme KomojuSDK \ + -configuration Release \ + -destination "generic/platform=iOS" \ + -archivePath "./build/xcframework/komoju_ios.xcarchive" \ + -sdk iphoneos \ + ONLY_ACTIVE_ARCH=NO \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + + + # Build & Archive for iOS Simulator + xcodebuild archive \ + -scheme KomojuSDK \ + -configuration Release \ + -destination "generic/platform=iOS Simulator" \ + -archivePath "./build/xcframework/komoju_ios_simulators.xcarchive" \ + -sdk iphonesimulator \ + ONLY_ACTIVE_ARCH=NO \ + SKIP_INSTALL=NO \ + BUILD_LIBRARY_FOR_DISTRIBUTION=YES + + rm -rf ./build/outputs/xcframework/KomojuIosSDK.xcframework + + xcodebuild -create-xcframework \ + -framework ./build/xcframework/komoju_ios.xcarchive/Products/Library/Frameworks/KomojuSDK.framework \ + -framework ./build/xcframework/komoju_ios_simulators.xcarchive/Products/Library/Frameworks/KomojuSDK.framework \ + -output ./build/outputs/xcframework/KomojuIosSDK.xcframework + + zip -r KomojuIosSDK.zip ./build/outputs/xcframework/KomojuIosSDK.xcframework + - name: Publish Shared SDK Framework! + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: KomojuSharedSDK.zip + make_latest: false + - name: Publish iOS SDK Framework! + uses: svenstaro/upload-release-action@v2 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: komoju-ios-sdk/KomojuIosSDK.zip + make_latest: false \ No newline at end of file diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index d8c5a40..6e40f8a 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -2,7 +2,7 @@ name: Verify PR on: pull_request: - types: [opened, synchronize, reopened, ready_for_review] + types: [ opened, synchronize, reopened, ready_for_review ] concurrency: group: github-${{ github.ref }} @@ -42,4 +42,16 @@ jobs: distribution: zulu java-version: 17 - name: Run Gradle - run: ./gradlew :komoju-android-sdk:lintDebug \ No newline at end of file + run: ./gradlew :komoju-android-sdk:lintDebug + swiftlint: + runs-on: ubuntu-latest + name: SwiftLint + if: github.event.pull_request.draft == false + steps: + - uses: actions/checkout@v1 + - name: swiftLint on ios sdk + uses: norio-nomura/action-swiftlint@3.2.1 + with: + args: --strict + env: + WORKING_DIRECTORY: komoju-ios-sdk diff --git a/example-ios/.gitignore b/example-ios/.gitignore new file mode 100644 index 0000000..0f29ced --- /dev/null +++ b/example-ios/.gitignore @@ -0,0 +1,2 @@ +dev.xcconfig +live.xcconfig \ No newline at end of file diff --git a/example-ios/example-ios.xcodeproj/project.pbxproj b/example-ios/example-ios.xcodeproj/project.pbxproj new file mode 100644 index 0000000..77bc1b7 --- /dev/null +++ b/example-ios/example-ios.xcodeproj/project.pbxproj @@ -0,0 +1,530 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 7C0964882CE2E92600D5AE76 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 7C0964872CE2E92600D5AE76 /* Localizable.xcstrings */; }; + 7C0964D82CE30CB300D5AE76 /* FakeStoreSuccessView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0964D72CE30CB300D5AE76 /* FakeStoreSuccessView.swift */; }; + 7C0964DA2CE3484600D5AE76 /* FakeStoreFailedView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0964D92CE3484600D5AE76 /* FakeStoreFailedView.swift */; }; + 7C0966D52CE4523300D5AE76 /* KomojuSDK.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7C0966D42CE4522F00D5AE76 /* KomojuSDK.framework */; }; + 7C0966D62CE4523300D5AE76 /* KomojuSDK.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 7C0966D42CE4522F00D5AE76 /* KomojuSDK.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; + 7CA94A212CE21506008A3649 /* FakeItemDetailScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A1A2CE21506008A3649 /* FakeItemDetailScreen.swift */; }; + 7CA94A222CE21506008A3649 /* FakeStoreViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A1E2CE21506008A3649 /* FakeStoreViewModel.swift */; }; + 7CA94A232CE21506008A3649 /* CreateSessionResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A152CE21506008A3649 /* CreateSessionResponse.swift */; }; + 7CA94A242CE21506008A3649 /* FakeStoreView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A1D2CE21506008A3649 /* FakeStoreView.swift */; }; + 7CA94A252CE21506008A3649 /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A142CE21506008A3649 /* iOSApp.swift */; }; + 7CA94A262CE21506008A3649 /* RemoteApiServices.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A182CE21506008A3649 /* RemoteApiServices.swift */; }; + 7CA94A272CE21506008A3649 /* FakeStoreUiState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A1C2CE21506008A3649 /* FakeStoreUiState.swift */; }; + 7CA94A282CE21506008A3649 /* PublishableKeyResponse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A162CE21506008A3649 /* PublishableKeyResponse.swift */; }; + 7CA94A2A2CE21506008A3649 /* FakeStoreDisplayDataRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA94A1B2CE21506008A3649 /* FakeStoreDisplayDataRepository.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 7C0966D32CE4522F00D5AE76 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 7C0966CE2CE4522F00D5AE76 /* KomojuSDK.xcodeproj */; + proxyType = 2; + remoteGlobalIDString = 7CCCA0A82CCA23C600615A83; + remoteInfo = KomojuSDK; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + 7CA94A052CE1DD4B008A3649 /* Embed Frameworks */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 10; + files = ( + 7C0966D62CE4523300D5AE76 /* KomojuSDK.framework in Embed Frameworks */, + ); + name = "Embed Frameworks"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 7C0964872CE2E92600D5AE76 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = ""; }; + 7C0964D72CE30CB300D5AE76 /* FakeStoreSuccessView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreSuccessView.swift; sourceTree = ""; }; + 7C0964D92CE3484600D5AE76 /* FakeStoreFailedView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreFailedView.swift; sourceTree = ""; }; + 7C0966C72CE4522F00D5AE76 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7C0966CC2CE4522F00D5AE76 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 7C0966CE2CE4522F00D5AE76 /* KomojuSDK.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = KomojuSDK.xcodeproj; sourceTree = ""; }; + 7CA949BE2CE1D9AF008A3649 /* KomojuIosSDKExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = KomojuIosSDKExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7CA94A112CE21421008A3649 /* dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = dev.xcconfig; sourceTree = ""; }; + 7CA94A122CE21421008A3649 /* live.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = live.xcconfig; sourceTree = ""; }; + 7CA94A142CE21506008A3649 /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7CA94A152CE21506008A3649 /* CreateSessionResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateSessionResponse.swift; sourceTree = ""; }; + 7CA94A162CE21506008A3649 /* PublishableKeyResponse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PublishableKeyResponse.swift; sourceTree = ""; }; + 7CA94A182CE21506008A3649 /* RemoteApiServices.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RemoteApiServices.swift; sourceTree = ""; }; + 7CA94A1A2CE21506008A3649 /* FakeItemDetailScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeItemDetailScreen.swift; sourceTree = ""; }; + 7CA94A1B2CE21506008A3649 /* FakeStoreDisplayDataRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreDisplayDataRepository.swift; sourceTree = ""; }; + 7CA94A1C2CE21506008A3649 /* FakeStoreUiState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreUiState.swift; sourceTree = ""; }; + 7CA94A1D2CE21506008A3649 /* FakeStoreView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreView.swift; sourceTree = ""; }; + 7CA94A1E2CE21506008A3649 /* FakeStoreViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FakeStoreViewModel.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + B92378962B6B1156000C7307 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7C0966D52CE4523300D5AE76 /* KomojuSDK.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + 7C0966CF2CE4522F00D5AE76 /* komoju-ios-sdk */, + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + 7CA94A112CE21421008A3649 /* dev.xcconfig */, + 7CA94A122CE21421008A3649 /* live.xcconfig */, + 7555FF7D242A565900829871 /* example-ios */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7CA949BE2CE1D9AF008A3649 /* KomojuIosSDKExample.app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* example-ios */ = { + isa = PBXGroup; + children = ( + 7CA94A142CE21506008A3649 /* iOSApp.swift */, + 7CA94A192CE21506008A3649 /* remote */, + 7CA94A202CE21506008A3649 /* screens */, + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF8C242A565B00829871 /* Info.plist */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + 7C0964872CE2E92600D5AE76 /* Localizable.xcstrings */, + ); + path = "example-ios"; + sourceTree = ""; + }; + 7C0966C62CE4522F00D5AE76 /* types */ = { + isa = PBXGroup; + children = ( + ); + path = types; + sourceTree = ""; + }; + 7C0966CB2CE4522F00D5AE76 /* KomojuSDK */ = { + isa = PBXGroup; + children = ( + 7C0966C62CE4522F00D5AE76 /* types */, + 7C0966C72CE4522F00D5AE76 /* Assets.xcassets */, + ); + path = KomojuSDK; + sourceTree = ""; + }; + 7C0966CD2CE4522F00D5AE76 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 7C0966CC2CE4522F00D5AE76 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 7C0966CF2CE4522F00D5AE76 /* komoju-ios-sdk */ = { + isa = PBXGroup; + children = ( + 7C0966CB2CE4522F00D5AE76 /* KomojuSDK */, + 7C0966CD2CE4522F00D5AE76 /* Preview Content */, + 7C0966CE2CE4522F00D5AE76 /* KomojuSDK.xcodeproj */, + ); + name = "komoju-ios-sdk"; + path = "../komoju-ios-sdk"; + sourceTree = ""; + }; + 7C0966D02CE4522F00D5AE76 /* Products */ = { + isa = PBXGroup; + children = ( + 7C0966D42CE4522F00D5AE76 /* KomojuSDK.framework */, + ); + name = Products; + sourceTree = ""; + }; + 7CA94A172CE21506008A3649 /* dtos */ = { + isa = PBXGroup; + children = ( + 7CA94A152CE21506008A3649 /* CreateSessionResponse.swift */, + 7CA94A162CE21506008A3649 /* PublishableKeyResponse.swift */, + ); + path = dtos; + sourceTree = ""; + }; + 7CA94A192CE21506008A3649 /* remote */ = { + isa = PBXGroup; + children = ( + 7CA94A172CE21506008A3649 /* dtos */, + 7CA94A182CE21506008A3649 /* RemoteApiServices.swift */, + ); + path = remote; + sourceTree = ""; + }; + 7CA94A1F2CE21506008A3649 /* store */ = { + isa = PBXGroup; + children = ( + 7C0964D92CE3484600D5AE76 /* FakeStoreFailedView.swift */, + 7CA94A1A2CE21506008A3649 /* FakeItemDetailScreen.swift */, + 7CA94A1B2CE21506008A3649 /* FakeStoreDisplayDataRepository.swift */, + 7CA94A1C2CE21506008A3649 /* FakeStoreUiState.swift */, + 7CA94A1D2CE21506008A3649 /* FakeStoreView.swift */, + 7CA94A1E2CE21506008A3649 /* FakeStoreViewModel.swift */, + 7C0964D72CE30CB300D5AE76 /* FakeStoreSuccessView.swift */, + ); + path = store; + sourceTree = ""; + }; + 7CA94A202CE21506008A3649 /* screens */ = { + isa = PBXGroup; + children = ( + 7CA94A1F2CE21506008A3649 /* store */, + ); + path = screens; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* example-ios */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "example-ios" */; + buildPhases = ( + B92378962B6B1156000C7307 /* Frameworks */, + 7555FF77242A565900829871 /* Sources */, + 7555FF79242A565900829871 /* Resources */, + 7CA94A052CE1DD4B008A3649 /* Embed Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "example-ios"; + packageProductDependencies = ( + ); + productName = iosApp; + productReference = 7CA949BE2CE1D9AF008A3649 /* KomojuIosSDKExample.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = YES; + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1540; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "example-ios" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ja, + ); + mainGroup = 7555FF72242A565900829871; + packageReferences = ( + ); + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectReferences = ( + { + ProductGroup = 7C0966D02CE4522F00D5AE76 /* Products */; + ProjectRef = 7C0966CE2CE4522F00D5AE76 /* KomojuSDK.xcodeproj */; + }, + ); + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* example-ios */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXReferenceProxy section */ + 7C0966D42CE4522F00D5AE76 /* KomojuSDK.framework */ = { + isa = PBXReferenceProxy; + fileType = wrapper.framework; + path = KomojuSDK.framework; + remoteRef = 7C0966D32CE4522F00D5AE76 /* PBXContainerItemProxy */; + sourceTree = BUILT_PRODUCTS_DIR; + }; +/* End PBXReferenceProxy section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 7C0964882CE2E92600D5AE76 /* Localizable.xcstrings in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7CA94A212CE21506008A3649 /* FakeItemDetailScreen.swift in Sources */, + 7C0964D82CE30CB300D5AE76 /* FakeStoreSuccessView.swift in Sources */, + 7CA94A222CE21506008A3649 /* FakeStoreViewModel.swift in Sources */, + 7CA94A232CE21506008A3649 /* CreateSessionResponse.swift in Sources */, + 7C0964DA2CE3484600D5AE76 /* FakeStoreFailedView.swift in Sources */, + 7CA94A242CE21506008A3649 /* FakeStoreView.swift in Sources */, + 7CA94A252CE21506008A3649 /* iOSApp.swift in Sources */, + 7CA94A262CE21506008A3649 /* RemoteApiServices.swift in Sources */, + 7CA94A272CE21506008A3649 /* FakeStoreUiState.swift in Sources */, + 7CA94A282CE21506008A3649 /* PublishableKeyResponse.swift in Sources */, + 7CA94A2A2CE21506008A3649 /* FakeStoreDisplayDataRepository.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7CA94A112CE21421008A3649 /* dev.xcconfig */; + 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_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"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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 = 15.3; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 7CA94A122CE21421008A3649 /* live.xcconfig */; + 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_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"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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 = 15.3; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "example-ios/Preview\\ Content"; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = "example-ios/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "example-ios/Preview\\ Content"; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = "example-ios/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "example-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "example-ios" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/example-ios/example-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/example-ios/example-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/example-ios/example-ios.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/example-ios/example-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example-ios/example-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example-ios/example-ios.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example-ios/example-ios/Assets.xcassets/AccentColor.colorset/Contents.json b/example-ios/example-ios/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/Contents.json b/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..8edf56e --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000..53fc536 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png differ diff --git a/example-ios/example-ios/Assets.xcassets/Contents.json b/example-ios/example-ios/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/Contents.json new file mode 100644 index 0000000..e9a5bbd --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "earphone_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/earphone_1.png b/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/earphone_1.png new file mode 100644 index 0000000..be68624 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/earphone_1.imageset/earphone_1.png differ diff --git a/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/Contents.json new file mode 100644 index 0000000..114d55a --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "earphone_2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/earphone_2.png b/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/earphone_2.png new file mode 100644 index 0000000..4ef03d2 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/earphone_2.imageset/earphone_2.png differ diff --git a/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/Contents.json new file mode 100644 index 0000000..368df51 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "earphone_3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/earphone_3.png b/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/earphone_3.png new file mode 100644 index 0000000..50ac292 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/earphone_3.imageset/earphone_3.png differ diff --git a/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/Contents.json new file mode 100644 index 0000000..2123d47 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "earphone_4.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/earphone_4.png b/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/earphone_4.png new file mode 100644 index 0000000..9ed316d Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/earphone_4.imageset/earphone_4.png differ diff --git a/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/Contents.json new file mode 100644 index 0000000..4883ffa --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "headphone_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/headphone_1.png b/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/headphone_1.png new file mode 100644 index 0000000..0023bb9 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/headphone_1.imageset/headphone_1.png differ diff --git a/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/Contents.json new file mode 100644 index 0000000..f6d2c69 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "headphone_2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/headphone_2.png b/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/headphone_2.png new file mode 100644 index 0000000..f32bdf0 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/headphone_2.imageset/headphone_2.png differ diff --git a/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/Contents.json new file mode 100644 index 0000000..99026c3 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "headphone_3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/headphone_3.png b/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/headphone_3.png new file mode 100644 index 0000000..4c7630a Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/headphone_3.imageset/headphone_3.png differ diff --git a/example-ios/example-ios/Assets.xcassets/mic_1.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/mic_1.imageset/Contents.json new file mode 100644 index 0000000..59e8d25 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/mic_1.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "mic_1.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/mic_1.imageset/mic_1.png b/example-ios/example-ios/Assets.xcassets/mic_1.imageset/mic_1.png new file mode 100644 index 0000000..9868e36 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/mic_1.imageset/mic_1.png differ diff --git a/example-ios/example-ios/Assets.xcassets/mic_2.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/mic_2.imageset/Contents.json new file mode 100644 index 0000000..ff58aa1 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/mic_2.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "mic_2.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/mic_2.imageset/mic_2.png b/example-ios/example-ios/Assets.xcassets/mic_2.imageset/mic_2.png new file mode 100644 index 0000000..6d1a899 Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/mic_2.imageset/mic_2.png differ diff --git a/example-ios/example-ios/Assets.xcassets/mic_3.imageset/Contents.json b/example-ios/example-ios/Assets.xcassets/mic_3.imageset/Contents.json new file mode 100644 index 0000000..55fcc90 --- /dev/null +++ b/example-ios/example-ios/Assets.xcassets/mic_3.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "mic_3.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/example-ios/example-ios/Assets.xcassets/mic_3.imageset/mic_3.png b/example-ios/example-ios/Assets.xcassets/mic_3.imageset/mic_3.png new file mode 100644 index 0000000..d56b8ce Binary files /dev/null and b/example-ios/example-ios/Assets.xcassets/mic_3.imageset/mic_3.png differ diff --git a/example-ios/example-ios/Info.plist b/example-ios/example-ios/Info.plist new file mode 100644 index 0000000..dbddce1 --- /dev/null +++ b/example-ios/example-ios/Info.plist @@ -0,0 +1,54 @@ + + + + + BASE_URL + https://$(BASE_URL) + CADisableMinimumFrameDurationOnPhone + + 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 + KOMOJU_SCHEME + $(KOMOJU_SCHEME) + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/example-ios/example-ios/Localizable.xcstrings b/example-ios/example-ios/Localizable.xcstrings new file mode 100644 index 0000000..fa83687 --- /dev/null +++ b/example-ios/example-ios/Localizable.xcstrings @@ -0,0 +1,472 @@ +{ + "sourceLanguage" : "en", + "strings" : { + ", " : { + "localizations" : { + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "," + } + } + } + }, + "black" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Black" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブラック" + } + } + } + }, + "blue" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Blue" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ブルー" + } + } + } + }, + "bud" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Bud!" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "バド" + } + } + } + }, + "buy_this_item" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Buy this item" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "この商品を購入する" + } + } + } + }, + "close" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Close" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "近い" + } + } + } + }, + "continue_shopping" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Continue Shopping" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "買い物を続ける" + } + } + } + }, + "description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Premium Sound Shopping App" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プレミアムサウンドショッピングアプリ" + } + } + } + }, + "earphone_description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "These wireless earphones are designed to deliver high-quality sound and a comfortable, secure fit. Featuring advanced noise isolation technology, they block out external noise, allowing for an immersive listening experience. The ergonomic design ensures the earphones stay in place, even during intense activities, making them ideal for workouts or daily commutes. Equipped with long battery life, you can enjoy continuous playtime throughout the day. The earphones also feature touch controls for easy access to music, calls, and voice assistants, making them a versatile companion for all your audio needs." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "これらのワイヤレスイヤホンは、高音質なサウンドと快適でしっかりとしたフィット感を提供するよう設計されています。高度なノイズアイソレーション技術を搭載し、外部のノイズを遮断することで、没入感のあるリスニング体験を実現します。人間工学に基づいたデザインにより、激しい運動中でもイヤホンが外れにくく、トレーニングや通勤に最適です。長時間のバッテリー寿命により、一日中連続して音楽を楽しむことができます。また、タッチコントロールを搭載しているため、音楽、通話、音声アシスタントに簡単にアクセスでき、あらゆるオーディオニーズに対応する万能なパートナーです。" + } + } + } + }, + "earphone_name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Premium Wireless Earphone" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プレミアムワイヤレスイヤホン" + } + } + } + }, + "gold" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Gold" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ゴルド" + } + } + } + }, + "headphone_description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "The technology with two noise sensors and two microphones on each ear cup detects ambient noise and sends the data to the HD noise minimization processor QN1. Using a new algorithm, the QN1 then processes and minimizes noise for different acoustic environments in real time. Together with a new Bluetooth Audio SoC (System on Chip), the system can adapt and deliver sound based on real-time feedback, ensuring optimal performance across a variety of conditions. This results in enhanced noise cancellation capabilities, making the device effective in environments like airplanes, busy streets, or open offices, while maintaining high-quality audio output. The combination of the QN1 processor and Bluetooth Audio SoC ensures not only active noise cancellation but also stable, high-resolution audio streaming. This provides listeners with a seamless experience, whether they are enjoying music, taking calls, or simply trying to block out distractions." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "2つのノイズセンサーと各イヤーカップに搭載された2つのマイクロフォンを使用して周囲のノイズを検出し、そのデータをHDノイズ低減プロセッサ「QN1」に送信します。新しいアルゴリズムを使用して、QN1はリアルタイムで異なる音響環境に応じてノイズを処理・低減します。さらに、新しいBluetoothオーディオSoC(システム・オン・チップ)と連携し、リアルタイムのフィードバックに基づいてサウンドを適応させ、さまざまな状況下で最適なパフォーマンスを提供します。 これにより、飛行機や賑やかな通り、オフィスなどの環境でも効果的なノイズキャンセリング機能が実現しながら、高音質なオーディオ出力を維持できます。QN1プロセッサとBluetoothオーディオSoCの組み合わせにより、アクティブなノイズキャンセリングだけでなく、安定した高解像度オーディオストリーミングも実現します。これにより、音楽を楽しむとき、通話をするとき、あるいは単に外部の雑音を遮断したいときでも、シームレスな体験が提供されます。" + } + } + } + }, + "headphone_name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Premium Wireless Headphone" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プレミアムワイヤレスヘッドフォン" + } + } + } + }, + "mic_description" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "This premium microphone is designed for professionals who demand crystal-clear audio quality for recording, streaming, and podcasting. Equipped with a large diaphragm condenser, it captures rich, detailed sound with exceptional accuracy. The microphone features multiple pickup patterns, including cardioid, bidirectional, and omnidirectional, making it versatile for various recording environments. Its durable, sleek design not only looks great but ensures long-lasting performance. With low self-noise and high sensitivity, this microphone is ideal for voice work, music production, or any setting where superior sound quality is essential. USB or XLR connectivity options make it compatible with a wide range of devices." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "このプレミアムマイクは、録音、ストリーミング、ポッドキャストにおいてクリアで高品質な音声を求めるプロフェッショナル向けに設計されています。ラージダイアフラムコンデンサーを搭載しており、豊かで細部まで正確な音を捉えます。カーディオイド、双方向、全指向性など複数の指向性パターンを備えているため、さまざまな録音環境に適応できる汎用性があります。耐久性がありスタイリッシュなデザインは、見た目の美しさだけでなく、長期間にわたって優れたパフォーマンスを提供します。低いセルフノイズと高い感度により、ナレーション、音楽制作、または高音質が求められるあらゆる状況に最適です。USBまたはXLR接続オプションにより、さまざまなデバイスに対応しています。" + } + } + } + }, + "mic_name" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Premium wireless Microphone" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "プレミアム ワイヤレス マイク" + } + } + } + }, + "model" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Model" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "モデル" + } + } + } + }, + "off_white" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off White" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "オフホワイト" + } + } + } + }, + "off-white" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Off White" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "オフホワイト" + } + } + } + }, + "order_confirmed" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Order confirmed" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注文確定" + } + } + } + }, + "order_failed_description" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Oops! Seems like there was an error placing your order because of a failed payment, please try again or report an issue. If any money is deducted then it will be refunded within 7 days" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "申し訳ありません!お支払いに失敗したため、ご注文の際にエラーが発生したようです。もう一度お試しいただくか、問題を報告してください。差し引かれた金額は 7 日以内に返金されます。" + } + } + } + }, + "order_failed_title" : { + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Order Failed" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "注文が失敗しました" + } + } + } + }, + "pink" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pink" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ピンク" + } + } + } + }, + "purple" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Purple" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "パプル" + } + } + } + }, + "retry_payment" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Retry payment" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "支払いを再試行する" + } + } + } + }, + "share" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Share" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "共有" + } + } + } + }, + "sound" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Sound" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "サウンド" + } + } + } + }, + "thank_you_for_your_order" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Thank you for your Order, it has been confirmed, Shipping Details will be shared on your registered email address soon" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ご注文ありがとうございます。ご注文は確認されました。発送の詳細は、ご登録のメールアドレスにすぐに送信されます。" + } + } + } + }, + "white" : { + "extractionState" : "manual", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "White" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "ホワイト" + } + } + } + } + }, + "version" : "1.0" +} \ No newline at end of file diff --git a/example-ios/example-ios/Preview Content/Preview Assets.xcassets/Contents.json b/example-ios/example-ios/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/example-ios/example-ios/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/example-ios/example-ios/iOSApp.swift b/example-ios/example-ios/iOSApp.swift new file mode 100644 index 0000000..0f259d6 --- /dev/null +++ b/example-ios/example-ios/iOSApp.swift @@ -0,0 +1,11 @@ +import KomojuSDK +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + FakeStoreView() + } + } +} diff --git a/example-ios/example-ios/remote/RemoteApiServices.swift b/example-ios/example-ios/remote/RemoteApiServices.swift new file mode 100644 index 0000000..4bd5d3f --- /dev/null +++ b/example-ios/example-ios/remote/RemoteApiServices.swift @@ -0,0 +1,81 @@ +import Foundation + +protocol RemoteApiServices { + func getPublishableKey(onSuccess: @escaping (String) -> Void, onFailure: @escaping (Error) -> Void) + func createSession( + amount: String, + currency: String, + language: String, + onSuccess: @escaping (String) -> Void, + onFailure: @escaping (Error) -> Void + ) +} + +class RemoteApiServicesImpl: RemoteApiServices { + func getPublishableKey(onSuccess: @escaping (String) -> Void, onFailure: @escaping (Error) -> Void) { + let serveKeyUrl = (Bundle.main.object(forInfoDictionaryKey: "BASE_URL") as? String)! + "/serve-key" + print(serveKeyUrl) + let url = URL(string: serveKeyUrl) + URLSession.shared.dataTask(with: url!) { data, _, error in + guard let data = data else { + DispatchQueue.main.async { + onFailure(error.unsafelyUnwrapped) + } + return + } + do { + let decodedData = try JSONDecoder().decode(PublishableKeyResponse.self, from: data) + DispatchQueue.main.async { + onSuccess(decodedData.publishableKey) + } + } catch { + DispatchQueue.main.async { + onFailure(error) + } + } + }.resume() + } + + func createSession( + amount: String, + currency: String, + language: String, + onSuccess: @escaping (String) -> Void, + onFailure: @escaping (Error) -> Void + ) { + let url = URL(string: (Bundle.main.object(forInfoDictionaryKey: "BASE_URL") as? String)! + "/create-session") + let body = [ + "amount": amount, + "currency": currency, + "language": language + ] + let headers = [ + "Content-Type": "application/json", + "Accept": "application/json" + ] + var request = URLRequest(url: url!) + request.httpMethod = "POST" + request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: []) + request.allHTTPHeaderFields = headers + URLSession.shared.dataTask(with: request) { data, _, error in + guard let data = data else { + onFailure(error.unsafelyUnwrapped) + return + } + do { + let decodedData = try JSONDecoder().decode(CreateSessionResponse.self, from: data) + DispatchQueue.main.async { + onSuccess(decodedData.sessionId) + } + } catch { + DispatchQueue.main.async { + onFailure(error) + } + } + }.resume() + } +} + +func remoteApiServices() -> RemoteApiServices { + return RemoteApiServicesImpl() +} diff --git a/example-ios/example-ios/remote/dtos/CreateSessionResponse.swift b/example-ios/example-ios/remote/dtos/CreateSessionResponse.swift new file mode 100644 index 0000000..1cae5e8 --- /dev/null +++ b/example-ios/example-ios/remote/dtos/CreateSessionResponse.swift @@ -0,0 +1,5 @@ +import Foundation + +struct CreateSessionResponse: Codable, Copyable { + let sessionId: String +} diff --git a/example-ios/example-ios/remote/dtos/PublishableKeyResponse.swift b/example-ios/example-ios/remote/dtos/PublishableKeyResponse.swift new file mode 100644 index 0000000..9a606a8 --- /dev/null +++ b/example-ios/example-ios/remote/dtos/PublishableKeyResponse.swift @@ -0,0 +1,5 @@ +import Foundation + +struct PublishableKeyResponse: Codable, Copyable { + let publishableKey: String +} diff --git a/example-ios/example-ios/screens/store/FakeItemDetailScreen.swift b/example-ios/example-ios/screens/store/FakeItemDetailScreen.swift new file mode 100644 index 0000000..893d776 --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeItemDetailScreen.swift @@ -0,0 +1,74 @@ +import SwiftUI + +struct FakeItemDetailScreen: View { + var item: Item + var onBack: () -> Void + var onFavorite: () -> Void + var onBuyClicked: () -> Void + + init(item: Item, onBack: @escaping () -> Void, onFavorite: @escaping () -> Void, onBuyClicked: @escaping () -> Void) { + self.item = item + self.onBack = onBack + self.onFavorite = onFavorite + self.onBuyClicked = onBuyClicked + } + + var body: some View { + ZStack(alignment: .bottom) { + ScrollView { + ZStack { + VStack(alignment: .leading) { + ZStack(alignment: .top) { + Image(item.imageResource) + .resizable() + .aspectRatio(1, contentMode: .fill) + .clipShape(.rect) + .padding(24) + HStack { + ZStack { + Image(systemName: "chevron.left") + .padding(12).onTapGesture { + onBack() + } + }.background(Color.gray.opacity(0.2)).clipShape(.circle) + Spacer() + ZStack { + if item.isFavorite { + Image(systemName: "heart.fill") + .foregroundColor(.red) + .padding(12).onTapGesture { + onFavorite() + } + } else { + Image(systemName: "heart") + .padding(12).onTapGesture { + onFavorite() + } + } + }.background(Color.gray.opacity(0.2)).clipShape(.circle) + } + } + Text("¥ " + item.price).font(.system(size: 18, weight: .bold)).foregroundStyle(.black) + Text(item.name).font(.system(size: 20, weight: .bold)).foregroundStyle(.black) + HStack(spacing: 0) { + Text(LocalizedStringKey("model")).font(.system(size: 14)).foregroundStyle(.gray) + Text(": " + item.model).font(.system(size: 14)).foregroundStyle(.gray) + Text(", ").font(.system(size: 14)).foregroundStyle(.gray) + Text(item.color).font(.system(size: 14)).foregroundStyle(.gray) + Spacer() + } + Spacer() + Text(item.description).font(.system(size: 12, weight: .regular)).foregroundStyle(.gray) + Spacer() + + }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 16)) + } + } + Button(action: { + onBuyClicked() + }) { + Text(LocalizedStringKey("buy_this_item")).padding(16) + }.frame(maxWidth: .infinity).foregroundStyle(.white).background(Color.green).cornerRadius(16).padding(16).clipped() + } + } +} diff --git a/example-ios/example-ios/screens/store/FakeStoreDisplayDataRepository.swift b/example-ios/example-ios/screens/store/FakeStoreDisplayDataRepository.swift new file mode 100644 index 0000000..ac168c8 --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreDisplayDataRepository.swift @@ -0,0 +1,86 @@ +import SwiftUI + +class FakeStoreDisplayDataRepository { + let items = [ + Item( + imageResource: .headphone1, + name: LocalizedStringKey("headphone_name"), + description: LocalizedStringKey("headphone_description"), + price: "199", + model: "HD1000XMG", + color: LocalizedStringKey("gold") + ), + Item( + imageResource: .headphone2, + name: LocalizedStringKey("headphone_name"), + description: LocalizedStringKey("headphone_description"), + price: "199", + model: "HD1000XMB", + color: LocalizedStringKey("black") + ), + Item( + imageResource: .headphone3, + name: LocalizedStringKey("headphone_name"), + description: LocalizedStringKey("headphone_description"), + price: "199", + model: "HD1000XMP", + color: LocalizedStringKey("purple") + ), + Item( + imageResource: .earphone1, + name: LocalizedStringKey("earphone_name"), + description: LocalizedStringKey("earphone_description"), + price: "99", + model: "E1000XMW", + color: LocalizedStringKey("white") + ), + Item( + imageResource: .earphone2, + name: LocalizedStringKey("earphone_name"), + description: LocalizedStringKey("earphone_description"), + price: "99", + model: "E1000XOW", + color: LocalizedStringKey("off-white") + ), + Item( + imageResource: .earphone3, + name: LocalizedStringKey("earphone_name"), + description: LocalizedStringKey("earphone_description"), + price: "99", + model: "E1000XMB", + color: LocalizedStringKey("black") + ), + Item( + imageResource: .earphone4, + name: LocalizedStringKey("earphone_name"), + description: LocalizedStringKey("earphone_description"), + price: "99", + model: "E1000XMB", + color: LocalizedStringKey("blue") + ), + Item( + imageResource: .mic1, + name: LocalizedStringKey("mic_name"), + description: LocalizedStringKey("mic_description"), + price: "49", + model: "M1000XMB", + color: LocalizedStringKey("black") + ), + Item( + imageResource: .mic2, + name: LocalizedStringKey("mic_name"), + description: LocalizedStringKey("mic_description"), + price: "49", + model: "M1000XMW", + color: LocalizedStringKey("white") + ), + Item( + imageResource: .mic3, + name: LocalizedStringKey("mic_name"), + description: LocalizedStringKey("mic_description"), + price: "49", + model: "M1000XMP", + color: LocalizedStringKey("purple") + ) + ] +} diff --git a/example-ios/example-ios/screens/store/FakeStoreFailedView.swift b/example-ios/example-ios/screens/store/FakeStoreFailedView.swift new file mode 100644 index 0000000..2206f5a --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreFailedView.swift @@ -0,0 +1,47 @@ +import SwiftUI + +struct FakeStoreFailedView: View { + var onBack: () -> Void + + init(onBack: @escaping () -> Void) { + self.onBack = onBack + } + var body: some View { + VStack { + HStack { + Button(action: { + onBack() + }, label: { + HStack(spacing: 8) { + Image(systemName: "arrow.left") + } + }) + .accentColor(.blue) + Spacer() + } + Spacer() + Image(systemName: "xmark.octagon.fill").resizable().foregroundColor(.red).frame(width: 120, height: 120).padding(16) + Text(LocalizedStringKey("order_failed_title")).fontWeight(.bold).font(.system(size: 24)).padding(16) + Text(LocalizedStringKey("order_failed_description")).font(.system(size: 16)).multilineTextAlignment(.center) + Spacer() + Button(action: { + onBack() + }) { + Text(LocalizedStringKey("retry_payment")).padding(16) + }.frame(maxWidth: .infinity).foregroundStyle(.white).background(Color.green).cornerRadius(16).padding(16).clipped() + + Button(action: { + onBack() + }) { + Text(LocalizedStringKey("close")).padding(16) + }.foregroundStyle(.blue) + Spacer() + }.padding(16) + } +} + +#Preview { + FakeStoreFailedView(onBack: { + + }) +} diff --git a/example-ios/example-ios/screens/store/FakeStoreSuccessView.swift b/example-ios/example-ios/screens/store/FakeStoreSuccessView.swift new file mode 100644 index 0000000..13b8865 --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreSuccessView.swift @@ -0,0 +1,47 @@ +import SwiftUI + +struct FakeStoreSuccessView: View { + var onBack: () -> Void + + init(onBack: @escaping () -> Void) { + self.onBack = onBack + } + var body: some View { + VStack { + HStack { + Button(action: { + onBack() + }, label: { + HStack(spacing: 8) { + Image(systemName: "arrow.left") + } + }) + .accentColor(.blue) + Spacer() + } + Spacer() + Image(systemName: "checkmark.seal.fill").resizable().foregroundColor(.green).frame(width: 120, height: 120).padding(16) + Text(LocalizedStringKey("order_confirmed")).fontWeight(.bold).font(.system(size: 24)).padding(16) + Text(LocalizedStringKey("thank_you_for_your_order")).font(.system(size: 16)).multilineTextAlignment(.center) + Spacer() + Button(action: { + onBack() + }) { + Text(LocalizedStringKey("continue_shopping")).padding(16) + }.frame(maxWidth: .infinity).foregroundStyle(.white).background(Color.green).cornerRadius(16).padding(16).clipped() + + Button(action: { + onBack() + }) { + Text(LocalizedStringKey("share")).padding(16) + }.foregroundStyle(.blue) + Spacer() + }.padding(16) + } +} + +#Preview { + FakeStoreSuccessView(onBack: { + + }) +} diff --git a/example-ios/example-ios/screens/store/FakeStoreUiState.swift b/example-ios/example-ios/screens/store/FakeStoreUiState.swift new file mode 100644 index 0000000..b53af1c --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreUiState.swift @@ -0,0 +1,19 @@ +import Foundation +import SwiftUI + +struct FakeStoreUiState { + var items: [Item] = [] + var isCreatingSession: Bool = false + var error: String? +} + +struct Item: Identifiable, Equatable { + var id: UUID = .init() + var imageResource: ImageResource + var name: LocalizedStringKey + var description: LocalizedStringKey + var price: String + var model: String + var color: LocalizedStringKey + var isFavorite: Bool = false +} diff --git a/example-ios/example-ios/screens/store/FakeStoreView.swift b/example-ios/example-ios/screens/store/FakeStoreView.swift new file mode 100644 index 0000000..ffac8e6 --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreView.swift @@ -0,0 +1,140 @@ +import KomojuSDK +import SwiftUI + +struct FakeStoreView: View { + @StateObject private var viewModel = FakeStoreViewModel() + + var body: some View { + ZStack { + if viewModel.uiState.items.isEmpty { + ZStack(alignment: .center) { + ProgressView().onAppear { + Task { + await viewModel.fetchData() + } + } + } + } else { + VStack(alignment: .leading) { + HStack(spacing: 0) { + Text(LocalizedStringKey("sound")).font(.system(size: 24)) + Text(LocalizedStringKey("bud")).font(.system(size: 24)).fontWeight(.bold).foregroundStyle(.green) + Spacer() + }.padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 10)) + Text(LocalizedStringKey("description")).font(.system(size: 12)).padding(EdgeInsets(top: 0, leading: 16, bottom: 0, trailing: 10)) + ScrollView { + LazyVGrid(columns: Array(repeating: GridItem(), count: 2)) { + ForEach(viewModel.uiState.items) { item in + FakeStoreItemView(item: item) { + viewModel.onFavIconClicked(item: item) + }.onTapGesture { + viewModel.onItemClicked(item: item) + } + } + } + }.padding(EdgeInsets(top: 8, leading: 8, bottom: 0, trailing: 8)) + Spacer() + } + } + ZStack(alignment: .leading) { + if viewModel.itemDetail != nil { + FakeItemDetailScreen(item: viewModel.itemDetail!, onBack: { + viewModel.closeItemDetail() + }, onFavorite: { + viewModel.onFavIconClicked(item: viewModel.itemDetail!) + }, onBuyClicked: { + viewModel.createSession(item: viewModel.itemDetail!) + }) + } + }.background(Color.white) + .transition(.move(edge: .trailing)) + .animation(.easeInOut, value: viewModel.itemDetail) + + ZStack(alignment: .leading) { + if viewModel.paymentResult != nil { + if viewModel.paymentResult!.isSuccess { + FakeStoreSuccessView(onBack: { + viewModel.clearResult() + }) + } else { + FakeStoreFailedView(onBack: { + viewModel.clearResult() + }) + } + } + }.background(Color.white) + .transition(.move(edge: .trailing)) + .animation(.easeInOut, value: viewModel.paymentResult) + + if viewModel.uiState.isCreatingSession { + ZStack { + ProgressView().padding(32).background(Color.gray.opacity(0.5).blur(radius: 2)).cornerRadius(16) + } + } + if viewModel.komojuPaymentConfiguration?.canProcessPayment() ?? false { + KomojuPaymentView(configuration: viewModel.komojuPaymentConfiguration!) { result in + viewModel.onPaymentResultReceived(result: result) + debugPrint("Payment Result -> \(result.isSuccess)") + } + } + } + } +} + +struct FakeStoreItemView: View { + var item: Item + var onClick: () -> Void + + init(item: Item, onClick: @escaping () -> Void) { + self.item = item + self.onClick = onClick + } + + var body: some View { + ZStack(alignment: .topTrailing) { + VStack(alignment: .leading) { + Image(item.imageResource) + .resizable() + .aspectRatio(contentMode: .fill) + .frame( + minWidth: 0, + maxWidth: .infinity, + minHeight: 0, + maxHeight: .infinity + ) + .aspectRatio(0.77, contentMode: ContentMode.fit) + .clipShape(.rect) + .padding(24) + + Text("¥ " + item.price).font(.system(size: 18, weight: .bold)).foregroundStyle(.black) + Text(item.name).font(.system(size: 14, weight: .medium)).foregroundStyle(.black) + HStack(spacing: 0) { + Text(LocalizedStringKey("model")).font(.system(size: 12)).foregroundStyle(.gray) + Text(": " + item.model).font(.system(size: 12)).foregroundStyle(.gray) + Text(", ").font(.system(size: 12)).foregroundStyle(.gray) + Text(item.color).font(.system(size: 12)).foregroundStyle(.gray) + Spacer() + } + } + ZStack { + if item.isFavorite { + Image(systemName: "heart.fill") + .foregroundColor(.red) + .padding(12).onTapGesture { + onClick() + } + } else { + Image(systemName: "heart") + .padding(12).onTapGesture { + onClick() + } + } + }.background(Color.gray.opacity(0.2)).clipShape(.circle) + + }.padding(8) + } +} + +#Preview { + FakeStoreView() +} diff --git a/example-ios/example-ios/screens/store/FakeStoreViewModel.swift b/example-ios/example-ios/screens/store/FakeStoreViewModel.swift new file mode 100644 index 0000000..0c314ac --- /dev/null +++ b/example-ios/example-ios/screens/store/FakeStoreViewModel.swift @@ -0,0 +1,92 @@ +import Foundation +import KomojuSDK +import SwiftUI + +class FakeStoreViewModel: ObservableObject { + let apiServices = remoteApiServices() + var publisherKey: String? + @Published var uiState: FakeStoreUiState = .init() + @Published var itemDetail: Item? + @Published var komojuPaymentConfiguration: KomojuIosSDK.Configuration? + @Published var paymentResult: KomojuIosSDK.PaymentResult? + + func fetchData() async { + apiServices.getPublishableKey { key in + self.uiState.items = FakeStoreDisplayDataRepository().items + self.publisherKey = key + debugPrint(key) + } onFailure: { error in + self.uiState.error = error.localizedDescription + debugPrint(error) + } + } + + func onFavIconClicked(item: Item) { + uiState.items = uiState.items.map { it in + if it.id == item.id { + return Item( + id: it.id, + imageResource: it.imageResource, + name: it.name, + description: it.description, + price: it.price, + model: it.model, + color: it.color, + isFavorite: !it.isFavorite + ) + } else { + return it + } + } + if itemDetail != nil { + itemDetail?.isFavorite.toggle() + } + } + + func onItemClicked(item: Item) { + itemDetail = item + } + + func closeItemDetail() { + itemDetail = nil + } + + func createSession(item: Item) { + uiState.isCreatingSession = true + Task { + await createSessionAsync(item: item) + } + } + + private func createSessionAsync(item: Item) async { + apiServices.createSession(amount: item.price, currency: Currency.JPY.currencyCode, language: Language.english.useSystemLanguage.languageCode, onSuccess: { sessionId in + self.uiState.isCreatingSession = false + self.komojuPaymentConfiguration = KomojuIosSDK.Configuration.Builder( + publishableKey: self.publisherKey!, + sessionId: sessionId + ).setConfigurableTheme(KomojuIosSDK.ConfigurableTheme( + primaryColor: Color.green, + primaryContentColor: Color.white, + loaderColor: Color.green, + primaryButtonCornerRadius: 16 + )) + .setInlinedProcessing(true) + .setLanguage(Language.english.useSystemLanguage) + .setCurrency(Currency.JPY) + .setDebugMode(true) + .build() + }, onFailure: { error in + self.uiState.isCreatingSession = false + debugPrint(error) + }) + } + + func onPaymentResultReceived(result: KomojuIosSDK.PaymentResult) { + komojuPaymentConfiguration = nil + paymentResult = result + } + + func clearResult() { + paymentResult = nil + } +} diff --git a/komoju-ios-sdk/.gitignore b/komoju-ios-sdk/.gitignore new file mode 100644 index 0000000..d239d6a --- /dev/null +++ b/komoju-ios-sdk/.gitignore @@ -0,0 +1 @@ +xcframework/** \ No newline at end of file diff --git a/komoju-ios-sdk/.swiftlint.yml b/komoju-ios-sdk/.swiftlint.yml new file mode 100644 index 0000000..6f7f870 --- /dev/null +++ b/komoju-ios-sdk/.swiftlint.yml @@ -0,0 +1,7 @@ +strict: true +line_length: 180 + +nesting: + type_level: + warning: 6 + error: 6 \ No newline at end of file diff --git a/komoju-ios-sdk/KomojuSDK.xcodeproj/project.pbxproj b/komoju-ios-sdk/KomojuSDK.xcodeproj/project.pbxproj new file mode 100644 index 0000000..904e63e --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK.xcodeproj/project.pbxproj @@ -0,0 +1,427 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 77; + objects = { + +/* Begin PBXBuildFile section */ + 7C0966E52CE452F300D5AE76 /* Language.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0966E42CE452F000D5AE76 /* Language.swift */; }; + 7C0966E62CE452F300D5AE76 /* Currency.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0966E32CE452EE00D5AE76 /* Currency.swift */; }; + 7C0966E72CE452F300D5AE76 /* KomojuPaymentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0966E22CE452DE00D5AE76 /* KomojuPaymentView.swift */; }; + 7C0966E82CE452F300D5AE76 /* KomojuIosSDK.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C0966E12CE452DC00D5AE76 /* KomojuIosSDK.swift */; }; + 7CCCA0B72CCA25E600615A83 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7CCCA0B52CCA25E600615A83 /* Preview Assets.xcassets */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 7C0966DC2CE452D900D5AE76 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 7C0966E12CE452DC00D5AE76 /* KomojuIosSDK.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KomojuIosSDK.swift; sourceTree = ""; }; + 7C0966E22CE452DE00D5AE76 /* KomojuPaymentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KomojuPaymentView.swift; sourceTree = ""; }; + 7C0966E32CE452EE00D5AE76 /* Currency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Currency.swift; sourceTree = ""; }; + 7C0966E42CE452F000D5AE76 /* Language.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Language.swift; sourceTree = ""; }; + 7CCCA0A82CCA23C600615A83 /* KomojuSDK.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = KomojuSDK.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 7CCCA0B52CCA25E600615A83 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7CCCA0A52CCA23C600615A83 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7C0966DB2CE452D900D5AE76 /* types */ = { + isa = PBXGroup; + children = ( + 7C0966E42CE452F000D5AE76 /* Language.swift */, + 7C0966E32CE452EE00D5AE76 /* Currency.swift */, + ); + path = types; + sourceTree = ""; + }; + 7C0966E02CE452D900D5AE76 /* KomojuSDK */ = { + isa = PBXGroup; + children = ( + 7C0966DB2CE452D900D5AE76 /* types */, + 7C0966DC2CE452D900D5AE76 /* Assets.xcassets */, + 7C0966E22CE452DE00D5AE76 /* KomojuPaymentView.swift */, + 7C0966E12CE452DC00D5AE76 /* KomojuIosSDK.swift */, + ); + path = KomojuSDK; + sourceTree = ""; + }; + 7CCCA09E2CCA23C600615A83 = { + isa = PBXGroup; + children = ( + 7C0966E02CE452D900D5AE76 /* KomojuSDK */, + 7CCCA0B62CCA25E600615A83 /* Preview Content */, + 7CCCA0A92CCA23C600615A83 /* Products */, + ); + sourceTree = ""; + }; + 7CCCA0A92CCA23C600615A83 /* Products */ = { + isa = PBXGroup; + children = ( + 7CCCA0A82CCA23C600615A83 /* KomojuSDK.framework */, + ); + name = Products; + sourceTree = ""; + }; + 7CCCA0B62CCA25E600615A83 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 7CCCA0B52CCA25E600615A83 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 7CCCA0A32CCA23C600615A83 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 7CCCA0A72CCA23C600615A83 /* KomojuSDK */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7CCCA0AE2CCA23C600615A83 /* Build configuration list for PBXNativeTarget "KomojuSDK" */; + buildPhases = ( + 7CCCA0B82CCA27B900615A83 /* Compile Kotlin MultiPlatform Framework */, + 7CCCA0A32CCA23C600615A83 /* Headers */, + 7CCCA0A42CCA23C600615A83 /* Sources */, + 7CCCA0A52CCA23C600615A83 /* Frameworks */, + 7CCCA0A62CCA23C600615A83 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = KomojuSDK; + packageProductDependencies = ( + ); + productName = ios; + productReference = 7CCCA0A82CCA23C600615A83 /* KomojuSDK.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7CCCA09F2CCA23C600615A83 /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastUpgradeCheck = 1600; + TargetAttributes = { + 7CCCA0A72CCA23C600615A83 = { + CreatedOnToolsVersion = 16.0; + LastSwiftMigration = 1600; + }; + }; + }; + buildConfigurationList = 7CCCA0A22CCA23C600615A83 /* Build configuration list for PBXProject "KomojuSDK" */; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ja, + ); + mainGroup = 7CCCA09E2CCA23C600615A83; + minimizedProjectReferenceProxies = 1; + preferredProjectObjectVersion = 77; + productRefGroup = 7CCCA0A92CCA23C600615A83 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7CCCA0A72CCA23C600615A83 /* KomojuSDK */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7CCCA0A62CCA23C600615A83 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7CCCA0B72CCA25E600615A83 /* Preview Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 7CCCA0B82CCA27B900615A83 /* Compile Kotlin MultiPlatform Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin MultiPlatform Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :shared:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7CCCA0A42CCA23C600615A83 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 7C0966E52CE452F300D5AE76 /* Language.swift in Sources */, + 7C0966E62CE452F300D5AE76 /* Currency.swift in Sources */, + 7C0966E72CE452F300D5AE76 /* KomojuPaymentView.swift in Sources */, + 7C0966E82CE452F300D5AE76 /* KomojuIosSDK.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7CCCA0AF2CCA23C600615A83 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_ASSET_PATHS = "Preview\\ Content/Preview\\ Assets.xcassets"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Degica inc"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.komoju.ios.sdk; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7CCCA0B02CCA23C600615A83 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUILD_LIBRARY_FOR_DISTRIBUTION = YES; + CLANG_ENABLE_MODULES = YES; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DEVELOPMENT_ASSET_PATHS = "Preview\\ Content/Preview\\ Assets.xcassets"; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_NSHumanReadableCopyright = "Degica inc"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 15.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 1.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20"; + PRODUCT_BUNDLE_IDENTIFIER = com.komoju.ios.sdk; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_INSTALL_OBJC_HEADER = NO; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 7CCCA0B12CCA23C600615A83 /* 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; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.0; + 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"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 7CCCA0B22CCA23C600615A83 /* 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; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; + 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.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7CCCA0A22CCA23C600615A83 /* Build configuration list for PBXProject "KomojuSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7CCCA0B12CCA23C600615A83 /* Debug */, + 7CCCA0B22CCA23C600615A83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7CCCA0AE2CCA23C600615A83 /* Build configuration list for PBXNativeTarget "KomojuSDK" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7CCCA0AF2CCA23C600615A83 /* Debug */, + 7CCCA0B02CCA23C600615A83 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7CCCA09F2CCA23C600615A83 /* Project object */; +} diff --git a/komoju-ios-sdk/KomojuSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/komoju-ios-sdk/KomojuSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/komoju-ios-sdk/KomojuSDK/Assets.xcassets/AccentColor.colorset/Contents.json b/komoju-ios-sdk/KomojuSDK/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..ee7e3ca --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/komoju-ios-sdk/KomojuSDK/Assets.xcassets/Contents.json b/komoju-ios-sdk/KomojuSDK/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/komoju-ios-sdk/KomojuSDK/KomojuIosSDK.swift b/komoju-ios-sdk/KomojuSDK/KomojuIosSDK.swift new file mode 100644 index 0000000..94c6150 --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/KomojuIosSDK.swift @@ -0,0 +1,171 @@ +import Foundation +import komojuShared +import SwiftUI + +public class KomojuIosSDK { + public struct Configuration { + let language: Language + let currency: Currency + let publishableKey: String + let isDebugMode: Bool + let sessionId: String + let redirectUrl: String + let appScheme: String + let configurableTheme: ConfigurableTheme + let inlinedProcessing: Bool + + public class Builder { + private var publishableKey: String + private var sessionId: String + private var language: Language = .japanese // Default language is Japanese. + private var currency: Currency = .JPY // Default currency is Japanese Yen. + private var isDebugMode: Bool = false // Debug mode is off by default. + private var configurableTheme: ConfigurableTheme = ConfigurableThemeCompanion().default_.toIosTheme() + private var inlinedProcessing: Bool = false + public init(publishableKey: String, sessionId: String) { + self.publishableKey = publishableKey + self.sessionId = sessionId + } + + public func setLanguage(_ language: Language) -> Builder { + self.language = language + return self + } + + public func setCurrency(_ currency: Currency) -> Builder { + self.currency = currency + return self + } + + public func setDebugMode(_ isDebugMode: Bool) -> Builder { + self.isDebugMode = isDebugMode + return self + } + + public func setConfigurableTheme(_ configurableTheme: ConfigurableTheme) -> Builder { + self.configurableTheme = configurableTheme + return self + } + + public func setInlinedProcessing(_ inlinedProcessing: Bool) -> Builder { + self.inlinedProcessing = inlinedProcessing + return self + } + + public func build() -> Configuration { + .init(language: language, + currency: currency, + publishableKey: publishableKey, + isDebugMode: isDebugMode, + sessionId: sessionId, + redirectUrl: "komapp://", + appScheme: "komapp", + configurableTheme: configurableTheme, + inlinedProcessing: inlinedProcessing) + } + } + } + + public struct ConfigurableTheme { + public init(primaryColor: Color, primaryContentColor: Color, loaderColor: Color, primaryButtonCornerRadius: Int) { + self.primaryColor = primaryColor + self.primaryContentColor = primaryContentColor + self.loaderColor = loaderColor + self.primaryButtonCornerRadius = primaryButtonCornerRadius + } + + let primaryColor: Color + let primaryContentColor: Color + let loaderColor: Color + let primaryButtonCornerRadius: Int + } + + public struct PaymentResult: Codable, Equatable { + public let isSuccess: Bool + } +} + +extension KomojuIosSDK.PaymentResult { + func fromMobilePaymentResult(_: KomojuMobileSDKPaymentResult) -> KomojuIosSDK.PaymentResult { + .init(isSuccess: isSuccess) + } +} + +public extension KomojuIosSDK.Configuration { + internal func toMobileConfiguration() -> KomojuMobileSDKConfiguration { + .init(language: language.languageCode, + currency: currency.currencyCode, + publishableKey: publishableKey, + isDebugMode: isDebugMode, + sessionId: sessionId, + redirectURL: redirectUrl, + appScheme: appScheme, + configurableTheme: configurableTheme.toMobileConfigurableTheme(), + inlinedProcessing: inlinedProcessing) + } + + func canProcessPayment() -> Bool { + return toMobileConfiguration().canProcessPayment() + } +} + +private extension KomojuIosSDK.ConfigurableTheme { + func toMobileConfigurableTheme() -> DefaultConfigurableTheme { + .init(primaryColor: UIColor(primaryColor).toArgb(), + primaryContentColor: UIColor(primaryContentColor).toArgb(), + primaryShapeCornerRadiusInDp: Int32(primaryButtonCornerRadius), + loaderColor: UIColor(loaderColor).toArgb()) + } +} + +private extension DefaultConfigurableTheme { + func toIosTheme() -> KomojuIosSDK.ConfigurableTheme { + return KomojuIosSDK.ConfigurableTheme( + primaryColor: Color(hex: UInt64(primaryColor)), + primaryContentColor: Color(hex: UInt64(primaryContentColor)), + loaderColor: Color(hex: UInt64(loaderColor)), + primaryButtonCornerRadius: Int(primaryShapeCornerRadiusInDp) + ) + } +} + +private func hexColor(_ hex: UInt64) -> UIColor { + UIColor(red: .init((hex & 0xFF0000) >> 16) / 255, + green: .init((hex & 0xFF00) >> 8) / 255, + blue: .init(hex & 0xFF) / 255, + alpha: 1) +} + +extension Color { + init(hex: UInt64, alpha: Double = 1) { + self.init( + .sRGB, + red: Double((hex >> 16) & 0xFF) / 255, + green: Double((hex >> 08) & 0xFF) / 255, + blue: Double((hex >> 00) & 0xFF) / 255, + opacity: alpha + ) + } +} + +private extension UIColor { + func toArgb() -> Int64 { + var fRed: CGFloat = 0 + var fGreen: CGFloat = 0 + var fBlue: CGFloat = 0 + var fAlpha: CGFloat = 0 + if getRed(&fRed, green: &fGreen, blue: &fBlue, alpha: &fAlpha) { + let iRed = Int64(fRed * 255.0) + let iGreen = Int64(fGreen * 255.0) + let iBlue = Int64(fBlue * 255.0) + let iAlpha = Int64(fAlpha * 255.0) + + // (Bits 24-31 are alpha, 16-23 are red, 8-15 are green, 0-7 are blue). + let rgb = (iAlpha << 24) + (iRed << 16) + (iGreen << 8) + iBlue + return rgb + } else { + debugPrint("Cannot convert UIColor to Int") + return 0 + } + } +} diff --git a/komoju-ios-sdk/KomojuSDK/KomojuPaymentView.swift b/komoju-ios-sdk/KomojuSDK/KomojuPaymentView.swift new file mode 100644 index 0000000..7ba9bc5 --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/KomojuPaymentView.swift @@ -0,0 +1,48 @@ +import komojuShared +import SwiftUI +import UIKit + +public struct KomojuPaymentView: View { + @State var isVisible: Bool = false + @State private var sheetHeight: CGFloat = .zero + @State private var deeplinkUrl: String? + + let configuration: KomojuIosSDK.Configuration + let onDismiss: (KomojuIosSDK.PaymentResult) -> Void + public init(configuration: KomojuIosSDK.Configuration, onDismiss: @escaping (KomojuIosSDK.PaymentResult) -> Void) { + self.configuration = configuration + self.onDismiss = onDismiss + } + + public var body: some View { + Spacer().onAppear { + isVisible = true + } + .sheet(isPresented: $isVisible, onDismiss: { + isVisible = false + }, content: { + ComposeView( + configuration: configuration.toMobileConfiguration(), + onDismiss: onDismiss, + deepLinkUrl: deeplinkUrl) + .ignoresSafeArea(.keyboard) + }).onOpenURL(perform: onNewDeeplink) + } + + private func onNewDeeplink(_ url: URL) { + deeplinkUrl = url.absoluteString + } +} + +private struct ComposeView: UIViewControllerRepresentable { + let configuration: KomojuMobileSDKConfiguration + let onDismiss: (KomojuIosSDK.PaymentResult) -> Void + let deepLinkUrl: String? + func makeUIViewController(context _: Context) -> UIViewController { + MainViewControllerKt.MainViewController(configuration: configuration) { result in + self.onDismiss(KomojuIosSDK.PaymentResult(isSuccess: result.isSuccessFul)) + } + } + + func updateUIViewController(_: UIViewController, context _: Context) {} +} diff --git a/komoju-ios-sdk/KomojuSDK/types/Currency.swift b/komoju-ios-sdk/KomojuSDK/types/Currency.swift new file mode 100644 index 0000000..35186a2 --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/types/Currency.swift @@ -0,0 +1,21 @@ +import Foundation + +public enum Currency { + case JPY + case USD + + public var currencyCode: String { + switch self { + case .JPY: return "JPY" + case .USD: return "USD" + } + } + + func parse(from string: String) -> Currency? { + switch string.lowercased() { + case "jpy": return .JPY + case "usd": return .USD + default: return nil + } + } +} diff --git a/komoju-ios-sdk/KomojuSDK/types/Language.swift b/komoju-ios-sdk/KomojuSDK/types/Language.swift new file mode 100644 index 0000000..eaa10a6 --- /dev/null +++ b/komoju-ios-sdk/KomojuSDK/types/Language.swift @@ -0,0 +1,31 @@ +import Foundation + +public enum Language { + case english + case japanese + + public var languageCode: String { + switch self { + case .english: return "en" + case .japanese: return "ja" + } + } +} + +public extension Language { + var useSystemLanguage: Language { + return if Locale.current.languageCode == "en" { + .english + } else { + self + } + } + + func parse(from string: String) -> Language { + return switch string { + case "en": .english + case "ja": .japanese + default: useSystemLanguage + } + } +} diff --git a/komoju-ios-sdk/LICENSE b/komoju-ios-sdk/LICENSE new file mode 100644 index 0000000..be57f08 --- /dev/null +++ b/komoju-ios-sdk/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Degica + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/komoju-ios-sdk/Preview Content/Preview Assets.xcassets/Contents.json b/komoju-ios-sdk/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..4aa7c53 --- /dev/null +++ b/komoju-ios-sdk/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file