Open23

🧑🏻‍💻 個人開発メモ

donchan922donchan922

SwiftUIのローカライズ(多言語)対応

https://developer.apple.com/documentation/xcode/localizing-and-varying-text-with-a-string-catalog

古い情報↓

アプリ表示はこちら。
https://zenn.dev/sakutech/articles/swiftui-localization

InfoPlistの多言語対応は、各言語用のInfoPliststringsファイルを作ればOK。

InfoPlist.strings
"CFBundleDisplayName" = "Password Gen";
"NSUserTrackingUsageDescription" = "This identifier will be used to deliver personalized ads to you.";
donchan922donchan922

Xcode Tips

キーボードショートカット

  • シミュレータのライト / ダークモードの切り替え: command + shift + A
donchan922donchan922

SwiftでRevenueCatを使うとき、StoreKit 2を利用するオプションは使わないほうが安定しそう。

// 🙆‍♂️ 挙動安定
Purchases.configure(withAPIKey: <public_apple_api_key>)

// 🙅‍♂️ 挙動不安定
Purchases.configure(
    with: Configuration.Builder(withAPIKey: <public_apple_api_key>)
        .with(usesStoreKit2IfAvailable: true)
        .build()
)

詳細な原因まではわからなかったが、Sandbox環境でまれにエラーが発生した。

[Purchases] - WARN: 🍎⚠️ StoreKit 2 purchase took longer than expected (21.13 seconds)
...
[Purchases] - ERROR: 😿‼️ The receipt is not valid.
...
[Purchases] - ERROR: 💰 Product purchase for 'xxx' failed with error: Error Domain=RevenueCat.ErrorCode Code=8 "The receipt is not valid." UserInfo={readable_error_code=INVALID_RECEIPT, NSLocalizedDescription=The receipt is not valid., source_file=RevenueCat/HTTPClient.swift:379, rc_backend_error_code=7103, source_function=convertUnsuccessfulResponseToError(), rc_response_status_code=400, NSUnderlyingError=0x280c68720 {Error Domain=RevenueCat.BackendErrorCode Code=7103 "The receipt is not valid." UserInfo={rc_backend_error_code=7103, NSLocalizedDescription=The receipt is not valid.}}}
loading end

https://www.revenuecat.com/docs/configuring-sdk

donchan922donchan922

iOSでサブスク機能実装したアプリがAppleの審査でリジェクトされた。理由は以下のとおり。利用規約を用意する必要があるとのこと。

Guideline 3.1.2 - Business - Payments - Subscriptions

We noticed that your app did not meet all the terms and conditions for auto-renewing subscriptions, as specified in Schedule 2, section 3.8(b) of the Paid Applications agreement.

We were unable to find the following required item(s) in your app's metadata:

– A functional link to the Terms of Use (EULA)

Next Steps

To resolve this issue, please add this missing information. If the above information is present, please reply to this message in App Store Connect to provide details on where to locate it.

If you are using the standard Apple Terms of Use (EULA), you will need to include a link to the Terms of Use in your App Description. If you are using a custom EULA, add it in App Store Connect.

対応方法は以下のとおり。
https://help.apple.com/app-store-connect/#/dev7fa2fff96
https://starhoshi.hatenablog.com/entry/2023/01/16/085641

donchan922donchan922

iOSの場合、アプリにはじめてサブスクリプションを導入する際、本番環境でサブスクリプションが機能するまでAppleの承認後24時間ほどかかるみたい。自分のアプリでサブスクリプションが機能していないのはこのせいかも。

2023/2/15追記
サブスクリプション承認後に、27時間後 or アプリ空リリースのための審査提出(審査待ち)で本番環境で機能しはじめた。どちらが効いたのかはわからなかった。

https://www.revenuecat.com/docs/launch-checklist#5-prepare-release

donchan922donchan922

Info.plistを「Source Code」で開いて変更するとタブやスペースが混在することがある。その場合、「Property List」で開き適当なKeyを追加→削除するとタブに統一される。

donchan922donchan922

SwiftDataでCloudKitを使うときのモデル設計に関する注意点

  • @Attribute(.unique)は利用できない
  • @Relationshipの関係にあるすべてのフィールドはオプショナル型にしないといけない
  • すべてのフィールドは初期値を設定するかオプショナル型にしないといけない

https://developer.apple.com/documentation/swiftdata/syncing-model-data-across-a-persons-devices
https://zenn.dev/himara2/articles/88d4d873d4078d
https://alexanderlogan.co.uk/blog/wwdc23/08-cloudkit-swift-data

donchan922donchan922

Flutterでのリリース

# iOS
$ flutter build ipa

# Android
$ flutter build appbundle

# デバッグシンボルをアップロード用に加工する
$ cd build/app/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib
$ zip -r Archive.zip arm64-v8a armeabi-v7a x86_64

# Play Storeで手動アップロードしやすい場所にコピーしてくる
$ cp build/app/outputs/bundle/release/app-release.aab ~/Documents
$ cp build/app/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib/Archive.zip ~/Documents
donchan922donchan922

Androidでアプリ内課金を有効化するときは、AndroidManifest.xmlに設定を追加する必要がある。追加しない場合だと、Google Play Consoleの「アプリ内アイテム」でアイテムを作成するメニューが出ない。追加後、クローズド テストでトラックを作成するとメニューが表示される。

AndroidManifest.xml
<uses-permission android:name="com.android.vending.BILLING" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- ここ -->
    <uses-permission android:name="com.android.vending.BILLING" />

    <application
        ...
    </application>

</manifest>

https://support.google.com/googleplay/android-developer/answer/1153481

課金パッケージの依存性を追加しないとPlay Store上でApp Bundleをアップロードした後に「このアプリで現在使用している Play Billing Library のバージョンは AIDL です。Google Play で最新の収益化機能を利用するには、バージョン 6.0.1 以上に更新する必要があります。」エラーが発生する。

build.gradle.kts
implementation("com.revenuecat.purchases:purchases:8.15.1")
donchan922donchan922

ネイティブのAndroidアプリをビルドするときはAndroid Studioのメニューの「Build」→「Generate Signed App Bundle or APK...」から行う。

アップロード鍵とキーストアはAndroid Studioから以下を参考に作成する。例えば、プロジェクト直下のkeystores/upload-keystore.jksとして保存する。⚠️ git管理しないこと
app/release/app-release.aab (App Bundle)が生成される。
https://developer.android.com/studio/publish/app-signing?hl=ja#sign-apk

donchan922donchan922

Jetpack Composeでよく使うプロジェクト構成

app/
├── src/
│   ├── main/
│   │   ├── java/com/donchan922/myapp/
│   │   │   ├── data/
│   │   │   │   ├── local/
│   │   │   │   │   ├── dao/
│   │   │   │   │   ├── entity/
│   │   │   │   │   └── AppDatabase.kt
│   │   │   │   ├── model/
│   │   │   │   ├── preferences/
│   │   │   │   │   └── AppPreferences.kt
│   │   │   │   └── repository/
│   │   │   ├── di/
│   │   │   │   └── AppContainer.kt
│   │   │   ├── ui/
│   │   │   │   ├── components/
│   │   │   │   ├── screens/
│   │   │   │   │   ├── home/
│   │   │   │   │   │   ├── HomeScreen.kt
│   │   │   │   │   │   └── HomeViewModel.kt
│   │   │   │   │   └── AppViewModelProvider.kt
│   │   │   │   └── theme/
│   │   │   ├── utils/
│   │   │   └── MainActivity.kt
│   │   └── res/
│   └── test/
donchan922donchan922

Androidの多言語化対応

デフォルト言語を英語、そこに日本語を追加する場合

  1. res/values-ja/strings.xml を作成する
res/values-ja/strings.xml
<resources>
    <string name="app_name">アプリ名</string>
</resources>
  1. 各ファイルから読み込む
// Composeで使用する場合
Text(stringResource(id = R.string.app_name))
or
// それ以外の場合
R.string.app_name

https://developer.android.com/guide/topics/resources/localization?hl=ja

donchan922donchan922

Androidリリース時の警告の対応方法

この App Bundle に関連付けられている難読化解除ファイルはありません。難読化コード(R8 / ProGuard)を使用している場合、難読化解除ファイルをアップロードすると、クラッシュと ANR をより簡単に分析、デバッグできるようになります。R8 / ProGuard の使用は、アプリサイズの縮小につながります。

https://developer.android.com/studio/build/shrink-code#decode-stack-trace

build.gradle.kts
    buildTypes {
        release {
-           isMinifyEnabled = false
+           isMinifyEnabled = true
        }
    }

app/build/outputs/mapping/release/mapping.txt をアップロードすればOK

この App Bundle にはネイティブ コードが含まれ、デバッグ シンボルがアップロードされていません。クラッシュや ANR を簡単に分析、デバッグできるよう、シンボル ファイルをアップロードすることをおすすめします。

https://developer.android.com/build/shrink-code?hl=ja#native-crash-support

↑の手順だとnative-debug-symbols.zipが出力されなかったので自分で生成する。native-debug-symbols.zip をアップロードすればOK

cd app/build/intermediates/merged_native_libs/release/mergeReleaseNativeLibs/out/lib
zip -r native-debug-symbols.zip arm64-v8a armeabi-v7a x86_64
donchan922donchan922

Androidでマテリアルアイコンを追加したい場合は、resディレクトリ右クリック→New→Vector Asset→Clip artから対象のアイコンを選択

baseline_workspace_premium_24のアイコンを選択した場合
Icon(
    painter = painterResource(id = R.drawable.baseline_workspace_premium_24),
    contentDescription = null,
    tint = MaterialTheme.colorScheme.primary,
    modifier = Modifier.padding(end = 8.dp)
)