Group Group Group Group Group Group Group Group Group

Integrating CoreData with new SwiftUI App protocol

Hi,

Xcode 12 (beta) has some new templated code when creating a new App supporting CoreData.
It would be good if your book finally addressed working SwiftUI now that it is the default for new projects.

Example of topic to cover:

  1. working with a struct (as shown below) or class for the CoreDataStack? (particularly in MVVM setup where a viewModel will be handling CoreData interactions instead of leveraging property wrappers inside Views)
  2. using @StateObject vs @ObservedObject for a viewModel var in a View
  3. using property wrappers directly in a View vs the MVVM handling of data operations in a separate store (aka ViewModel class, or whatever people choose to call it).

Thanks!

1 Like

— PersistenceController.swift

import CoreData

struct PersistenceController {
static let shared = PersistenceController()

static var preview: PersistenceController = {
    let result = PersistenceController(inMemory: true)
    let viewContext = result.container.viewContext
    for _ in 0..<10 {
        let newItem = Item(context: viewContext)
        newItem.timestamp = Date()
    }
    do {
        try viewContext.save()
    } catch {
        // Replace this implementation with code to handle the error appropriately.
        // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
    return result
}()

let container: NSPersistentContainer

init(inMemory: Bool = false) {
    container = NSPersistentContainer(name: "test2CoreData")
    if inMemory {
        container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
    }
    container.loadPersistentStores(completionHandler: { (storeDescription, error) in
        if let error = error as NSError? {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.

            /*
            Typical reasons for an error here include:
            * The parent directory does not exist, cannot be created, or disallows writing.
            * The persistent store is not accessible, due to permissions or data protection when the device is locked.
            * The device is out of space.
            * The store could not be migrated to the current model version.
            Check the error message to determine what the actual problem was.
            */
            fatalError("Unresolved error \(error), \(error.userInfo)")
        }
    })
}

}

---- MyApp.swift

import SwiftUI

@main
struct myApp: App {
let persistenceController = PersistenceController.shared

var body: some Scene {
    WindowGroup {
        ContentView()
            .environment(\.managedObjectContext, persistenceController.container.viewContext)
    }
}

}

— ContentView

import SwiftUI
import CoreData

struct ContentView: View {
@Environment(.managedObjectContext) private var viewContext

@FetchRequest(
    sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
    animation: .default)
private var items: FetchedResults<Item>

var body: some View {
    List {
        ForEach(items) { item in
            Text("Item at \(item.timestamp!, formatter: itemFormatter)")
        }
        .onDelete(perform: deleteItems)
    }
    .toolbar {
        #if os(iOS)
        EditButton()
        #endif

        Button(action: addItem) {
            Label("Add Item", systemImage: "plus")
        }
    }
}

private func addItem() {
    withAnimation {
        let newItem = Item(context: viewContext)
        newItem.timestamp = Date()

        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

private func deleteItems(offsets: IndexSet) {
    withAnimation {
        offsets.map { items[$0] }.forEach(viewContext.delete)

        do {
            try viewContext.save()
        } catch {
            // Replace this implementation with code to handle the error appropriately.
            // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
    }
}

}

private let itemFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateStyle = .short
formatter.timeStyle = .medium
return formatter
}()

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