20210514のiOSに関する記事は3件です。

【Swift】Hashable, Identifiableについて

はじめに HashableプロトコルとIdentifiableプロトコルって何のために使うの?違いは?と疑問に思ったので調べました。 もし間違いがありましたらご指摘いただけると幸いです。 Hashable 概要 その型の値を元にハッシュ値が計算可能であることを表します。 下記のHashableの定義から、HashableはEquatableプロトコルを準拠しており、hashValueプロパティとhashメソッドを持っていることがわかります。 Hashable public protocol Hashable : Equatable { /// The hash value. /// /// Hash values are not guaranteed to be equal across different executions of /// your program. Do not save hash values to use during a future execution. /// /// - Important: `hashValue` is deprecated as a `Hashable` requirement. To /// conform to `Hashable`, implement the `hash(into:)` requirement instead. var hashValue: Int { get } /// Hashes the essential components of this value by feeding them into the /// given hasher. /// /// Implement this method to conform to the `Hashable` protocol. The /// components used for hashing must be the same as the components compared /// in your type's `==` operator implementation. Call `hasher.combine(_:)` /// with each of these components. /// /// - Important: Never call `finalize()` on `hasher`. Doing so may become a /// compile-time error in the future. /// /// - Parameter hasher: The hasher to use when combining the components /// of this instance. func hash(into hasher: inout Hasher) } Hashableへの準拠が必要なケース ハッシュ値を用いて表現されるデータ構造において、Hashableに準拠された型の利用が必要になります。 代表的な一例ですが、Set、Dictionaryなどを利用する場合、Hashableに準拠した型が必須です。 Set Setの定義を見ると、Element : Hashableとなっており、Hashableに準拠しなければいけないことがわかります。 Setの定義 @frozen public struct Set<Element> where Element : Hashable そのため、Hashableに準拠していないインスタンスをSetに利用すると、エラーが発生します。 struct GridPoint { var x: Int var y: Int } var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] エラー内容 Generic struct 'Set' requires that 'GridPoint' conform to 'Hashable' Hashableに準拠することでエラーが解消され、Setに利用できるようになります。 struct GridPoint: Hashable { var x: Int var y: Int } var tappedPoints: Set = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y: 1)] Dictionaryのキー Setと同様に、Dictionaryの定義から Key : Hashable キーがHashableに準拠する必要があることがわかります。 Dictionaryの定義 @frozen public struct Dictionary<Key, Value> where Key : Hashable そのため、Hashableに準拠していないインスタンスをDictionaryのキーに利用すると、エラーが発生します。 struct GridPoint { var x: Int var y: Int } let dictionary = [GridPoint(x: 2, y: 3): "point A", GridPoint(x: 4, y:1): "point B"] エラー内容 Generic struct 'Dictionary' requires that 'GridPoint' conform to 'Hashable' Hashableに準拠することでエラーが解消され、Setに利用できるようになります。 struct GridPoint: Hashable { var x: Int var y: Int } let dictionary = [GridPoint(x: 2, y: 3): "point A", GridPoint(x: 4, y:1): "point B"] ForEach SetやDictionaryと同様に、ForEachの定義から ID : Hashable IDがHashableに準拠する必要があることがわかります。 ForEachの定義 public struct ForEach<Data, ID, Content> where Data : RandomAccessCollection, ID : Hashable Set、Dictionaryと同じなので、Hashableに準拠しない(エラーになる)場合の記載は割愛します。 以下のようにForEachのidには、Hahsableへの準拠が必要になります。 struct GridPoint: Hashable { var x: Int var y: Int } struct ContentView: View { let array = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y:1)] var body: some View { List { ForEach(array, id: \.self, content: { elem in Text("x:\(elem.x) y:\(elem.y)") }) } } } ForEachの場合、Identifiableを準拠するケースもよくあるので、そちらについては次節で説明します。 Identifiable 概要 インスタンスが一意のIDを持つエンティティの値を保持するタイプのクラス。 下記のIdentifiableの定義を見ると、IDがHashableを準拠していることがわかります。 Identifiable public protocol Identifiable { /// A type representing the stable identity of the entity associated with /// an instance. associatedtype ID : Hashable /// The stable identity of the entity associated with this instance. var id: Self.ID { get } } また、Identifiableを準拠すると、そのクラスや構造体はidプロパティを持つ必要があります。 struct GridPoint: Identifiable { let id = UUID() var x: Int var y: Int } Identifiableへの準拠が必要なケース Hashableと似ていますが、一意に特定する必要がある要素において、Identifiableに準拠された型の利用が必要となるケースが多いです。 たくさんあるので、一例としてHashableでも出てきたForEachについて説明します。 ForEach Hashableの例ではForEach内でidを引数指定していましたが、Identifiableを準拠したクラスや構造体の場合、そのクラスや構造体のプロパティにidがあるため、ForEach内では指定しません。 Hashableの場合 ForEach(array, id: \.self, content: { elem in Identifiableの場合 ForEach(array, content: { elem in ただ、今までと同様にIdentifiableを準拠していない場合、idが無いため、ForEachでエラーとなります。 struct GridPoint { var x: Int var y: Int } struct ContentView: View { let array = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y:1)] var body: some View { List { ForEach(array, content: { elem in Text("x:\(elem.x) y:\(elem.y)") }) } } } なぜかこんなエラーが出ます。 Failed to produce diagnostic for expression; please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the project Identifiableを準拠することでエラーが解消され、ForEachで利用できるようになります。 struct GridPoint: Identifiable { let id = UUID() var x: Int var y: Int } struct ContentView: View { let array = [GridPoint(x: 2, y: 3), GridPoint(x: 4, y:1)] var body: some View { List { ForEach(array, content: { elem in Text("x:\(elem.x) y:\(elem.y)") }) } } } Identifiable と Hashable の違い では、IdentifiableとHashableの違いは何でしょうか? こちらの記事によると、違いは以下のようです。 https://nshipster.com/identifiable/ 識別子とは異なり、ハッシュ値は状態に依存し、オブジェクトが変更されると変化する。 識別子は起動間で安定しているが、ハッシュ値はランダムに生成されたハッシュシードによって計算されるため、起動間で不安定になる。 識別子は一意だが、ハッシュ値が衝突する可能性があるため、コレクションからフェッチするときに等価性チェックが必要になる。 識別子は意味のあるものになる可能性があるが、ハッシュ値は無秩序である。 つまりIdentifyとHashの言葉通り、識別子 ≠ ハッシュ値 となります。 最後に 普段何気なく利用していましたが、調べることで理解が深まり、意図を持って利用できるようになりました。 今後も疑問を持ったものは調べてまとめていければと思います。 閲覧いただきありがとうございました。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【iOS】古いMacとXcodeでバイナリを作ったらアプリが起動できなくなった話

概要 2020年9月末に社内配布用にバイナリ作成をしたところ、iOS13でアプリが起動できなくなってしまった。 (iOS11では起動できていた。) バイナリ作成環境 Mac Mojave Xcode 9.4 調査 バイナリ作成者のKeychainを確認したところ、バイナリ作成時の署名に利用した証明書のステータスが「信頼されていません」になっていた。 証明書の問題かと思い、証明書を作り直すがステータスは変わらず、バイナリを作ってもやはりクラッシュのまま... 原因 一言で言うと、作業者のMacの中間証明書更新漏れ \(^o^)/ Appleからのお知らせにもありました。 Xcode 11.4.1以降を使用していたら自動で取得できている模様。 新しいXcodeを使っていない開発者は自分で取得する必要があるってことですね。 まとめ 最新の中間証明書を取得してバイナリ作成したら動きました。 やはり、古いシステムは使い続けてもいいことないです。(当たり前) 皆さんは定期的にバージョンアップしましょう。 2021年内にプッシュ通知証明書の更新計画もあるようです。 当日になって慌てないよう事前の準備を心がけましょう。(当たり前) 関連 補足 中間認証局のしくみ Apple Worldwide Developer Relations Certification Authority (WWDR) は、Appleに認証された開発者であることを証明してくれる中間認証局です。 Apple Root CA によって、この中間認証局の証明書が発行されています。そして、中間認証局が、iOSアプリ用の証明書を発行してくれます。 つまり以下のような関係になっています。 Apple Root CA (ルート認証局) ↓ WWDR (中間認証局) ↓ iOSアプリ用証明書 認証局の証明書が有効期限切れになると、発行された証明書も無効になります。 多段構成にすることによって、細かく有効期限を管理できるため、中間認証局を使うという構成が取られます。SSL/TLSのサーバ証明書も同じですね。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityからXcodeにしてビルドする関連(2021年5月)

やったこと ・Unityで2Dゲームを制作 ・Admobで広告を表示(iOS) Unityでアプリを作るまでは良かったが、その後が大変だった。 自分の環境 ・MacOS catalina ・Xcode 12.4 ・Unity 2019.2.12f1 Personal ・Google Mobile Ads Unity Plugin v5.4.0 ・CocoaPods 1.10.1 Admob広告を表示させる GoogleMobileAds-v5.4.0.unitypackageをダブルクリックしてすべてチェックが入っていることを確認してimport その他メディエーションしている場合は各種Adaptorをimportした後、iOSリゾルバを設定する。 Unity > Assets > External Dependency Manager > iOS resolver > settings にて、CocoaPodsIntegration のプルダウンから Xcode Project Add CocoaPods to the Xcode Project を選ぶ。 アプリを実装する Unityで作る。Admobのテスト広告が表示されることを確認。 証明書を作る 端末で確認&ストアで公開するために必要 ・AppleDeveloperProgramを購入する。 ・Macのキーチェーンアクセス > 証明書アシスタント > 認証局に証明書を要求 CertificateSigningRequest.certSigningRequestを作る ・Apple Developer > Certificates, Identifiers & Profiles > Certificates からCertificateSigningRequest.certSigningRequestのアップロードし、DeveloperとDistributionの証明書を作る(ios_develop.cer, ios_distribution.cer) ・AppIDを作成する ・provisioning profilesを作成する このとき開発用(developer)と公開用(distribution)を作成する。 開発用provisioning fileは開発用のAppIdとBundleIdを割当て、公開用には公開用のAppIdとBundleIdを割り当てておく。また開発用では実行したい端末を登録する。 Unityから「開発用」にビルド UnityでビルドするとXcodeのプロジェクトが作られ、Xcodeからビルドして端末で実行&公開用のipaファイルを作るという流れ。 なおXcodeのシミュレーターでの実行はしない(というよりUnity自体がシミュレーターである...というよりXcodeシミュレータでの確認はできなかったので諦めた)。 まずは端末でアプリを確認するためにUnityからビルドする。 ・Unity > Fils > BuidSettings にてPlatformをiOSを選択してswitch platformする。 ・GraphicAPIsが足りない状況だとBuildボタンがグレーアウトするので PlayerSettings > Other Settings > GraphicAPI にて OpenGLES2, 3を追加する ・Unity > Preferences > PlayerSettings > iOS > Other Settings にて以下の設定をする  ・iOS provisioning profilesの開発用を設定する  ・profile typeをDevelopmentにする  ・Bundle Identiferを開発用にする  ・TargetSDKをDeviceSDKにする ・Buildボタンを押すと開発用のXcodeのプロジェクトファイルができる Xcodeからビルドして端末で確認 ・Xcodeのプロジェクトファイル(Unity-iPhone.xcodeproj)ができているので、ターミナルで同階層のディレクトリに行ってCocoaPodsを実行する pod install --repo-update ここでエラーになる場合はCocoaPodsを再インストールする(別記事を参考にすべし) ・Xcodeをxcworkspace(Unity-iPhone.xcworkspace)で起動する ・build settingsにて以下の設定をする  ・provisioningfile 開発用のprovisioning fileを選択  ・Deployment teamで開発用を選択 ・端末をつないで実行する 公開用のipaファイルを作成する 開発用と公開用とではprojectファイルから異なってくるのでUnityからビルドをしてXcodeプロジェクト自体を作り直すことになる。 2つディレクトリを作っておき、そこに開発用と公開用で別途Xcodeのprojectを作成するのがスマートかと思う。 先程の開発用として設定した部分をすべて公開用のものを使って Unity〜Xcode〜CocoaPods〜BuildSettingsの設定作業を経て XCode > Product > Archive する。 起こりうるエラーと解決方法 証明書関連のエラー ・Signing for "Unity-iPhone" requires a development team. Select a development team in the Signing & Capabilities editor.(「Unity-iPhone」にサインするには開発チームが必要です。 Signing&Capabilitiesエディターで開発チームを選択します) ・Unity-iPhone has conflicting provisioning settings.(Unity-iPhoneのプロビジョニング設定が競合しています) ・A valid provisioning profile for this executable was not found.(この実行可能ファイルの有効なプロビジョニングプロファイルが見つかりませんでした) 証明書には開発用と公開用があり、UnityからXcodeを生成した場合、Xcode上のbuildsettingで開発用と公開用を切り替える必要はない(というよりできないと思う)。 従って開発用と公開用で別々にXcodeのprojectを吐き出させることになる。 Xcode上で設定するのは BuildSettings > Provisioning File と DeveloperTeamの2点のみ。 開発用と公開用でそれぞれ適したProvisioningFileとDeveloperTeamを設定する。 Admob関連のエラー ・GoogleMobileAds/GoogleMobileAds.h' file not found ・No visible @interface for 'GADRewardedAd' declares the selector 'initWithAdUnitID:' ・No visible @interface for 'GADRewardedAd' declares the selector 'loadRequest:completionHandler:' ・No visible @interface for 'GADRewardedAd' declares the selector 'isReady' 各種frameworkは手動ではなくcocoapodsで入れる。またiOSリゾルバ設定を行って解決する。 ・<Google> Cannot find an ad network adapter with the name(s) メディエーションすると起こりうるエラー。 cocoapodsでのimportとiOSリゾルバを設定して解決する。 余談 今回は以下のことも行っているのでこれらも記事にしたい。 ・CocoaPodsのインストール ・スクロールテキスト ・WebView導入 ・iOS14対応(AppTrackingTransparency導入+SKAdNetwork記述) ・サーバとの通信 ・メディエーション
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む