はじめに
Core Dataを使って、情報を保存する処理と取得する処理について簡単に実装してみたので記事に残します。
開発環境
手順
"Use Core Data"にチェックを入れる。
Data Modelファイルがプロジェクトに追加される。
また、AppDelegate.swiftに永続コンテナ(NSPersistentContainer)を初期化する以下の処理が追加される。
AppDelegate.swift
// CoreDataを初期化 // Lazy Stored Propertyによって使用されるまで初期化を遅らせている lazy var persistentContainer: NSPersistentContainer = { // Data Modelファイル名をイニシャライザに渡してコンテナをインスタンス化する let container = NSPersistentContainer(name: "CoreDataSample") // StoreCoordinatorを読み込む // 存在しない場合はStoreを作成する container.loadPersistentStores(completionHandler: { (storeDescription, error) in if let error = error as NSError? { // 書き込み許可されていない場合、Storeにアクセスできない、デバイスの容量不足などの際にエラーになる fatalError("Unresolved error \(error), \(error.userInfo)") } }) return container }()
永続コンテナの初期化が完了すると、コンテナはモデル(NSManagedObjectModel)やコンテキスト(NSManagedObjectContext)、ストアコーディネータ(NSPersistentStoreCoordinator)のインスタンスへの参照を保持する。
※参考:
https://developer.apple.com/documentation/coredata/setting_up_a_core_data_stack
ルートビューコントローラー(今回はViewControllerクラス)でCoreDataをインポートし、永続コンテナへの参照を保持する変数を追加。
ViewController.swift
import UIKit import CoreData class ViewController: UIViewController { // 永続コンテナへの参照を保持する変数 var container: NSPersistentContainer! override func viewDidLoad() { super.viewDidLoad() // 永続コンテナのnilチェック guard container != nil else { fatalError("This view needs a persistent container.") } } }
Main.storyboardでStoryboard IDを設定。今回はStoryboard IDを"ViewController"とする。
Main.storyboard
SceneDelegate.swiftのscene(_:willConnectTo:options:)で、ルートビューコントローラに永続コンテナに設定する。
SceneDelegate.swift
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let scene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: scene) self.window = window
let storyboard = UIStoryboard(name: "Main", bundle: nil) guard let rootVC = storyboard.instantiateViewController(identifier: "ViewController") as? ViewController else { return }
// ルートビューコントローラーに永続コンテナを設定する rootVC.container = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer self.window?.rootViewController = rootVC self.window?.makeKeyAndVisible() }
エンティティを追加する。
"+"を選択してAttributeを追加する。
今回は画像情報を扱うエンティティと仮定して、Attributeを以下のようにしてみる。
Main.storyboardに画像情報を保存するボタンとラベルに保存した情報を表示するためのボタンを追加。
Main.storyboard
それぞれの部品をカスタムクラスに関連づける。
InsertボタンとFetchボタンにそれぞれ画像の保存処理と取得処理を実装する。ViewController.swiftの全体のコードは以下。
実装後、ビルドしInsertボタンを選択→Fetchボタンを選択すると、ラベルに保存した情報が表示される。
ViewController.swift
import UIKit import CoreData class ViewController: UIViewController { // 永続コンテナへの参照を保持する変数 var container: NSPersistentContainer! // 取得した画像情報を保持する配列 var imageData: [NSManagedObject] = [] // 保存した画像情報を表示するラベル @IBOutlet weak var imageIdText: UILabel! @IBOutlet weak var imageURLText: UILabel! @IBOutlet weak var imageTagText: UILabel! @IBOutlet weak var imageLikesText: UILabel! override func viewDidLoad() { super.viewDidLoad() // 永続コンテナのnilチェック guard container != nil else { fatalError("This view needs a persistent container.") } } // 画像情報を保存 @IBAction func insertImageData(_ sender: Any) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } let managedContext = appDelegate.persistentContainer.viewContext let entity = NSEntityDescription.entity(forEntityName: "Image", in: managedContext)! let image = NSManagedObject(entity: entity, insertInto: managedContext) // 保存する画像情報 image.setValue(1, forKey: "id") image.setValue("http://...", forKey: "imageURL") image.setValue("sea", forKey: "tag") image.setValue(5, forKey: "likes") appDelegate.saveContext() } // 保存した画像情報を画面に表示 @IBAction func fetchImageData(_ sender: Any) { guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return } let managedContext = appDelegate.persistentContainer.viewContext let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "Image") do { // 保存した画像情報を取得 imageData = try managedContext.fetch(fetchRequest) imageIdText.text = "id:\(String(describing: imageData[0].value(forKey: "id")!))" imageURLText.text = "id:\(String(describing: imageData[0].value(forKey: "imageURL")!))" imageTagText.text = "id:\(String(describing: imageData[0].value(forKey: "tag")!))" imageLikesText.text = "id:\(String(describing: imageData[0].value(forKey: "likes")!))" } catch let error as NSError { print("Could not fetch. \(error), \(error.userInfo)") } } }
おわりに
他の記事にもある通り、Appleのドキュメントが古かったり、記事が少なめだったこともあって意外に時間がかかってしまいました・・・。
Core Dataについて、簡単ではありますが試すことができたので、Firebase Cloud Firestoreも機会があれば試してみたいと思います。
※Realm Databaseはだいぶ前に触って既に記事にしていました(忘れてた笑)
【Swift】Realmを触ってみる(レコードの書き込み/取得) - Swift・iOS
参考
- https://developer.apple.com/documentation/coredata
-
https://developer.apple.com/documentation/coredata/creating_a_core_data_model
-
https://developer.apple.com/documentation/coredata/setting_up_a_core_data_stack
-
https://developer.apple.com/documentation/uikit/uiscenedelegate/3197914-scene
- core data - Setting Up CoreData with SceneDelegate - unknown identifier 'window' error - iOS 13 onwards - Stack Overflow
- SwiftでCoreData - Qiita
- 【初心者向け】Core Dataの使い方と説明swift3.0 - Qiita