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)
}
}

@cegrnd Thanks very much for your question!

This is definitely a good suggestion. We always try to ensure that we are as thorough as possible when we publish a book, and this definitely is something very important to include. As the SwiftUI technology is still evolving, eventually we’ll be able to provide better examples that are thorough, and comprehensive to give you the best learning experience.

Please stay tuned, and keep your eyes open for future updates!

@cegrnd Please check out the latest version of the book when you get a chance:

I hope it helps!

Maybe I’m missing something but the latest version of the book that you linked to says in the getting started section in Ch. 1 that all of the projects in this book use UIKit. Isn’t the OP asking if you could update the Interface to SwiftUI and the LifeCycle to SwiftUI App (both of which are now the default options in XCode)?