ユーザーに属するタスクを取得して画面に一覧表示する

## 概要
CoreDataを使って以下のような関係を作りました。
`User : Task = 1 : n`
あるユーザーに属するタスクの一覧を取得するときは `user.tasks` みたいな感じで取得できると思ってたのですが、取得できませんでした。
今回の記事でユーザーに属するタスクを取得して画面に表示する方法を紹介します。

## 環境
`macOS Monterey 12.1`
`Xcode 13.2.1`

## 手順
1. CoreDataを使って`User : Task = 1 : n`を表現する(記事の主題から外れるため詳しい説明は割愛)
2. ユーザーに属するタスクの一覧を取得する
3. 取得したタスクの一覧を画面に表示する

### 1. `User : Task = 1 : n`を表現する

https://capibara1969.com/3367/

↑こちらの記事を参考にしました。
コードは以下です。
記事の内容を参考にしながら自分は以下のような状態になりました。

**User Entity**
![スクリーンショット 2022-01-01 0.57.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/199f4521-cbc6-09d2-b220-8aada890e7b4.png)

**Task Entity**
![スクリーンショット 2022-01-01 0.58.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/1496eba0-a2d6-d503-1e03-9eaad8f3c488.png)

サンプルデータを準備します。

```swift:RegistSampleData.swift
import CoreData

func registSampleData(context: NSManagedObjectContext) {
    let userSeeds: [(id: UUID, name: String)] = [
        (UUID(), "User A"),
        (UUID(), "User B"),
        (UUID(), "User C")
    ]
    
    let taskSeeds: [(id: UUID, title: String, userName: String)] = [
        (UUID(), "Task A", "User A"),
        (UUID(), "Task B", "User B"),
        (UUID(), "Task C", "User A"),
        (UUID(), "Task D", "User C"),
        (UUID(), "Task E", "User A"),
        (UUID(), "Task F", "User B")
    ]
    
    let fetchRequestUser = NSFetchRequest<NSFetchRequestResult>()
    fetchRequestUser.entity = User.entity()
    let users = try? context.fetch(fetchRequestUser) as? [User]
    for user in users! {
        context.delete(user)
    }
    
    let fetchRequestTask = NSFetchRequest<NSFetchRequestResult>()
    fetchRequestTask.entity = Task.entity()
    let tasks = try? context.fetch(fetchRequestTask) as? [Task]
    for task in tasks! {
        context.delete(task)
    }
    
    for user in userSeeds {
        let newUser = User(context: context)
        newUser.id = user.id
        newUser.name = user.name
    }
    
    for task in taskSeeds {
        let newTask = Task(context: context)
        newTask.id = task.id
        newTask.title = task.title
        
        fetchRequestUser.predicate = NSPredicate(format: "name = %@", task.userName)
        let users = try? context.fetch(fetchRequestUser) as? [User]
        newTask.user = users![0]
    }
    
    try? context.save()
}
```

ユーザーの一覧を取得してテーブルを表示します。

```swift:ContentView.swift
import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var context
    @FetchRequest(entity: User.entity(), sortDescriptors: []) private var users: FetchedResults<User>
    
    var body: some View {
        List {
            ForEach(users) { user in
                Section(content: {
                    Text(String(user.tasks!.count))
                }, header: {
                    Text(user.name!)
                })
            }
        }
        .onAppear {
            registSampleData(context: context)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}
```

現状画像のような表示になると思います。
ユーザーの名前と属するタスクの数を表示しています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/d9a92d07-2ada-0f0c-3546-fdb1133c62cf.png" width="300" />

### 2. ユーザーに属するタスクを取得する

ユーザーに属するタスクを取得するためには `user.tasks` という記述が必要なのですが、
戻り値が`NSSet?`となっています。
これを以下のように変換して`[Task]`を取得できるようにします。
`NSSet? -> Set<Task>? -> [Task]`

コードは以下です。

```swift
let taskSet = user.tasks! as? Set<Task> // NSSet? -> Set<Task>?
let tasks = taskSet!.map { task in
    return task
} // Set<Task>? -> [Task]
```

### 3. タスクの一覧を画面に表示する
上記で紹介したコードをContentView.swiftに組み込みます。

```swift:ContentView.swift
import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) private var context
    @FetchRequest(entity: User.entity(), sortDescriptors: []) private var users: FetchedResults<User>
    
    var body: some View {
        List {
            ForEach(users) { user in
                Section(content: {
                    let taskSet = user.tasks! as? Set<Task>
                    let tasks = taskSet!.map { task in
                        return task
                    }
                    ForEach(tasks) { task in
                        Text(task.title!)
                    }
                }, header: {
                    Text(user.name!)
                })
            }
        }
        .onAppear {
            registSampleData(context: context)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
    }
}
```

結果、画像のようにユーザーに属するタスクの一覧が表示されます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/d54a591f-1024-2059-e149-be61284d9117.png" width="300" />

## 参考リンク
https://www.hackingwithswift.com/books/ios-swiftui/one-to-many-relationships-with-core-data-swiftui-and-fetchrequest

https://capibara1969.com/3367/