- 投稿日:2021-01-24T23:20:05+09:00
ちゃんと紐づけてるのにunrecognized selector sent to instanceエラーが出る時の対処方法
このエラーに数日悩まされたので備忘録として残します。
Bar Button Itemタップ時の処理を実装していたところ、
以前までは正常に実装できていたものが、「unrecognized selector sent to instance」のエラーで前に進めなくなった。調べてみると、いくつか要因があるとのこと
- identity inspectorのClassの設定が間違っている
- Inherit Module From Targetにチェックが入っていない(自分はこれが原因だった)
- タップ時のアクションを示す関数にて、引数(例:_ sender: UIBarButtonItem)が設定されていない
(2が原因の場合、Inherit Module From Targetにチェックを入れるか、もしくは、Moduleに手打ちすればよいとのこと)
参考になればいいな
- 投稿日:2021-01-24T23:02:01+09:00
FlutterでiOSアプリの名前を日本語で設定する簡単な方法
ウェブで調べるとXcodeを使っての情報と日本語では簡単には設定できない旨の情報ばかりで、私のようにWindows→Codemagicで開発をしている物には参考情報が得られませんでした。
これを友人にぐちったところあっさり解決策が見つかったので、メモを兼ねて記載します。
プロジェクトフォルダ\ios\Runner\Info.plistを編集する点は同じです。
一番多く見つかる情報
先に注意を兼ねて半分間違いの情報です。
<key>CFBundleName</key>
<string>MyApp</string>
CFBundleNameのstringにアプリ名を入力します。
ただしこれでは日本語の名前は設定できません。正解の情報
<key>CFBundleDisplayName</key> <string>私のアプリ</string>とします。
CFBundleDisplayNameはプロジェクト作成時には記載がありませんので追記する必要があります。まとめ
Xcodeでローカライズが必要などの情報ばかりでしたが、こんな簡単に指定ができると知って拍子抜けでした。
今後クロスプラットフォーム開発が売りなので、Androidのアプリ名と合わせてプロジェクト作成のときなどに簡単に設定できるようにして欲しい物です。
- 投稿日:2021-01-24T21:14:43+09:00
あなたのiPhoneを一瞬でアイプラフォンにしちゃおう!
動機
アイカツプラネット!始まりましたね!
筐体ではアイカツオンパレード!以前と同様に2次元バーコードを読み込ませる方式ですが、
テレビシリーズではアイプラフォンと呼ばれる限りなくiPhoneに近いデバイスにセットしています。このままでは筐体で遊ぶときに混乱してしまうので、iPhoneをアイプラフォンにしてしまいましょう。
用意するもの
iPhone (XS以降、iOS13.1)
NFCタグ(シールになってるやつがいいです)
マイページ登録済みのアイドルライセンスオートメーションの設定
前項でほとんどの方がピンときたと思いますので、ここからはほぼ蛇足となる予感がしています(笑
ショートカットアプリで"個人用オートメーション"を作成します。
オートメーションのきっかけとして"NFC"を選択し、用意したNFCタグをスキャンします。
タグの名称はなんでもいいんですが、ここでは"アイドルライセンス"としておきます。
"次へ"をタップでオートメーションのアクション選択に移るので、Safariの"URLを開く"を選びます。
開くURLは https://mypage.aikatsu.com/digital-licences を指定します。
"次へ"をタップして確認画面に移行し、実行の前に尋ねるをオフにして"完了"をタップして設定は完了です。
やってみよう
Safariでマイページにログインしておきます。
登録したNFCタグをアイドルライセンス裏の上部に貼り付けます。
アニメのようにiPhoneのうしろにアイドルライセンスをスライドするとマイページのデジタルライセンスが開きます。
実際の様子を動画でご覧ください。
これでなりたい私に、ミラーイン!手持ちのiPhoneXSをアイプラフォンみたいにアイドルライセンス表示するようにしてみました!これでミラーインできるぞ! #aikatsu #アイカツプラネット pic.twitter.com/fS89V0beUu
— kmgnkhr (@kmgnkhr) January 24, 2021
- 投稿日:2021-01-24T12:35:55+09:00
TCAでdelegateを処理するパターン
はじめに
この発表資料は、
iOSアプリ開発のためのFunctional Architecture情報共有会3の発表資料です。内容として、TCAがdelegateを処理するパターンを整理し、Core DataのNSFetchedResultsControllerDelegateで自動で差分を検知してくれる仕組みをTCAでも使えるようにするという内容です。
導入
iOSDC2020 iOSアプリ開発のための"The Composable Architecture"がすごく良いので紹介したいで出た質問から
TCAではActionはViewStoreからしか発火できないので、ユーザ操作からしかActionを発火できないですよね?(だからAppDelegateのイベントとかからActionを呼び出せないのどうするんですか?)
ActionはViewStoreからしか呼び出せないわけじゃない
- ActionはAction自体から呼び出せる
- そもそもReducerはEffectを返しそこで別のActionにmapすることができる
- 例えば何かを監視する際にdelegateの実行をトリガーにしてActionを呼び出せる
- アプリ起動でView表示
- SwiftUIなら
onAppear { viewStore.send( Action.A ) }
- Reducerの処理でDelegateを作成して実行しておく
- delegateの処理で実行されてそこからSubscriberがAction.Bを呼び出す
話すこと
ユーザアクションに縛られないActionの発行
参考
- TCAが用意してくれてるライブラリ
- LocatoionManager
- MotionManager
- TCAのサンプル
- WebSocketClient
- SpeechClient
ユーザアクションに縛られず、何かしらを監視しdelegateで処理を呼び出し、SubscriberでActionを発火させてる例がある
余談: よくわからないこと
- ManagerとClientの名前の違いはどこから?
- Managerが専用のActionとかErrorを持ってるわけじゃない
- LocationManager
- Action
- Error
- MotionManager
- WebSocketClient
- Action
- Error
- SpeechClient
- Action
共通するパターン
- 監視開始用クロージャを定義
- staticな初期化でシングルトン的に作れるようにする(.live)
- 監視開始用クロージャを上書きする
- delegateを作成
- delegateが実行するメソッドからActionを呼び出すようにする
- Reducerからシングルトンを呼び出す
- Actionを次のActionにmapできるようにする
実際のコードからこのパターンを理解する
LocationManager
- ユーザの位置情報をリアルタイムに検知してActionを発火してその位置を教えてくれる
- ReducerのActionとして位置情報を伝えてくれる
実際のコード
プロパティとしてクロージャを用意
上書き可能なロジックを用意しておく
- 上書きされる前に実行すると未実装としてクラッシュ
- これはテストコードでも任意の実装に置き換えも可能
public struct LocationManager { ... var create: (AnyHashable) -> Effect<Action, Never> = { _ in // ActionはLocationManager.Actionを省略できている _unimplemented("create") } ... }
Managerをstaticに作成
- シングルトン的にしておく
- classじゃなくてstructでいい
- 状態の変化を通知するオブジェクトは重複してはいけないし壊されてもいけない
public struct LocationManager { ... } extension LocationManager { public static let live: LocationManager = { () -> LocationManager in var manager = LocationManager() ... /* 次にここで managerのクロージャを上書きする処理を書く */ return manager } }
先程用意したcreateプロパティにクロージャを代入
- やること
- iOSのCLLocationManagerでロケーションを探せるように
- delegateをセットしEffectのsubscriberを渡す
- privateなグローバル変数dependenciesにidをキーにして保持
- delegateやmanagerやsubscriberを保持
- AnyCancellableで終了時/キャンセル時にクリーンアップ
extension LocationManager { public static let live: LocationManager = { () -> LocationManager in var manager = LocationManager() ... // ここから追記した __________________ manager.create = { id in Effect.run { subscriber in let manager = CLLocationManager() var delegate = LocationManagerDelegate(subscriber) manager.delegate = delegate dependencies[id] = Dependencies( delegate: delegate, manager: manager, subscriber: subscriber ) return AnyCancellable { dependencies[id] = nil } } } // 追記ここまで ... return manager } }
CLLocationManagerDelegateとは
- Locationが変わった際のprotocolを定義している
- delegateで特定のメソッドが実行される
public protocol CLLocationManagerDelegate : NSObjectProtocol { @available(iOS 6.0, *) optional func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) ... }
CLLocationManagerDelegateのメソッドでSubscriberがイベントを発火させる
let subscriber: Effect<LocationManager.Action, Never>.Subscriber
をもたせる- subscriber.sendでActionを呼び出す
private class LocationManagerDelegate: NSObject, CLLocationManagerDelegate { let subscriber: Effect<LocationManager.Action, Never>.Subscriber // エラー時 func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) { subscriber.send(.didFailWithError(LocationManager.Error(error))) } // location変更時 func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) { subscriber.send(.didUpdateLocations(locations.map(Location.init(rawValue:)))) } ... }
Reducerからシングルトンを呼び出す
- .create(id: LocationManagerId())
- 実行時のユニークなハッシュをIDに
- mapで次のアクションAppAction.locationManagerに変換
- combineでReducerを繋げる
public enum AppAction: Equatable { case onApper case locationManager(LocationManager.Action) ... } ... public let appReducer = Reducer<AppState, AppAction, AppEnvironment> { state, action, environment in switch action { ... case .onAppear: return environment.locationManager.create(id: LocationManagerId()) .map(AppAction.locationManager) // combineしているlocationManagerReducerを呼び出す ... } } .combined( with: locationManagerReducer .pullback(state: \.self, action: /AppAction.locationManager, environment: { $0 }) ) .signpost() .debug() private let locationManagerReducer = Reducer<AppState, LocationManager.Action, AppEnvironment> { state, action, environment in ... }
Core DataのNSFetchedResultsControllerDelegateでDB監視を利用する
- 監視開始用クロージャを定義
- staticな初期化でシングルトン的に作れるようにする(.live)
- 監視開始用クロージャを上書きする
- delegateを作成
- delegateが実行するメソッドからActionを呼び出すようにする
enum InfomationListCore { struct Observer { enum Action { case fetched([InfomationEntity]) } var create: (AnyHashable, NSManagedObjectContext) -> Effect<Action, Never> = { _, _ in .none } static let live: Self = { var observer = Self() observer.create = { id, viewContext in Effect.run { subscriber in let frc = createFetchedResultsController(viewContext) let delegate = FetchedResultsDelegate(subscriber) dependencies[id] = Dependencies( fetchedResultsController: frc, fetchedResultsDelegate: delegate ) return AnyCancellable {} } } return observer } } }
delegateを実装するclass
- NSFetchedResultsControllerDelegateを実装するclass
- delegateメソッドからSubscriberのsendを呼び出す
extension InfomationListCore { class FetchedResultsDelegate: NSObject, NSFetchedResultsControllerDelegate { private let subscriber: Effect<Observer.Action, Never>.Subscriber init(_ subscriber: Effect<Observer.Action, Never>.Subscriber) { self.subscriber = subscriber } // delegateのメソッド func controllerDidChangeContent( _ controller: NSFetchedResultsController<NSFetchRequestResult> ) { let contents = controller.fetchedObjects as! [InfomationEntity] subscriber.send(.fetched(contents)) } } }iOS13からCore Dataのdelegateはsnapshotを取り出せるのもある。snapshotを取り出してCollectionView側で差分をチェックしてくれて反映さそうなのでそっちでもいい。
まとめ
- 前提
- ActionはViewStore以外でも動作する
- ActionはActionを呼べる
- 同列のActionだけでなく、親から子、子から親でも可能
- TCAのEffectはCombine.Publisherに準拠している
- Combine.Publisher/Subscriberはイベントを完了するまで何度でも呼び出せる
- TCAからdelegateで自動で何かを監視して処理が実行される仕組みを使う
- その仕組の中でEffectからSubscriberが取り出せるのでイベントを呼び出せる
一つのActionをきっかけにdelegateを作成して監視させれば、そこからActionを何度も呼び出すことができる。
- 投稿日:2021-01-24T09:50:26+09:00
Kotlin Multiplatform Mobileで作ったプロジェクトで実機ビルドができない場合の対応
概要
プラグイン
Kotlin Multiplatform Mobile
を利用してプロジェクトを作った際、実機ビルドができないとなった時の対応。エラー内容としては下記。
${Path}/iosApp.xcodeproj Building for iOS Simulator, but the linked and embedded framework 'shared.framework' was built for iOS.原因
AndroidとiOS側で共有されている'shared.framework'がiOS Simulator用にビルドされていて、実機では使えないという話。
FWは以下のような処理を経て生成される。
具体的にはConfigurationを基に各ターゲット向けに生成されたFWをコピーし、xcode-frameworks
という名前にしている。shared/build.gradle.ktsval packForXcode by tasks.creating(Sync::class) { group = "build" val mode = System.getenv("CONFIGURATION") ?: "DEBUG" val sdkName = System.getenv("SDK_NAME") ?: "iphonesimulator" val targetName = "ios" + if (sdkName.startsWith("iphoneos")) "Arm64" else "X64" val framework = kotlin.targets.getByName<KotlinNativeTarget>(targetName).binaries.getFramework(mode) inputs.property("mode", mode) dependsOn(framework.linkTask) val targetDir = File(buildDir, "xcode-frameworks") from({ framework.outputDirectory }) into(targetDir) }Gradleタスクを呼び出しは、iOSのBuild Phaseで行われている。
問題があるのはここ。cd "$SRCROOT/.." ./gradlew :shared:packForXCode -PXCODE_CONFIGURATION=${CONFIGURATION}Xcode10で最適化のため新しいbuild systemに移行された。
その際build phaseでの設定方法やその処理内容に変更が入り、ファイルに変更がなければbuild phaseのスクリプトも実行されなくなった。詳しい変更内容は下記で確認。
そのため、
shared:packForXCode
においても一度Simlulator向けにビルドすると、FWは存在しているので実行されなくなる。
xcode-frameworks
を削除すれば、実機向けにもビルドできるが逆も然りで、この場合Simlulator向けにビルドできなくなる${Path}/iosApp.xcodeproj Building for iOS Simulator, but the linked and embedded framework 'shared.framework' was built for iOS.対応
Build Phaseではなく、SchemaのPre-Actionsで
packForXCode
を実行する。
注意が必要なのはProvided build settings from
も変更しておくこと。
- 投稿日:2021-01-24T03:06:19+09:00
【iOS】個人開発で使って気分がよかったライブラリ5選【個人開発】
Yalantis/ColorMatchTabs: This is a Review posting app that let user find interesting places near them
綺麗な上タブが作れるライブラリ!
janselv/fave-button: FaveButton is an iOS cute animated like button written in Swift.
Ramotion/paper-onboarding: PaperOnboarding is a material design UI slider. Swift UI library by @Ramotion
ウォークスルー(解説画面)が簡単に実装できる!
varabeis/SPPermissions: Ask permissions on Swift. Available List, Dialog & Native interface. Can check state permission.
権限のリクエストがお洒落になるよ!
iOSライセンス一覧を生成するツールLicensePlistが便利 | kazyブログ
ライセンス表示もこれで安心!
まとめ
エンジョイ個人開発!
- 投稿日:2021-01-24T02:45:07+09:00
【iOS/Android】ネイティブアプリ開発をしたい人への上達マップ【初心者】
想定読者:これからネイティブアプリを開発したい人
想定読者に向けて、上達の全体像をお伝えします。
新人や駆け出しエンジニアの方が自分がどのあたりにいるか把握でき
モチベーションアップになれば幸いです。まずは入門書の前半くらい
画面のレイアウトを作れるようになる
数字や文字列を表示できるようになる
ボタンを作る
ボタンを押したら、数字が増えたり減ったりするカウンターを作れるようになる
画面遷移ができるようになる
画面遷移の時に値を渡せるようになる入門書の後半くらい
値を保存できるようになる。
値を呼び出せるようになる。
データベースにデータを保存できるようになる。
データベースからデータを読み出せるようになる。色や動きなどを実装できるようになる
結構大変になってくる
通信処理ができるようになる
エラーハンドリングができるようになるお、そろそろ実務の駆け出しくらいには行けるか?
綺麗にレイアウトを組めるようになる
(AutoLayoutなど)
デザインガイドラインを理解する
非同期通信ができるようになるアプリの審査に提出できる
権限周りが大変。プロレベル。
アーキテクチャについて詳しくなる
MVCとMVVMくらいまで
共通化ができるようになる
レイヤードアーキテクチャにて理解・実装リアクティブに動けるようにする
(RxSwiftやJetPackなど)プラスアルファ
CI/CDの構築
適切なアーキテクチャの選定
UX/UIデザインを考えた実装の可能不可能の判断
営業やデザイナーとのコミュニケーションまとめ
箇条書きですが、こんな道筋です。
結構大変になってくるレベルで挫折や、応募してくる方がいらっしゃいますが
もう少しです。どうにかリリースまで漕ぎ着けてください!
いっしょにプロのネイティブアプリエンジニアとしていいもの作りましょう!
- 投稿日:2021-01-24T00:23:16+09:00
iOSアプリリリースで、誰も教えてくれなかった9つのこと
今週、iOSアプリをリリースしました。
Purelistという、とびきり書きやすいリスト型のライティングアプリです。
たぶん使えば言っている意味がわかってもらえるかと思います。リリース後1週間、いくつか想定外のことがあったのでリストにしました。
これを読んで少しでもプロモーションやアプリストアのスクショの改善に役立てていただけると幸いです。1. リリース後にプロモーションしない限り、ほぼ誰にも見つけてもらえない
2. プロモーションしてもほぼ埋もれるし、インストールされない
3. レーティングは国別に表示される
4. サブタイトルは結構重要
5. プロモーション用テキストと概要は同じ場所に表示される
6. Store用アイコンは透過していなくても透過してるよって注意される
7. App Analyticsの更新時間はバラバラ
8. オプトインユーザーが少ないのでデータを拾いにくい
9. フィードバックはApp Storeでもらいにくい1. リリース後にプロモーションしない限り、ほぼ誰にも見つけてもらえない
リリースしてから12時間、何もせずにいたら自分(デザイナー)とエンジニアしかインストールしませんでした。
その後、Product Hunt、Twitter広告、Facebook広告、Apple Search Ads、Instagramに流してやっと何百かインストールしてもらえました。
2. プロモーションしてもほぼ埋もれるし、インストールされない
USやEU、インドを中心とした話ですが、Twitter広告とFacebook広告は配信してクリックがあってもインストールまではほぼ到達しません。
3%〜4%くらいしかインストールしません。
クリエイティブやプロモーション文と実際のアプリストアに差を感じたのかもしれませんが、個人的にはレーティングがないとほぼインストールに至らないという仮説を立てています。App StoreでリリースしたらApple Search Adsを使うのが一番コスパいいです。
Product Huntはよほど世界的に愛されるサービスか、100人くらい継続的にProduct Huntを利用している知り合いがいれば有利です。
知り合いがいない場合は土曜日か日曜日に情報を発信するのが有利です。
サイトを見る人も少ないですが、投稿する人も少ないので、その分TOPに載りやすくなります。
できれば日曜日の方が、月曜日にサイトを閲覧した人にも届きやすいのでいいと思います。3. レーティングは国別に表示される
上にも仮説として書きましたが、レーティングがないとインストールに至る確率は下がると感じています。
そのため、レーティングを知人に頼んで稼ぐことができても、それは他の国では全く見られていないということです。ターゲットとしている国が母国ではないのであれば、なるべくターゲット国を絞った状態でプロモーション等をすることをお勧めします。
でないとレーティングが分散し、誰からも微妙にしか相手にされていないアプリになります。4. サブタイトルは結構重要
App Storeに掲載される、サブタイトルはSEO的にかなり重要になります。
ここに検索キーワードとなるような語句を入れておいた方がいいです。Apple Search AdsのTOPにも記載してある通り、検索結果画面からそのままインストールする人は65%ほどいます。
検索画面に表示されるのはスクショ、サブタイトル、レーティング、タイトルです。
この4つはかなり重要な情報なので慎重に言葉選びや画像選びをすることをお勧めします。5. プロモーション用テキストと概要は同じ場所に表示される
App Store Connectをみるとプロモーション用テキストと概要の入力欄があるが、同じ位置に表示されるものなので、同じ文章を書くとおかしくなります(おかしくなりました)
6. Store用アイコンは透過していなくても透過してるよって注意される
Macのプレビューで画像を開いて保存時に透過のチェックを外すのを忘れずに。。。
7. App Analyticsの更新時間はバラバラ
毎日20時に更新されると思ってたけど全然間違いでした。
16:00に更新されることもあれば、朝4:00になっても更新されないことがあります。
諦めて寝ましょう。スマホもパソコンもバラバラでした。
8. オプトインユーザーが少ないのでデータを拾いにくい
リテラシー高いのはいいことだけども!!
オプトインユーザー、つまりAppleへのデータの提供をOKと言ってくれてるユーザーですが、かなり少ないです。
Appleのアナリティクスしか使用していない場合、十分なデータを取るには十分なオプトインユーザーの数が必要です。
なので、データにならないことがだいぶあります。9. フィードバックはApp Storeでもらいにくい
USやEUは個人主義が進んでいるのか、自分のメディアにフィードバックを記載しますが、App Storeには書いてくれません。
たくさんエゴサしてフィードバックを見つけるしかないです。
なのでエゴサしやすい名前をつけることをお勧めします。
以上です。
また期間が空いたら追加したり別の内容を書いたりすると思います。