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
よってたかってハイリスクのビジネスモデルに仕立て上げるステークホルダー 「社会的理由」が求められる時代の起業戦略