【Swift】Core Dataの使い方。複数の管理オブジェクトコンテキストを使う。(Swift 2.1、XCode 7.2)
複数の管理オブジェクトコンテキストとは
前回記事までは、1つの管理オブジェクトコンテキストを使用してデータを保存した。
外部ファイルに保存するタイミングがすべてのオブジェクトで同じで良ければいいが、あるエンティティは即時保存し、その他のエンティティは登録ボタンを押した段階で保存するなどエンティティごとに保存するタイミングを分けたいことがある。
そんなときは管理オブジェクトを2つ作り、管理オブジェクトコンテキストごとに保存するタイミングを変える。
複数の管理オブジェクトコンテキストを利用してみる
実際に、複数の管理オブジェクトコンテキストを利用してみよう。
以降の手順を行う前のXcodeプロジェクトをGitHubに置いたので、試してみる人はご利用されたし。
⇒「テスト用プロジェクト」
部活動と生徒を登録する画面があり、キーボードのReturnキーを押すと管理オブジェクトコンテキストにデータが追加される。その後、保存ボタンを押すと外部ファイルにデータが保存される。
部活動と生徒のオブジェクトは同じ管理オブジェクトコンテキストに追加しているので、保存ボタンを押すと両方が保存される。これから、部活動用と生徒用の管理オブジェクトコンテキストを分けて、保存ボタンを押した側の管理オブジェクトコンテキストのみ保存されるように変更する。
以下は変更前のプレイ動画
AppDelegate.swiftを以下のコードに変更する。
変更箇所は2つ目の管理オブジェクトコンテキストを作ったのと、アプリ終了時に保存されていないデータはそのまま保存しないようにした(青色箇所)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
// // AppDelegate.swift // import UIKit import CoreData @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { return true } func applicationWillResignActive(application: UIApplication) { } func applicationDidEnterBackground(application: UIApplication) { } func applicationWillEnterForeground(application: UIApplication) { } func applicationDidBecomeActive(application: UIApplication) { } func applicationWillTerminate(application: UIApplication) { //self.saveContext() } // MARK: - Core Data stack lazy var applicationDocumentsDirectory: NSURL = { let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask) return urls[urls.count-1] }() lazy var managedObjectModel: NSManagedObjectModel = { let modelURL = NSBundle.mainBundle().URLForResource("ManyContext", withExtension: "momd")! return NSManagedObjectModel(contentsOfURL: modelURL)! }() lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = { let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel) let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite") var failureReason = "There was an error creating or loading the application's saved data." do { try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil) } catch { // Report any error we got. var dict = [String: AnyObject]() dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" dict[NSLocalizedFailureReasonErrorKey] = failureReason dict[NSUnderlyingErrorKey] = error as NSError let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict) NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)") abort() } return coordinator }() lazy var managedObjectContext: NSManagedObjectContext = { let coordinator = self.persistentStoreCoordinator var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() //2つめの管理オブジェクトコンテキストを追加 lazy var managedObjectContext2: NSManagedObjectContext = { let coordinator = self.persistentStoreCoordinator var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType) managedObjectContext.persistentStoreCoordinator = coordinator return managedObjectContext }() } |
ViewController.swiftを以下のコードに変更する。
生徒のデータは生徒用の管理オブジェクトコンテキストに追加するようにした。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
// // ViewController.swift // import UIKit import CoreData class ViewController: UIViewController, UITextFieldDelegate { //管理オブジェクトコンテキスト var contextClub:NSManagedObjectContext! var contextStudent:NSManagedObjectContext! @IBOutlet weak var clubTextField: UITextField! @IBOutlet weak var teacherTextField: UITextField! @IBOutlet weak var studentTextField: UITextField! @IBOutlet weak var activityTextField: UITextField! //最初からあるメソッド override func viewDidLoad() { super.viewDidLoad() do { //部活動用と生徒用の管理オブジェクトコンテキストを取得する。 let applicationDelegate = UIApplication.sharedApplication().delegate as! AppDelegate contextClub = applicationDelegate.managedObjectContext contextStudent = applicationDelegate.managedObjectContext2 //Clubオブジェクトをフェッチしてラベルに設定する。 let fetchRequest = NSFetchRequest(entityName: "Club") let clubList = try contextClub.executeFetchRequest(fetchRequest) as! [Club] for club in clubList { clubTextField.text = club.clubName teacherTextField.text = club.teacher } //Studentオブジェクトをフェッチしてラベルに設定する。 let fetchStudent = NSFetchRequest(entityName: "Student") let studentList = try contextStudent.executeFetchRequest(fetchStudent) as! [Student] for student in studentList { studentTextField.text = student.name activityTextField.text = student.activity } //デリゲート先に自分を設定する。 clubTextField.delegate = self teacherTextField.delegate = self studentTextField.delegate = self activityTextField.delegate = self } catch { print(error) } } //Returnキー押下時の呼び出しメソッド func textFieldShouldReturn(textField: UITextField) -> Bool { //キーボードをしまう。 clubTextField.endEditing(true) teacherTextField.endEditing(true) studentTextField.endEditing(true) activityTextField.endEditing(true) do { //Clubオブジェクトを取得する。 let fetchRequest = NSFetchRequest(entityName: "Club") fetchRequest.predicate = NSPredicate(format:"clubName = %@", clubTextField.text!) let clubList = try contextClub.executeFetchRequest(fetchRequest) as! [Club] //Clubオブジェクトが取得できた場合はそのオブジェクトを使い、取得できなかった場合は新規追加する。 var club:Club if(clubList.count > 0) { club = clubList[0] } else { club = NSEntityDescription.insertNewObjectForEntityForName("Club", inManagedObjectContext: contextClub) as! Club } //属性値を変更する。 club.clubName = clubTextField.text club.teacher = teacherTextField.text //Studentオブジェクトを取得する。 let fetchStudent = NSFetchRequest(entityName: "Student") fetchStudent.predicate = NSPredicate(format:" name = %@", studentTextField.text!) let studentList = try contextStudent.executeFetchRequest(fetchStudent) as! [Student] //Studentオブジェクトが取得できた場合はそのオブジェクトを使い、取得できなかった場合は新規追加する。 var student:Student if(studentList.count > 0) { student = studentList[0] } else { student = NSEntityDescription.insertNewObjectForEntityForName("Student", inManagedObjectContext: contextStudent) as! Student } //属性値を変更する。 student.name = studentTextField.text student.activity = activityTextField.text } catch { print(error) } return true } //部活動の登録ボタン押下時の呼び出しメソッド @IBAction func pushClubButton(sender: UIButton) { do { //管理オブジェクトコンテキストの中身を保存する。 try contextClub.save() } catch { print(error) } } //生徒の登録ボタン押下時の呼び出しメソッド @IBAction func pushStudentButton(sender: UIButton) { do { //管理オブジェクトコンテキストの中身を保存する。 try contextStudent.save() } catch { print(error) } } } |
以下は実際のプレイ動画。部活動側の保存ボタンを押した場合は、部活動のデータのみ保存されるようになった。
逆に、生徒側の保存ボタンを押した場合は生徒のデータのみ保存される。