3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[SwiftUI] CoreDataのDataModelをイニシャライザの引数に受け取るViewのCanvas Previewを上手く書く

Last updated at Posted at 2021-02-15

概要

例えば、以下の様なCore DataのData Modelを想定します。

extension ToDo {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<ToDo> {
        return NSFetchRequest<ToDo>(entityName: "ToDo")
    }
    
    @NSManaged public var id: UUID?
    @NSManaged public var title: String?
    @NSManaged public var content: String?
}

このData Modelをイニシャライザの引数に取るViewを適当に以下の様に書きます。

import SwiftUI

struct ContentView: View {
    var todo: ToDo
    var body: some View {
        VStack(alignment: .leading) {
            Text(todo.title!)
            Text(todo.content!)
        }
    }
}

この時、このContentViewのCanvas Previewを表示するコードをどう書けば上手くいくのか悩みました。

色々ググった結果、以下の記事を見つけました。

解決

前提として、CoreDataのPersistenceController周りのコードを一つの構造体にまとめておきます。

import CoreData

struct PersistenceController {
    static let shared = PersistenceController()

    let container: NSPersistentCloudKitContainer

    init() {
        container = NSPersistentCloudKitContainer(name: "ToDo")
        container.loadPersistentStores(completionHandler: { _, error in
            if let error = error as NSError? {
                print(error.localizedDescription)
            }
        })
    }

    static func saveContext() {
        let context = Self.shared.container.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

そして、以下の様なCanvas PreviewのWrapperになるコードを書きます。

import CoreData
import SwiftUI

struct PreviewWrapper<Content: View>: View {
    let content: (NSManagedObjectContext, ToDo) -> Content

    var body: some View {
        let context = PersistenceController.shared.container.viewContext
        let todo = ToDo(context: context)
        
        todo.id = UUID()
        todo.title = "宿題"
        todo.content = "算数のドリル10ページ、英単語の書取りの練習100語"

        return self.content(context, todo)
    }

    init(@ViewBuilder content: @escaping (NSManagedObjectContext, ToDo) -> Content) {
        self.content = content
    }
}

こうすることで、肝心のCanvas Previewのコードが以下の様に書けます。

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        PreviewWrapper { _, todo in
            ContentView(todo: todo)
        }
    }
}

Canvas Previewは以下の様になります。

スクリーンショット 2021-02-15 7.30.35(2).png

Listの場合は?

例えば、次の様にイニシャライザに[ToDo]型の引数を取るListを表示したい時はどうでしょうか。
コード内のContentViewは前の例のContentViewをそのまま使用しています。

import SwiftUI

struct ContentListView: View {
    
    var todos: [ToDo]
    
    var body: some View {
        List {
            ForEach(todos) { todo in
                ContentView(todo: todo)
            }
        }
    }
}

この場合は一例として次の様にPreviewのWrapperを作ることで解決できます。
[ToDo]型の配列の長さは適当に10にしています。

struct PreviewListWrapper<Content: View>: View {
    let content: (NSManagedObjectContext, [ToDo]) -> Content

    var body: some View {
        let context = PersistenceController.shared.container.viewContext
        let todo = ToDo(context: context)
        
        let todos: [ToDo] = ([Int])(0 ..< 10).map { _ in
            todo.id = UUID()
            todo.title = "宿題"
            todo.content = "算数のドリル10ページ、英単語の書取りの練習100語"
            
            return todo
        }

        return self.content(context, todos)
    }

    init(@ViewBuilder content: @escaping (NSManagedObjectContext, [ToDo]) -> Content) {
        self.content = content
    }
}

Canvas Previewのコード:

struct ContentListView_Previews: PreviewProvider {
    static var previews: some View {
        PreviewListWrapper { _, todos in
            ContentListView(todos: todos)
        }
    }
}

Canvas Preview:

スクリーンショット 2021-02-15 7.40.32(2).png

3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?