2024.12.10
“放置系”なのにサイバー攻撃を監視・検知、「統合ログ管理ツール」とは 最先端のログ管理体制を実現する方法
提供: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.12.10
メールのラリー回数でわかる「評価されない人」の特徴 職場での評価を下げる行動5選
2024.12.09
10点満点中7点の部下に言うべきこと 部下を育成できない上司の特徴トップ5
2024.12.09
国内の有名ホテルでは、マグロ丼がなんと1杯「24,000円」 「良いものをより安く」を追いすぎた日本にとって値上げが重要な理由
2023.03.21
民間宇宙開発で高まる「飛行機とロケットの衝突」の危機...どうやって回避する?
2024.12.10
職場であえて「不機嫌」を出したほうがいいタイプ NOと言えない人のための人間関係をラクにするヒント
2024.12.12
会議で発言しやすくなる「心理的安全性」を高めるには ファシリテーションがうまい人の3つの条件
2024.12.06
嫌いな相手の行動が気になって仕方ない… 臨床心理士が教える、人間関係のストレスを軽くする知恵
PR | 2024.11.26
なぜ電話営業はなくならない?その要因は「属人化」 通話内容をデータ化するZoomのクラウドサービス活用術
2024.12.11
大企業への転職前に感じた、「なんか違うかも」の違和感の正体 「親が喜ぶ」「モテそう」ではない、自分の判断基準を持つカギ
PR | 2024.11.22
「闇雲なAI導入」から脱却せよ Zoom・パーソル・THE GUILD幹部が語る、従業員と顧客体験を高めるAI戦略の要諦