2024.10.10
将来は卵1パックの価格が2倍に? 多くの日本人が知らない世界の新潮流、「動物福祉」とは
提供:LINE株式会社
リンクをコピー
記事をブックマーク
Bruce Evans氏:では、ここからはiOSの話になります。LINEでは昔からCoreDataを利用して、大量のコードがCoreDataに依存しています。CoreDataは、AndroidのRoomのように、ハイレベルなパーシステンスフレームワークですがApple専用のものです。
これから、このCoreDataのセットアップ、データベース作成、ID変更、検索、保存、そしてデータの読み込みを紹介します。
まずは、CoreDataは暗号化のサポートがないため、最初から暗号化の仕組みを加える必要があります。今表示している画像は、CoreDataの基本的なかたちです。NSManagedObjectContextは、プログラマーがふだん使っている部分です。オブジェクトやエンティティがこのレイヤーなので、CoreDataのメイン機能と考えてもいいと思います。
その下はNSPersistentStoreCoordinatorです。NSPersistentStoreCoordinatorは、Storeを複数管理するレイヤーだけです。そしてNSPersistentStoreは、ファイルストアのレベルで、最もraw SQLiteと近いです。
では、NSPersistentStoreをサブクラスしましょう。でもその前に、サブクラスしたあとにどうやって使うかをまず見てみましょう。
このコードは、カスタムのMySecureStoreをCoreDataに接続します。はじめに、MySecureStoreを登録する必要があります。これはUICollectionViewと似ていて、MySecureStoreのクラスをStringIDに紐づけます。
このコードはどこでも置けますが、絶対にCoreDataが立ち上がる前に呼ばないといけません。
それでは、CoreDataを立ち上げましょう。これについて2つのポイントがあります。まずはNSPersistentStoreのdescriptionのタイプに、先ほど登録したStringIDを使います。ここはすごく大事です。これを忘れてしまうと、作ったサブクラスは一切使われません。
次にSQLCipherを使っているので、パスワードのオプションを追加します。ここからは、いつもどおりのCoreDataです。呼ぶ場所やオブジェクトの使い方は、気にせずに使いましょう。
ではCoreDataの準備が終わったので、次にSQLCipherを準備しましょう。SQLCipherへのリンクは、SQLCipherのサイトに詳しく書かれているので、ここはあまり細かく話しません。ただ、次の2点はかなり大事なので、気をつけてください。
まず1つは、SQLCipherはSQLiteと一緒に動かないので、SQLiteの代わりにリンクしましょう。もう1つはlinker flagsです。もしSwiftで動かなければ、おそらくlinker flagの問題です。
準備が終わったので、やっとサブクラスを作れます。このスライドが表示しているように実装します。
基本の動きは、この5つのファンクション。loadMetadataで準備して、excute(_:with:)で検索と保存します。2つのnewValueは、データベースから読み込んだデータをNSManagedObjectに変更するためで、obtainPermanentIDs(for:)は、データベースのプライマリーキーを使ってNSManagedObjectIDに作成します。
とりあえず、loadMetadataを見てみましょう。loadMetadataは、CoreDataが起動する時に呼ばれます。やっとStoreをセットアップするタイミングです。
主に3つのタスクがあります。まず1つ目は、データベースを開くことです。
続いて2つ目は、データベースのパスワードを使って、復号化の作業です。先ほどoptionsに追加をしたパスワードを呼んでデータに変更し、UnsafePointerとしてSQLCipherに渡します。
最後に、loadMetadataの名前どおり、metadataをセットしないといけません。基本のvalueはこの2つで、ユニークキーはNSPersistentStoreからそのまま使えます。そしてtypeは先ほどのStringIDになります。
次は、データベース作成が必要です。基本的にmanagedObjectModelのエンティティを全部テーブル化します。なのでエンティティ、リレーションシップ、そしてインデックスにテーブルを分けます。
まずは、エンティティのテーブルを作りましょう。このテーブルは普通のSQLiteテーブルで、エンティティのプロパティをカラムにマッピングします。
はじめにテーブルのプライマリーキーを指定します。これはNamespace collisionの可能性があるので、名前に気をつけましょう。
次に、エンティティのアトリビュートをカラムに変更します。ここで、attributesByNameを使います。attributesByNameは、NSAttributeDescriptionのディクショナリです。
キーをコードネームとして使い、attributeTypeでSQLiteのタイプを決められます。
そしてエンティティのリレーションシップです。CoreDataでリレーションシップは、2種類あります。toOneとtoManyです。toManyはちょっと特別なので、別のテーブルを作ります。この方法はのちほどお話しします。toOneは、ここで追加できます。ただ、オブジェクトごとに保存はできません。その代わりに、先ほどのプライマリーキーを使います。
ここで止めることはできますが、CoreDataはだいたいsubentityを使いますので、subentityを追加しましょう。CoreDataの裏のsubentityはsuperentityのテーブルに入っているので、同じにしました。今のファンクションがrecursiveになれば、普通に呼んで、コードを書かなくていいので、楽にできます。
そして、カラムの情報が全部集まったので、CREATEを実行して、エンティティテーブルは完成です。
続いて、toManyをどこかでストアしないといけません。なので別のテーブルを作ります。このテーブルのローは、1つのエンティティと1つのリレーションシップです。そこで複数のローを合わせることによって、toManyのリストを作れます。
今一番大事なのは、ここです。toOneはもう使ったので、まずはフィルタリングして、toManyのみにしましょう。そしてテーブルが終わったら、またsubentityのケースを考えないといけません。ただし今度は、同じテーブルではなく、新しいテーブルを作ります。
最後にインデックスを追加しましょう。エンティティにインデックスのリストがあるので、かなり助かります。NSFetchIndexDescriptionを使って、どのカラムをインデックスするか確かめます。また同じテーブルを使っているので、subentityも同じ扱いです。これで、データベース作成が終わりました。
データベースを作成したら、プライマリーキーのマッピングが必要です。これは簡単に実装できますので、説明は省略します。
一番大きなプライマリーキーを見つけて、1を足してベースIDとして使います。そのベースIDをnewObjectID(for: referenceObject:)に渡して使います。これは、スーパークラスに実装しているので、呼ぶだけで大丈夫です。
検索と保存は、すべて同じファンクションを通します。少し不思議ですが、AppleのAPIなので仕方ありません。NSPersistentStoreRequestはサブクラスなので、サブクラスのタイプによって検索か保存か分けることは可能です。
検索の場合は、NSFetchRequestですが、保存の場合は、NSSaveChangesRequestです。まずは検索のほうを見てみましょう。
Fetch Requestは、さらに4種類あります。基本はそんなに変わりませんが、全部Arrayをreturnします。ということで、.countResultTypeの場合は、1つのエレメントしか入っていないArrayをreturnします。ここのは、1つのエレメントに1つのオブジェクトのかたちです。
全部の検索をお見せするのは時間的に難しいので、managedObjectIDResultTypeだけを見てみましょう。
まず、さっき作ったプライマリーキーをセレクトします。ここから結果がある間にループして、データベース用のIDを読みます。これはこのまま使いませんので、newObjectID(for: referenceObject:)を通して、NSManagedObjectIDに変更します。あとはreturnするだけです。
ということで検索が終わって、残されているのは保存です。今回は1つのタイプしかないんですが、タイプの中に、3つのタスクが含んでいます。幸いすべて似ているので今日はInsertに集中します。
ちなみに1ヶ所変な行があります。この最後のreturnは、空のArrayです。これはさっきの検索と同じファンクションですから、returnタイプはOptionalじゃない上に、Appleのドキュメンテーションによると、Arrayを期待しています。
とりあえず、Insertを見てみましょう。これはデータベース作成の時とよく似ています。ただし今回の目的はアトリビュートではなく、アトリビュートバリューのことです。今回のほうが、少し難しいです。
まずは、データベースのプライマリーキーを必ず入れないといけないので、先に用意しましょう。続いて、プロパティごとに名前を使って、カラムの名前をマッピングします。バリューのほうはもうちょっとやっかいです。アトリビュートの場合は、そのまま使いますが、リレーションシップの場合はNSManagedObjectなので、直接データベースで使えません。
nilじゃない時に、データベース用のキーに変更する必要があります。ちなみにここでは、toManyを無視していますが、実際にそれもフィルタリングしないといけないんです。ただ、スライドに入れるのはちょっと難しかったです。
では、検索も保存もできたから試してみると、動きません。今のはまだ、Faultしか返せないので、Faultをきちんと読み込まないといけません。
これについては、2つのファンクションがあります。newValuesForObject(with:with:)と、newValue(forRelationship:forObjectWith:with:)です。簡単に言うと、newValuesForObject(with:with:)は、アトリビュートを取り出し、newValue(forRelationship:forObjectWith:with:)は、リレーションシップを取り出します。
はじめに、アトリビュートを見てみます。主に2つのステップがあります。ここで、カラムの名前とタイプを取ります。リレーションシップは、完全に無視しています。先ほどの、newValue(forRelationship:forObjectWith:with:)でできますので、ここはだいぶ楽になります。
これを実装したら、次にvalueをマッピングしないといけません。特別なことはやっていませんが、SQLiteのAPIにより、タイプごとの取り扱いになります。なので、かなり大きい数値になります。
ただし、これが終われば、ストアノードを作って、returnするだけです。
リレーションシップのほうは楽です。1つ引っかかるポイントとしては、このAnyのreturnタイプです。実は3つのタイプをreturnできます。toOneのリレーションシップなら、NSIIncrementalStoreNodeかNSManagedObjectIDにreturnします。そして、toManyの場合は、NSManagedObjectIDのArrayをreturnします。
returnだけではなく、実装も別々なので、toOneとtoManyを分けてみましょう。
toOneの場合は、さっきのアトリビュートより簡単です。リレーションシップをメインエンティティテーブルから取って、NSManagedObjectIDに変更するだけです。毎回同じタイプなので、さっきの大きい数値も必要ありません。
注意点はここです。SQLiteのAPIは、nil Intの場合、0をreturnします。なのでこの実装だと、0をvalid IDとして認めてはいけません。
toManyはtoOneと似ていますが、データベースが別なので、スレッドは少し違います。複数なのでループも入っていますが、それ以外はtoOneとまったく一緒です。
これで、簡単なStoreを作成できました。いろいろ(説明が)足りない点はありますが、もし興味があればぜひSNSで連絡してください。喜んでお話しします。
今日はiOSと暗号化したCoreDataの作り方という内容をお話ししました。ありがとうございました。
LINE株式会社
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略
2024.11.13
週3日働いて年収2,000万稼ぐ元印刷屋のおじさん 好きなことだけして楽に稼ぐ3つのパターン
2024.11.11
自分の「本質的な才能」が見つかる一番簡単な質問 他者から「すごい」と思われても意外と気づかないのが才能
2024.11.13
“退職者が出た時の会社の対応”を従業員は見ている 離職防止策の前に見つめ直したい、部下との向き合い方
2024.11.12
自分の人生にプラスに働く「イライラ」は才能 自分の強みや才能につながる“良いイライラ”を見分けるポイント
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.11.11
気づいたら借金、倒産して身ぐるみを剥がされる経営者 起業に「立派な動機」を求められる恐ろしさ
2024.11.11
「退職代行」を使われた管理職の本音と葛藤 メディアで話題、利用者が右肩上がり…企業が置かれている現状とは
2024.11.18
20名の会社でGoogleの採用を真似するのはもったいない 人手不足の時代における「脱能力主義」のヒント
2024.11.12
先週まで元気だったのに、突然辞める「びっくり退職」 退職代行サービスの影響も?上司と部下の“すれ違い”が起きる原因
2024.11.14
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略