Swift で Core Dataのリレーションシップ（1対多）を試す

Swift のプロジェクトで CoreData でも1対多（リレーションシップ：relationship）の関係を持つデータを用意したいと思うことがあります。

# CoreDataを操作するためだけのプロジェクトを用意

今回は（も？）、CoreData の操作だけのプロジェクトを用意しました（本当は、Playground で使えたら面白いんだけれど…）。

# リレーションシップ（Relationship）の話に 登場するクラス

クラスの関係 としてBlog は、複数の Article を持つ状態になります

* Blog
* Article

# リレーションシップ（1対多）を設定する

## Blog側のリレーションシップ設定

![スクリーンショット 2015-05-06 15.07.56.png](https://qiita-image-store.s3.amazonaws.com/0/21321/b11e013c-1a64-5f8c-32a2-b47070ff724c.png)

## Article側のリレーションシップ設定

![スクリーンショット 2015-05-11 22.21.38.png](https://qiita-image-store.s3.amazonaws.com/0/21321/2b97c5a6-5f9a-f2f2-9912-78bc182e9816.png)

## クラスの設定

![スクリーンショット 2015-05-11 22.25.23.png](https://qiita-image-store.s3.amazonaws.com/0/21321/792604cb-dd53-1fe3-ba6c-ff91b948fe0f.png)


# 各クラスのファイルを書き出す

* 「Editor」 → 「Create NSManagedObject Subclass」
* Swift で書き出す

## Swift 1.2 から？

どうもリレーションを設定してClassを書き出した場合、Objective-Cだと色んなfunction も書き出してくれているらしいですが、Swift 1.2 の環境では再現できませんでした（何か足りない可能性もあります…）

今回は、Blogが複数のArticleを持つ関係なので、Blogについて function を追加して対応しました。

## Blog.swif

* 追加したのは addArticles() です
* Blog.articles は、もともとはNSSetでしたが変更しました
* NSSet については、Swift 1.2 で変更された箇所らしいです
 * NSSet なんですが、Array とどう違うの？と納得できていない箇所だったり

```swift:Blog.swif
import Foundation
import CoreData

class Blog: NSManagedObject {

    @NSManaged var title: String
    @NSManaged var uuid: Int16
    @NSManaged var articles: NSMutableSet
   
    func name() -> String{
        return self.title
    }
   
    func addArticles(value:Article) {
        var items = self.mutableSetValueForKey("articles")
        items.addObject(value)
    }
}
```

## Article.swif

* 親となるBlogについて、関連が書かれています
* Blog はnil になることもあるので、nil の許可をしています
* 余談）こうしてみると、Active Record っぽい

```swift:Article.swif
import Foundation
import CoreData

class Article: NSManagedObject {
    @NSManaged var title: String
    @NSManaged var body: String
    @NSManaged var blog: Blog!

}
```

# 実行・制作環境

* Xcode 6.3.1
* Swift 1.2
* iOS 8.2

# 全体のソース

## データーの登録

* 「[Swift で CoreData の必要な初期データー（マスターデータ）を用意する](http://qiita.com/matsuhisa@github/items/741d7b1c2541ea6fc68d)」でデーターを登録しています

```swift:ViewController.swift
import UIKit
import CoreData

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        self.initMasterArticleData()
        self.initMasterBlogData()

        println("<--------------->")
        println("")
        //self.readArticleData()
        self.readBlogData()

        println("<--------------->")
        println("")

        self.updateBlog(items: self.readBlogByUuid(1))
        //var results = self.readArticleByTitle()
        //self.updateArticle(items: results)
        //self.readArticleByTitle()

        println("<--------------->")
        println("")

        //self.readArticleData()
        self.readBlogData()

        println("<--------------->")
        println("")

    }
   
    func updateBlog(items:NSArray = [Blog]()) {
        if items.count > 0 {
            println("Yes Blog Data")
           
            let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let blogContext:NSManagedObjectContext = app.managedObjectContext!
            var error: NSError?
           
            var articles = self.readArticleByTitle()
           
            println("updateBlog -----------")
            for item in items {
                var blog = item as! Blog
               
                for item_a in articles {
                    var article = item_a as! Article

                    //blog.addArticles(article)
                    //article.blog = blog
                    blog.title = "ブログの題名を変更してみた"
                    app.saveContext()

                }
               
            }
            println("----------- updateBlog")
           
        }else {
            println("No Blog Data")
        }
    }
   
    // データー読み込み：Blog
    func readBlogData() -> NSArray{
        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let blogContext:NSManagedObjectContext = app.managedObjectContext!
        let blogRequest: NSFetchRequest = NSFetchRequest(entityName: "Blog")
       
        var results: NSArray! = blogContext.executeFetchRequest(blogRequest, error: nil)
        for item in results {
            var blog = item as! Blog
            println("read Blog ------------")
            println(blog.name())
            println(blog.articles.count)
           
            for item_a in blog.articles {
                println("blog.articles ----------")
                var article = item_a as! Article
                println(article.title)
                println("---------- blog.articles")
            }
           
            println("------------ read Blog")
        }
        return results
    }

    // データー読み込み：Article
    func readArticleData() -> NSArray{
        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let articleContext:NSManagedObjectContext = app.managedObjectContext!
        let articleRequest: NSFetchRequest = NSFetchRequest(entityName: "Article")
       
        var results: NSArray! = articleContext.executeFetchRequest(articleRequest, error: nil)
        for item in results {
            var article = item as! Article
            println("read.article ------------")
            println(article.title)
           
            if article.blog != nil {
                println("article.blog ----------")
                println(article.blog.title)
                println("---------- article.blog")
            }
           
            println("------------ read.article")
        }
        return results
    }
   
    // データー読み込み：
    func readBlogByUuid(uuid:NSNumber) ->NSArray {
        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let blogContext:NSManagedObjectContext = app.managedObjectContext!
       
        let blogRequest: NSFetchRequest = NSFetchRequest(entityName: "Blog")

        let predicate = NSPredicate(format: "%K == %@", "uuid", uuid)
        blogRequest.predicate = predicate
       
        var error: NSError?
        var results: NSArray = []
       
        results = blogContext.executeFetchRequest(blogRequest, error: nil)!
       
        for item in results {
            var blog = item as! Blog
            println("readBlogByUuid ------------")
            println(blog.title)
            println("------------ readBlogByUuid")
        }
       
        return results
    }
   
    // データー読み込み：Articleを題名指定で
    func readArticleByTitle(title:String? = "題名です") ->NSArray{
        println("readArticleByTitle ------------")
        println(title)

        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let articleContext:NSManagedObjectContext = app.managedObjectContext!

        let articleRequest: NSFetchRequest = NSFetchRequest(entityName: "Article")

        // http://qiita.com/yusuga/items/8fd531ebd8f5e72bb97b
        // LIKE句 じゃなくて CONTAINS を使うと良いみたい
        let predicate = NSPredicate(format: "%K CONTAINS[cd] %@", "title", title!)
        articleRequest.predicate = predicate

        var error: NSError?
        var results: NSArray = []

        results = articleContext.executeFetchRequest(articleRequest, error: nil)!

        for item in results {
            var article = item as! Article
            println("readArticleByTitle ------------")
            println(article.title)
            println("------------ readArticleByTitle")
        }
       
        println("------------ readArticleByTitle")
        return results
    }

    // データー更新：Article
    func updateArticle(items:NSArray = [Article]()){
        if items.count > 0 {
            println("Yes Data ------------")
           
            let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            let articleContext:NSManagedObjectContext = app.managedObjectContext!
            var error: NSError?
           
            for item in items {
                var article = item as! Article
                println(article.title)
                article.title = "「題名です」を書き換え書き換え"
                app.saveContext()
            }

            println("------------ Yes Data")
        }else {
            println("No Data")
        }
    }



    //
    func initMasterBlogData() {
        let blogPath:NSString = NSBundle.mainBundle().pathForResource("BlogData", ofType: "plist")!
        var blogDataDictionary:NSDictionary = NSDictionary(contentsOfFile: blogPath as String)!
       
        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let blogContext:NSManagedObjectContext = app.managedObjectContext!


        for(var i = 1; i<=blogDataDictionary.count; i++) {
            let index_name: String = "Blog" + String(i)
            var item: AnyObject = blogDataDictionary[index_name]!

           
            let blogEntity: NSEntityDescription! = NSEntityDescription.entityForName(
                "Blog",
                inManagedObjectContext: blogContext
            )
            var new_data  = NSManagedObject(entity: blogEntity, insertIntoManagedObjectContext: blogContext)
            new_data.setValue(item["title"] as! String, forKey: "title")
            new_data.setValue(item["uuid"] as! NSNumber, forKey: "uuid")
            var error: NSError?
            blogContext.save(&error)
        }
    }

    func initMasterArticleData() {
        let articlePath:NSString = NSBundle.mainBundle().pathForResource("ArticleData", ofType: "plist")!
        var articleDataDictionary:NSDictionary = NSDictionary(contentsOfFile: articlePath as String)!
       
        let app: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
        let articleContext:NSManagedObjectContext = app.managedObjectContext!
       
       
        for(var i = 1; i<=articleDataDictionary.count; i++) {
            let index_name: String = "Article" + String(i)
            var item: AnyObject = articleDataDictionary[index_name]!
           
            let articleEntity: NSEntityDescription! = NSEntityDescription.entityForName(
                "Article",
                inManagedObjectContext: articleContext
            )
            var new_data  = NSManagedObject(entity: articleEntity, insertIntoManagedObjectContext: articleContext)
            new_data.setValue(item["title"] as! String, forKey: "title")
            new_data.setValue(item["body"] as! String, forKey: "body")
           
            var error: NSError?
            articleContext.save(&error)
        }
    }
   
    //
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}

```