Swiftで遊ぼう! - 530 - Core Data
- Swiftで遊ぼう!の前書き-> Life-LOG OtherSide
- Swift2.1 & Xcode7.1対応の日本語版アップルチュートリアル!
2016年2月23日:記事をまとめました。
メリークリスマス!
とうとうSwiftの勉強を初めて1年半が過ぎました。1年半、毎日Swift関連の勉強を続けているのに未だにアプリ公開はできていません(T_T) ぼんやりとしたアプリのイメージはあるけど、なかなか具体的なコーディングに繋がりませんね。自分のアプリ実現のために必要な知識が無いからです。データベースです。この機能も必要です。
データベースを使うなら、Realmってのがゴールドスタンダード的な内容がネット上に溢れているけど、Apple信者の私はCore Dataから取り組みます。かなり取っつきにくいという話があります。
しかし、純正のデータベースってことなのでiOSフレームワークとの親和性はかなりと信じて取り組みます。
Core Dataって何の事でしょう?
Core Dataとは、アプリケーションのモデル(M)としてのオブジェクトを管理するためのフレームワークです。オブジェクト・サイクル*1とオブジェクト・グラフ*2を管理することで、Persistent*3機能も含んでいます。まあ、こういう説明って初心者には理解しにくいことが多いですね。
Core Dataを使うとモデル・レイヤー*4をサポートするコードの量を50〜70%減らすことができます。←なんの事かさっぱりです。
次のようなビルトイン機能があるという説明です。
- 基本的なtext編集じゃなくビルトイン機能としてのundo、redo機能が変わりました*5。
- 繁栄の変化の管理*6には、オブジェクトの関連性の一貫性の維持が含まれます*7。
- オブジェクトのレイジー・ローディング、特にフォルティング(失敗?)機能の維持*8と「コピー&ライト・データ」でオーバヘッドを減らすことができます。
- プロパティ値の有効性を自動で判断します。基本的なKVコーディングを拡張したオブジェクト管理で有効範囲内にある個別の値を確認するメソッドが使える事になって値の組み合わせが分かるようになる*9。
- シェーマ融合ツール*10で、簡単にシェーマを変更して、シェーマ融合に効率的な実行をする*11。
- ユーザーインターフェイスの統一性をサポートするためのアプリケーションコントローラーを統合するオプション*12。
- グルーピング、フィルタリング、そしてメモリー上、ユーザーインターフェイス上でデータを調整。
- 外部のデータ保存場所にオブジェクトを保存のための自動機能*13。
- 洗練されたクエリー編集*14。SQLの代わりに、フェッチリクエストに関連したNSPredicateオブジェクトに関連した複雑なクエリーを作れます。
- バージョントラッキングとマルチライティングで自動的に不一致の受け入れをサポートしたロッキング*15。
- OSXとiOSツールの効果的な融合
Core Dataが本当に理解できるのだろうか? 前書きから何を言っているのか1行目から理解できない状態です(T_T)
取りあえず、アップルのドキュメントから読んでいきます。
-
-
-
- -
-
-
Core Data、今のところさっぱり分かっていないけど勉強は続けます。
Creating a Managed Object Model
コントロール可能なオブジェクトモデルを作る
Core Dataの機能性は、アプリケーションの「エンティティ」、「プロパティ」そしてそれらの相互関係を表現する「シェーマ」に依存して発揮されます。Core Dataは「シェーマ」を利用するのですが、これを「マネージド・オブジェクト・モデル」と呼び、「NSManagedObjectModel」のインスタンスになります。今のところどういう意味かハッキリわかりません。一般的にモデル*16が複雑になれば、それだけCore Dataの利用に価値が生まれるということです。まあ裏を返せば簡単なモデルの場合、Core Dataの実装が複雑なだけに無駄なことをしているような気がするでしょう。こういうことを考えると、チュートリアルで本来の備わっているCore Dataの利便性を表現することができないかもしれないですね。
マネージド・オブジェクト・モデル(シェーマ)は、保持データから記録をマップ(map)してアプリケーションで使用しているマネージド・オブジェクトに加える(map.. from … to)権限(allow)をCore Dataに与えます*17。
モデルは、NSEntityDescriptionクラスのインスタンであるエンティティ・ディスクリプション・オブジェクトの集合体です。エンティティ・ディスクリプションは、その名前がエンティティを意味(データベースのテーブルのようなもの)して、クラス名がアプリケーション内でのエンティティを意味します。そのクラスにはプロパティ(属性や関係性)もあります。
ゆっくり続けます(^^;)
-
-
-
- -
-
-
Core Data、理解できないことにチャレンジするってのは楽しいですね。
今のところ分かっていないところだらけです。「エンティティ」とか「シェーマ」もまだはっきりしない状態です。それでも次に進んでいきます。
Creating an Entity and Its Properties
エンティティとそのプロパティを作る
Xcodeで新しいプロジェクトを選ぶと、最初にテンプレートを選ぶダイアログが出てきます。その中に「Use Core Data」というチェックボックスがあるので選びます。
するとプロジェクト・ナビゲータにプロジェクト名*18に「.xcdatamodeld」がついたCore Dataができています。まだエンティティは無いので何もありません。
下部にある「Add Entity」ボタンを押せば、新しい「Entity」が「Core Data editor」の「ナビゲーターエリア」に作られます。関連性を定義できるエディタエリアが横にできます。
新しくできたエンティティを選択した状態で、右橋のパンから「Data Model Inspector」を選択して、エンティティの名前を変更します。
エディタエリアの属性(Atributes)や関連(Relationships)を選んで「+」ボタンで新しいプロパティを作ります。
ここまでのステップでモデルの中にエンティティが作られたけどデータが作られたということではありません。データが作られるのはアプリケーションが立ち上がる時です。NSManagedObjectインスタンス(マネージドオブジェyクト)を作るための基本としてエンティティーが利用されます。←ちょっと分かって無いです。無茶苦茶な訳です...
ちょっと頭を冷やすためにここで中断(^^;)
-
-
-
- -
-
-
Core Dataの理解が進みません。
エンティティとはデータベースのテーブルのような存在で、NSManagedObjectクラスのサブクラスになります。エンティティの定義に関して理解する必要があります。
Defining an Entity
エンティティの定義
Entity Name and Class Name
Data Model inspectorを使ってエンティティの「名前」と「クラス名」を付けます。
同じものを刺していますが、「エンティティ名」と「クラス名」は同じで無いことに注意する必要があります。クラス名の名前の付け方は、NSManagedObjectクラスのサブクラスになるためObjective-C時代からの慣習から「MO」というサフィックスがつきます。
Abstract Entities
エンティティのインスタンスを作らない場合、エンティティを抽象化(abstract)させます。これは複数のエンティティが1つの共通のエンティティから派生しているような場合、共通のエンティティはインスタンス化されないため抽象化させることになります。例えば、「Employee」エンティティを「Person」を抽象化エンティティとして定義することができ、このPersonエンティティから具体的な「Employee」とか「Customer」といったサブエンティティをインスタンス化させることがで来ます。エンティティを抽象化した場合、COre Dataにインスタンス化されないことを明確にしておかなければなりません。
自分で上記の文章を書きながら、どういうことかはっきりと理解できないのが辛いです。
Entity Inheritance
エンティティはNSManagedObjectクラスのサブクラスになるので、当然継承もできるのも当然だと思いましたが、あえてクラス継承と同様にエンティティ継承ができることを明示するする理由はどうしてでしょう。似たようなエンティティを数多く持っている場合、共通プロパティをスーパー(上位)エンティティ(所謂ペアレント・エンティティ)に集めることができます。複数のエンティティから共通プロパティを拾い上げてくるのではなく、胸中プロパティでエンティティを定義して、それを継承してサブエンティティを作ればいいようです。例えば、「Person」エンティティに属性として「firstName」と「lastName」を定義してから、それを継承した「Employee」と「Customer」というサブエンティティを作ると、この両方のエンティティで「firstName」と「lastName」が継承されています。この例としてレイアウト・ダイアグラムで確認することができます。エディタエリアの右下部にある「Editor Style toggle」スイッチを押せば次のようなレイアウトが見えます。
多くの場合に、サブエンティティから継承するクラスに合わせてカスタムクラスを定義することもできます。ビジネスロジック*19で一般的な全てのエンティティを何度も定義するのではなく、1箇所に集めて、それを継承するといいでしょう。
最後の文章は分かっていません。まあいずれ理解できるとして、今日は終わり。
-
-
-
- -
-
-
Core Dataの勉強は超スローです。まあ進捗度は以前と同様ですけど(^^;)
Managed Object Modelの概要を勉強中です。
Defining Attributes and Relationships
エンティティのプロパティは3つあります。属性(attribute)、関連性(relationship)、そしてフェッチプロパティ(fetch property)です。これらのプロパティは「名前」と「タイプ」を持っています。「名前」に関して言えば、NSObjectもしくはNSManagedObjectのパラメーターを持たないメソッド名と同じ名前を付けることはできません。例えば、「description」という名前をプロパティ名につけることができないのです。
「Transient(一時的)」という属性をモデルの一部として与えることができるのですが、エンティティインスタンスのデータとして永続的な保存はしません。Core Dataは、Transientプロパティの変化を追跡しているので、undo操作のために記録されています。Transientプロパティを多様な目的のために利用できます。
Attributes
Core Data model editorでCore Data Model inspectorの属性の値を定義します。Core DataはネイティブにNSString型のstring、NSDate型のdate、NSNumber型のintegerをサポートしています。Swiftで取り扱うときにキャストが必要ですね。
更にオプショナルを設定することができるようですが、データベースとして有名なSQL(古くてパフォーマンスが遅いので資金は主流ではなくなっているようです)のNULLとObjective-Cのnilと扱いが異なるので、numberを扱う場合はオプショナルを使用しない方がいいようです。
今日はここまで。
-
-
-
- -
-
-
Core Dataの勉強は超スローです。理由はある程度わかりました。Core Dataの勉強というのは、実はデーターベースの勉強という側面もあるからです。プログラミングに作法(デザイン・パターン)があるように、データベースにも色々作法があるようです。「ビジネスロジック」という言葉も知りませんでした。Core Dataの勉強をすることでデーターベースの扱いを勉強します。
まだ、Managed Object Modelの概要を勉強しています。
エンティティのプロパティでAttribute(属性)の説明をしたので、今日は「関係(Relationship)」と「フェッチド・プロパティ(Fetched property)」の説明です。
Core Dataは、1方向、もしくは多方向への関係性、そして、フェッチド・プロパティをサポートしています。フェッチド・プロパティとは、weakな関係性で、一方通行の関係性の事です。employeeとdepartmentドメインにおいて、departmentのフェッチド・プロパティは、「recent hires」ってことになります。employeeには、recent hiresの関連性を持っていません。
「Type」フィールドで、関係性が、1方向なのか、多方向への関係性なのか設定します。関係性は、一度に一方の方向へのみ設定できます。複数から複数への関係性を作りたい場合、多方向への関係性を2つ作ってから、それぞれを「inverse」設定して組み上げます。
「Destination」フィールドは、関係性がコード内でアクセスされたときにオブジェクトが戻るところを定義します*20。仮に、1方向への関係性が定義されていれば、1つのオブジェクト(オプショナルならnil)が戻ってくるでしょう。複数の関係性が定義されていたら、「set」が戻ってくるでしょう(オプショナルはnil)。
1つの方向性にしか関係性を定義することができないので、逆方向の関係性は「Inverse」フィールドを使って設定します。これで両方向からの関係性を構築することができます。
今日はここまで。
-
-
-
- -
-
-
大晦日です。今年最後の締めくくりはCore Dataの勉強になりました。
まだまだ概要も掴めていないのですが、Core Dataを使えるようになると、アプリケーションのデータ管理が完璧になるんだろうとワクワク期待して先に進みます。
今日から「Core Data Stackの初期化」パートに入ります。
Core Data Stackは、フレームワークオブジェクトの集まりで、Core Dataの初期化の一部にアクセスして、アプリケーションのオブジェクトと外部のデータストアの間を取り持ちます。Core Data Stackは外部データとのやり取りを全て引き受けるので、アプリケーションは自分のビジネスロジックに集中することができます。このスタックは、3つの重要なオブジェクトで構成されています。
- Managed Object Context(NSManagedObjectContext)
- Persistent Store Coordinator(NSPersistentStoreCoordinator)
- Managed Object Model(NSManagedObjectModel)
アプリケーションのデータにアクセスする前にCore Data Stackの初期化が必要です。スタックの初期化は、Core Dataのデータ要求やデータ作成の準備になります。
ここにサンプルの初期化ステップが紹介されています。
import UIKit import CoreData class DataController: NSObject { var managedObjectContext: NSManagedObjectContext init() { // ここで使うリソース名は、プロジェクトでできた //「xcdatamodeld」ファイルと同じ名前になります。 // ここではサンプルとして「DataModel」になってます。 guard let modelURL = NSBundle.mainBundle().URLForResource("DataModel", withExtension:"momd") else { fatalError("Error loading model from bundle") } // ここでアプリケーションのManaged Object Modelが // 作られます。もしできなければ致命的エラーです。 guard let mom = NSManagedObjectModel(contentsOfURL: modelURL) else { fatalError("Error initializing mom from: \(modelURL)") } let psc = NSPersistentStoreCoordinator(managedObjectModel: mom) self.managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) self.managedObjectContext.persistentStoreCoordinator = psc // 別スレッドでダウンロードさせるというのも今は理解できます。 dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { let urls = NSFileManager.defaultManager().URLsForDirectory( .DocumentDirectory, inDomains: .UserDomainMask) let docURL = urls[urls.endIndex-1] // アプリケーションがCore Data 保存ファイルを保存する // ためのディレクトリを設定します。 // このケースでは、「DataModel sqlite」という名前を使って // アプリケーションのドキュメントディレクトリーに作ります。 let storeURL = docURL.URLByAppendingPathComponent("DataModel.sqlite") do { try psc.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil) } catch { fatalError("Error migrating store: \(error)") } } } }
今日はここまで。
-
-
-
- -
-
-
明けましておめでとうございます!
今年も初心者オヤジのプログラミング奮闘記を続けます。初心者50オヤジがアプリを作る事ができるのか? 温かく見守ってくださいm(_ _)m
早速今日の課題です。
まだCore Data Stackの説明が終わっていません。3つの構成要素の説明です。
まずNSManagedObjectModelからです。
NSManagedObjectModelのインスタンスとは、Core Data Stackからアクセスがあるデータを意味します。Core Data Stackが作られる間にNSManagedObjectModel(一般的に「mom」と名前をつけます)は、スタック作成の最初のステップとしてメモリーに展開されます。昨日のサンプルコードの中で、「mom」が、既知のファイル名(昨日の例では、「DataModel.momd」というファイル名になります)を使って、メインのアプリケーションバンドルからNSURLクラスから抽出しています。一度NSManagedObjectModelオブジェyクトが初期化されると、NSPersistentStoreCoordinatorオブジェクトの作成ががされます。
次はNSPersistenSotreCoorinatorです。
NSPersistentStoreCoordinatorは、Core Data Stackの中心的な役割を担います。オブジェクトで定義されているエンティティのインスタンス化をコントロールしているため、モデルの中のエンティティの新しいインスタンス化や、保持データ(NSPersittentSotre)から存在するエンティティを取りだしてきます。保存データは、メモリー上であったりディスク上だったりします。アプリケーションの構造によりますが、NSPersitentStoreCoordinatorを使って、複数のNSPersistentStoreを協調的に使うということもあります。しかし、これは一般的ではないようです。
NSManagedObjectModelがデータ構造を定義して、NSPersistentStoreCoordinatorが保存しているデータからオブジェクトを取りだし、NSManagedObjectContextの求めに応じてオブジェクトの出し入れをする。また一貫性のとれているデータが、NSManagedObjectModelで定義されているデータと同様かどうかの確認もします。
最後のNSManagedObjectContextは明日です。
-
-
-
- -
-
-
Core Data Stackの3つの構成要素の最後のパート、NSManagedObjectContextです。
NSManagedObjectContext
マネージド・オブジェクト・コンテクスト(NSManagedObjectContext)は、アプリケーションが最も関係するオブジェクトのことで、アプリケーションに曝されます。マネージド・オブジェクト・コンテクストを、インテリジェント・スクラッチパッドに例えてみましょう。保存ファイルからオブジェクトを取り出すときに、オブジェクト・グラフ(このグラフという概念をまだ理解できていません(^^;)を形成しているスクラッチパットにコピーを一時的に写します。このスクラッチパッド上のオブジェクトを好きなように修正できます。変更の保存を実行しない限り、保存ファイルにあるオブジェクトに変更は加えられません。
アプリケーションが扱うオブジェクトを全てManaged Object Contextを使って登録しなければいけません。コンテクストを使ってオブジェクトをオブジェクトグラフ*21に加えたり、オブジェクトグラフから外したりします。コンテキストは、オブジェクトの変化、属性の変化やオブジェクト間の関係性の変化を記録します。この変化の記録をすることで、undoやredoをサポートできるということです。自分でメメント・パターンを使って実装しなくていいんです!便利ですね。オブジェクト間の関係性やオブジェクトブラフの正確性は担保されているのでCore Data最高です*22。
変更した後にsaveを選択した時に、コンテキストはオブジェクトを有効にします。そうなれば、変更は保存データに書き込まれたり、オブジェクトの新しいレコードが加わったり、レコードが削除されたりします。
Core Dataが無ければ、データをアーカイブしたらいアンアーカイブするメソッド、モデルオブジェクトの変化を記録して、undoを実行するためにundoマネージャーを自分で操作しなければなりません。Core Dataフレームワークを使うと、こういう機能性をこのNSManagedObjectContextを通して、自動的に利用することができます。
いやあ便利なフレームワークですね。このフレームワークを使えるようにするのは私のアプリで必須です。わけ解らない状態ですが頑張って勉強します。
-
-
-
- -
-
-
Core Dataの勉強に取り組んで10日も過ぎてますが、いまだに概要も掴めていません。Core Dataで挫折する人が多いという話を聞きますが、それは関連するクラスが多く、その関係性の理解が難しいからでしょう。OOPプログラミングとデーターベース構築の両方の知識が必要なため敷居を高くしているようです。そういう私もデーターベースに関する知識が0ですけど(^^;)
取りあえず関連するクラスの説明を分からなくても読んでしまいます。
Managed Objectの作成と保存
Managed Object Modelを定義して、Core Data Stackを初期化したので、データを保存するためにManaged Objectを作る用意ができました。
さあ、Managed Objectを作るステップです。
NSManagedObjectインスタンスは、Core Data Modelオブジェクトの基本的な機能を備えています。NSManagedObjectインスタンスは2つの要素を必要とします:NSEntityDescriptionインスタンスとNSManagedObjectContextインスタンスです。エンティティ・ディスクリプションは、オブジェyクトを意味するエンティティの「名前」とその「属性」や「関係性」を含んでいます。コンテクストは昨日説明したようにスクラッチパッドを意味して、この上でManaged Objectを作り、オブジェクトの変化、関係性の変化を記録します。
次に例文を紹介します。
let employee = NSEntityDescription.insertNewObjectForEntityForName("Employee", inManagedObjectContext: self.managedObjectContext) as! AAAEmployeeMO
NSEntityDescriptionのクラスメソッドを使って、String型の変数からエンティティの名前を設定して、後でNSManagedObjectが関係するNSManagedObjectContesxtへの参照を可能にさせます。この例文では、AAAEmplyeeMOクラスを返しています。
次に重要なのはNSManagedObjectのサブクラスを作ってManagedObjectを作る事です。コードを間補するためのエンティティを自分で定義したり、簡便なメソッドを加えたりできるからです。
NSManagedObjectのサブクラス化は簡単です。XcodeのCore Data Model Editorを開いて、エンティティを選びます。Data Modelインスペクター、エンティティ・パン内のクラス・フィールドに「名前」を加える。Xcodeで「AAAEmployee」というサブクラスをファイルで定義します。
import UIKit import CoreData import Foundation class AAAEmployeeMO: NSManagedObject { @NSManaged var name: String? }
ここで重要なのが、「@NSManaged」という枕詞のついたプロパティ宣言です。このタグがあればNSManagedObjectクラスから継承されていることが明示されて、NSMangaedObjectクラスのメソッドが使えるということのようです。こういう変則ルールはObjecttive-CのAPI利用時に出現します。Swift3.0がリリースされる頃に消えていくんですかね。
NSManagedObjectインスタンスの保存に関して
NSManagedObjectインスタンスは、データの永続性に関して何ら関与しません。要するにスクラッチパッドにあたるNSManagedObjectContextインスタンス上でデータが展開されているからです。永続的に保存をさせたい時は、必ず明示的にsaveをしてやる必要があります。
do { try self.managedObjectContext.save() } catch { fatalError("Failure to save context: \(error)") }
ということでNSManagedObjectの説明は終わります。
-
-
-
- -
-
-
さて、今日からリレーショナルデーターベースを扱うクラスの勉強に入ります。
NSFetchRequestクラスです。このクラスがCore Dataフレームワークで最もパワフルな機能を発揮するクラスです。大規模なCore Dataクラスから素早くオブジェクトを取り出す(データーベース検索機能)からです。
まずNSFetchRequestインスタンスを作成しなければなりません。これで保存されたデータ、NSManagedObjectインスタンスを取ってくる訳です。
次の例では、エンティティのタイプ以外のデータを戻り値として返していません。NSManagedObjectContextのメソッド、executeFetchRequestを使ってデータを取り出すのですが、このメソッドもエラーへのポインターを渡すので*23、受け取るための「do-catch」が必要になります。
let moc = self.managedObjectContext let employeesFetch = NSFetchRequest(entityName: "Employee") do { let fetchedEmployees = try moc.executeFetchRequest(employeesFetch) as! [AAAEmployeeMO] } catch { fatalError("Failed to fetch employees: \(error)") }
executeFetchRequestメソッドで、NSArrayを返します。これは次のフィルタリングでNSDictionaryを返すのと異なるため注意が必要です。
そして検索候補を減らすためにフィルタリングの機能があります。これがNSFetchRequestの真骨頂なのかもしれません。まず最初にNSPredicateを使って、絞り込み検索をします。ここで戻す値がNSManagedObjectではなくNSDictionaryというところを注意しないといけません。
例えば、Emplyoeeオブジェクトを、「Trevor」というfirstNameで絞り込むなら次のようになります。
let firstName = "Trevor" fetchRequest.predicate = NSPredicate(format: "firstName == %@", firstName)
今日はここまで。
-
-
-
- -
-
-
*1:このcycleって今のところ何のことかさっぱり分かりません
*2:グラフもさっぱりです
*3:パーシステントって、持続的に保持ってことですよね。ここでの意味がイマイチ分かってないです
*4:このモデル・レイヤーって何の事でしょう。こういうjargonが分かっていないので辛いですね。Core Dataを理解するためにこれから勉強します
*5:非常に難しい表現です。何を言っているのか今の所不明。こういう内容も実際のコーディングをみないと理解できません。
*6:全く理解でき兄表現です。
*7:ちんぷんかんぷん!
*8:笑えるぐらい??? 理解できる日が来るのだろうか?
*9:自分で何を言っているのさっぱり分かってません。分からず訳すとこうなるんですよ。理解した後に読み直すと大笑いでしょう。
*10:分かってない!
*11:完全に翻訳が間違ってるけど何が違うのかも分からないレベル...
*12:?????????理解できる日が来るのだろうか
*13:イマイチ和からないが、私が最も知りたい機能の1つだと思います。
*14:??? もういいでしょう。分かってません
*15:翻訳になってません。
*17:プログラミング用語で「マップ(map)」ってどういう意味なんでしょう?イマイチ良くわからないので変な日本語になります。理解できたときに書き直します。
*18:私はプロジェクト名にCoreDataDemoにしました
*19:最初何のことかさっぱり分からなかったです。初めて聞いたIT用語だったので、グーグルで検索してちょっと調べました。プログラミングの作法だけでなくデータベースの仕様にも色々作法があるんですね。本当にプログラマーの皆さんの知識の多さに感服します。データベースの扱いも勉強しないといけませんね。ビジネスロジックは、「仮想フィールドの設定」、「入力値の妥当性チェック」、「関連テーブルの操作」などデータベースの扱いに対するルールです。
*20:何のことか、今のところさっぱり分かりません(^_^;)。
*21:グラフというのはデーターベース用語なんでしょうね。こういうJARGONの理解ができていません。
*22:こんなこと言ってますが、よく分かっていません(^^;
*23:これがthrowですね