4

Core Data 在 SwiftUI 中的应用

 3 years ago
source link: https://hidandelion.com/287/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Core Data 简介


Core Data 使得开发者在使用极其少量代码的情况下将对象存储到 iOS 设备的存储介质上,也就是常说的序列化。开发者无需手动管理数据库或者手动序列化数据,就可以实现数据的存储和读取。Core Data 的优势十分明显,就是其方便快捷并且效率高,甚至数据还可以从云端同步至其它 iOS 设备,所有的优化过程都会自动完成。

Core Data 初始化


创建 Core Data 对象

若你在创建 Project 时选择启用 Core Data , Xcode 会自动创建一个 Core Data Model 文件(后缀为 xcdatamodeld ),在其中可以对 Core Data 对象的成员进行编辑,如果没有在创建App Project时选择启用Core Data,手动新建这个文件也可以达到相同效果。

成员编辑都是一些图形化的操作,点按 + 设定类型即可。这里值得注意的是右侧 Class 栏中的 Codegen 选项。

  • Manual/None – 手动编写类定义
  • Class Definition – 自动完成类定义,这意味着你无法在类定义中加入方法等额外内容
  • Category/Extension – 手动编写 Extension ,自动完成类成员定义,提供 Extension 以供加入方法等额外内容

看起来好像第三种的折中方案十分完美,实际过程中如果你真的要加入一些额外方法,这种方案使用起来并不是那么舒服。我推荐若是无需加入方法,使用第二种方案,若是你更喜欢自由或需加入额外方法等内容,第一种是最佳方案。

若是这里选择了第一种或第三种,那么你需要编写相应部分的内容,或者到菜单选择 Editor – CreateNSManagedObject Subclass 以让 Xcode 为你生成默认的内容。注意若是方案中包含自动管理的功能,你无法在编辑器里找到它们,但它们在编译时会自动加入,若是你重复写了自动管理的内容,将会导致编译无法通过。另外,手动编写类定义时必须确保其为NSManagedObject的子类。

添加初始化代码

若你在创建 Project 时选择启用 Core Data , Xcode 会自动创建一个 Persistence 文件并自动生成相关代码以用于初始化 Core Data,其包含以下代码。如果没有在创建App Project时选择启用Core Data,手动添加以下代码也可以达到相同效果。

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 {
            let nsError = error as NSError
            fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
        }
        return result
    }()

    let container: NSPersistentContainer

    init(inMemory: Bool = false) {
        container = NSPersistentContainer(name: "test")
        if inMemory {
            container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
        }
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
    }
}

这里定义的 PersistenceController 实例可以在 App 文件的 App 实例中创建以用于初始化 Core Data 。注意到这里分别创建了一个 shared 和 preview 实例, shared 用于实际使用, preview 实例用于预览,这两种情境下使用的数据是完全独立的,这样在预览时就可以方便指定特殊的数据用例。对于这段代码,我们只需在其中加入若干处理异常错误的代码即可,而如果你想有效利用 SwiftUI 的 preview 功能,在这里指定用于 preview 的数据也是必要的。

Context 理解和应用


context 是 Core Data 中用于修改和追踪数据修改的主要实例,通过使用 context ,你决定何时向 Core Data 中保存数据,这使得撤销等操作变得十分容易。你也可以创建多个context以用于不同情境下的的需求。

PersistentContainer 中包含了一个自动维护的运行于主线程的 context ,也就是当我们创建上面提到的 PersistentController 的实例时就创建了一个主要的 context ,在一般情况下我们只需要使用这一个唯一的 context ,因此我们需要把它传递到其它需要用到它的 View 中,借助 environment 可以保存 context 的指针,也能够轻松在目标 View 中进行调用。

import SwiftUI

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

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

在目标 View 的 struct 内部使用 @environment 即可获取 context ,并传递到下一级需要用到 Core Data 的 View 。

import SwiftUI
import CoreData

struct ContentView: View {
    @Environment(\.managedObjectContext) var context

    var body: some View {
            SecondaryContentView()
                .environment(\.managedObjectContext, self.context)
    }
}

每一次当你需要将已修改的数据保存到 Core Data 时,你需要调用 context 的 save 方法,并进行适当的错误处理。

private func save() {
    do {
        try context.save()
    } catch {
        let nsError = error as NSError
        fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
    }
}

Core Data 基本操作(CRUD)


Create

创建新的对象只要在 initializer 中加入 context 即可。

private func addItem() {
    let newItem = Item(context: context)
}

使用 FetchRequest 可以读取 Core Data 中的数据,你可以指定排序的方式。

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

Update

直接对目标对象进行修改即可。

Delete

这里可能会让人有些困惑,实际上 Swift 在传递对象参数时永远是用指针传递,所以无论是在方法中还是方法外,这个对象始终是同一个,给予 context 对象指针就可以删除它。

private func deleteItem(item: Item) {
    context.delete(item)
}

所有的操作在做完之后并没有真正地保存,而是留下了撤销的机会,当你需要永久保存更改时总是需要对 context 进行 save 操作。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK