CoreData

  • 怎么在SwiftUI中运用CoreData
  • 在SwiftUI中怎么装备Core Data
  • 怎么从SwiftUI视图拜访中心数据管理目标Context
  • 怎么运用@FetchRequest创立一个中心数据获取恳求
  • 怎么运用predicate过滤CoreData的fetchRequests
  • 怎么从SwiftUI视图增加CoreData目标
  • 怎么从SwiftUI视图中删去CoreData目标
  • 怎么约束获取FetchRequest中的item数量
  • 怎么在SwiftUI中运用NSUserActivity

概述

文章首要共享SwiftUI Modifier的学习进程,将运用事例的办法进行阐明。内容深入浅出,CoreData部分调试,不过测试代码是齐全的。假如想要运转成果,能够移步Github下载code -> github事例链接

1、怎么在SwiftUI中运用CoreData

SwiftUI和CoreData作为Apple软件渠道的其间两个重要组成部分,有很好的协同能力,SwiftUI有特点包装器、环境支持等等,这些都是为了保证能以最小的费事将Core Data集成到SwiftUI的App中。在SwiftUI之前,在架构角度来看,运用Core Data的办法如下:

  • Apple主张在AppDelegate的级别创立容器,然后根据需要return
  • 一部分人喜爱运用Manager Classes
  • 还有一部分人喜爱彻底笼统Core Data,以便利彻底转移到Realm或其他可选项

SwiftUI与Core Data的集成是不同的,在发动时,需要创立Core Data容器,然后将其保管目标注入到环境中,然后直接在那里履行Request。以下是四个特定的功能,能够帮你理解我的意思:

  1. NSManagedObject遵守ObservableObject协议,这代表能够将任何目标绑定到用户界面。
  2. 环境中有一个managedObjectContent的key,用于存储Core Data保管目标的上下文。
  3. 然后Xcode的模板将这个context注入到初始的内容视图中。
  4. 有一个@FetchRequest的特点包装器,它运用环境的保管目标上下文来获取request

因此,在应用发动时创立一个保管目标上下文,将其附加到视图的环境中,然后运用@FetchRequest加载数据供App运用。

2、在SwiftUI中怎么装备Core Data

假如创立一个新项目,运用的是SwiftUI并需要运用Core Data,Xcode的装备分为四步:

  1. 创立一个空的项目模板,通常以你的项目命名(FFModifier.xcdatamodeld),和一个示例装备
  2. 增加一个Persistence.swift文件,将Core Data整齐的封装在这个地方。
  3. 运用managedObjectContext将上下文注入初始ContentView的environment中。
  4. 创立在ContentView中创立、读取和删去示例数据的示例代码。

这是供给了在SwiftUI中运用CoreData获取request的完好能力。

2.1、创立Main.xcdatamodeld

经过cmd+N来创立一个新文件,然后选择Data Model来创立一个Core Data模型。这个模型的姓名很重要,因为要在代码中引证,我这儿命名为“Main”。一旦创立了model,那么就能够在任何想要运用的方位创立实例。

SwiftUI基础篇CoreData

2.2、创立ProgrammingLanguage的实例

打开xcdatamodeld文件并创立一个名为ProgrammingLanguage的实例,我创立了两个字符串特点:“name”和“creator”。你能够根据你的需求创立想要的任何特点了。

SwiftUI基础篇CoreData

2.3、创立PersistenceController单例

需要一个地方来加载和管理Core Data装备,Apple的模版经过一个PersistenceController单例完成。首要做的工作是让Core Data发动并运转,同时也为SwiftUI供给与来Contenxts的能力。

import CoreData
struct PersistenceController {
    //创立单例
    static let shared = PersistenceController()
    //Core Data
    let container: NSPersistentContainer
    //SwiftUI预览装备
    static var preview: PersistenceController = {
        let controller = PersistenceController(inMemory: true)
        //创立10个example
        for i in 0..<100 {
            let language = ProgrammingLanguage(context: controller.container.viewContext)
            language.name = "Example Language \(i)"
            language.creator = "A.Programmer \(i)"
        }
        return controller
    }()
    //加载Core Data初始化器,将运用内存存储设定为可选值
    init(inMemory: Bool = false) {
        //将Main更改为你自己的名
        container = NSPersistentContainer(name: "Main")
        if inMemory {
            container.persistentStoreDescriptions.first?.url = URL(filePath: "/dev/null")
        }
        container.loadPersistentStores { descroption, error in
            if let error = error {
                fatalError("BBLv Error: \(error.localizedDescription)")
            }
        }
    }
    //向PersistenceController类增加save办法,以便它查看context是否更改。
    func save() {
        let context = container.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                print("save error: \(error.localizedDescription)")
            }
        }
    }
}

2.4、将Core Data容器的保管目标context注入到SwiftUI环境中

let persistenceController = PersistenceController.shared

2.5、scenePhase监听App状况

最终一步是可选的,但主张运用,来保证数据完好,针对的场景是当App状况切换为后台时,应该调用save()办法,以便Core Data来保存你的更改,在SwiftUI中,经过增加一个特点到App来监听场景。

@Environment(\.scenePhase) var scenePhase

2.6、结合起来的代码

这段代码写在FFModifierApp中,APP的main入口,因为做了许多其他的比方也用到了这个入口,目前这儿代码比较多,这是我整理的只要关于CoreData部分的代码。

@main
struct FFModifierApp: App {
    //将Core Data容器的保管目标context注入到SwiftUI环境中,这儿运用perview运用的是假数据,
    //假如真实情况下,运用单例进行初始化(PersistenceController.shared)
    let persistenceController = PersistenceController.preview
    //最终一步是可选的,但主张运用,来保证数据完好,针对的场景是当App状况切换为后台时,
    //应该调用save()办法,以便Core Data来保存你的更改,在SwiftUI中,经过增加一个特点到App来监听场景。
    @Environment(\.scenePhase) var scenePhase
    var body: some Scene {
        WindowGroup {
            FFCoreDataFetchRequest()
                .environment(\.managedObjectContext, persistenceController.container.viewContext)
        }
        .onChange(of: scenePhase) {
            persistenceController.save()
        }
    }
}

在上面的PersistenceController.swift类中我选择将数据保存在内存中了,所以该代码的内存存储部分很重要,因为Core Data装备为将信息保存到内存而不是磁盘是,这代表着Core Data中的数据被定义为暂时数据,在程序关闭时会被清理。

3、怎么从SwiftUI视图拜访中心数据管理目标Context

中心代码为@Environment(\.managedObjectContext) var managedObjectContext,在每一个想要运用CoreData的上下文内容上都要运用这个环境变量。下面的比方仅代表着FFCoreDataManagged想要运用数据。可是啥也没干。

struct FFCoreDataManagged: View {
    //将context作为环境变量传递给ContentView(我这儿是FFCoreDataManagged),
    //在此视图中增加一个@Environment特点来读取保管目标上下文。
    @Environment(\.managedObjectContext) var managedObjectContext
    var body: some View {
        Text("Hello, World!")
    }
}

4、怎么运用@FetchRequest创立一个中心数据获取恳求

FetchRequest能够加载符合指定的条件的Core Data成果,并且SwiftUI能够将这些成果直接绑定到用户界面。创立fetchRequest需要两条信息:

  • 要查询的实体
  • 以及确认回来成果次序的排序描述符。
struct FFCoreDataFetchRequest: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    //  SortDescriptors参数是一个数组,所以能够供给尽可能多的排序选项
    @FetchRequest(sortDescriptors: []) var languages: FetchedResults<ProgrammingLanguage>
    var body: some View {
        List(languages) { language in
            Text(language.name ?? "UnKnown")
        }
    }
}

调试成果

看似简略的代码就成功获得了数据,其实是上面许多过程后的成果

SwiftUI基础篇CoreData

5、怎么运用predicate过滤CoreData的fetchRequests

CoreData的fetchRequest中能够运用Predicates,就像在UIKit中,所有这些都是通向你的@FetchRequest特点包装器供给一个predicate来完成的。

struct FFCoreDataFetchRequestsFilter: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    //创立predicate
    @FetchRequest(
        sortDescriptors: [],
        predicate: NSPredicate(format: "name == %@", "Python")
    ) var languages: FetchedResults<ProgrammingLanguage>
    //目前@FetchRequest运用的是规范的CoreData的Predicate,也能够创立复合Predicate
    var body: some View {
        List(languages) { language in
            Text(language.name ?? "UnKnown")
        }
    }
}

6、怎么从SwiftUI视图增加CoreData目标

在SwiftUI中保存core data目标的工作办法与在SwiftUI之外的工作往事彻底相同,拜访保管目标的context,在该上下文中创立你的类型的实例,然后在准备好时保存context。

struct FFCoreDataAddObjects: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(sortDescriptors: []) var languages: FetchedResults<ProgrammingLanguage>
    var body: some View {
        //创立实例
        Button("Insert Example Languate") {
            let language = ProgrammingLanguage(context: managedObjectContext)
            language.name = "Python"
            language.creator = "Guido van Rossum"
            PersistenceController.shared.save()
        }
        //在增加一组目标之后,程序会多一组目标。
        List(languages) { language in
            Text(language.name ?? "UnKnown")
        }
    }
}

调试成果

SwiftUI基础篇CoreData
SwiftUI基础篇CoreData

7、怎么从SwiftUI视图中删去CoreData目标

在SwiftUI中删去CoreData目标与在UIKit中删去他们根本相同,虽然需要越过一些特殊的过程才能与SwiftUI视图集成。

struct FFCoreDataDeleteObjects: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    @FetchRequest(
        sortDescriptors: []
    ) var languages: FetchedResults<ProgrammingLanguage>
    var body: some View {
        //不管在哪里显示数据,都向SwiftUI视图增加onDelete修饰符。
        List {
            ForEach(languages) { language in
                Text("Creator: \(language.creator ?? "Anonymous")")
            }
            .onDelete(perform: { indexSet in
                removelanguages(at: indexSet)
            })
        }
        .toolbar(content: {
            EditButton()
        })
    }
    func removelanguages(at offsets: IndexSet) {
        for index in offsets {
            let language = languages[index]
            managedObjectContext.delete(language)
        }
        PersistenceController.shared.save()
    }
}

调试成果

SwiftUI基础篇CoreData
SwiftUI基础篇CoreData
SwiftUI基础篇CoreData

8、怎么约束获取FetchRequest中的item数量

SwiftUI的@FetchRequest特点包装器非常合适对Object宣布简略request,供给排序和过滤功能。可是,假如想调整回来item的数量,那么需要额外设置

struct FFCoreDataLimit: View {
    @Environment(\.managedObjectContext) var managedObjectContext
    //创立@FetchRequest
    @FetchRequest var languages: FetchedResults<ProgrammingLanguage>
    init() {
        let request: NSFetchRequest<ProgrammingLanguage> = ProgrammingLanguage.fetchRequest()
        request.sortDescriptors = [
            NSSortDescriptor(keyPath: \ProgrammingLanguage.name, ascending: true)
        ]
        request.fetchLimit = 10
        _languages = FetchRequest(fetchRequest: request)
    }
    var body: some View {
        List(languages) { language in
            Text(language.name ?? "UnKnown")
        }
    }
}

9、怎么在SwiftUI中运用NSUserActivity

SwiftUI有一个专用的onContinueUserActivity()修饰符,能够捕获各种NSUserActivity类型,比方来自网络的点击、来自第三方或者Siri发动等等。在AppDelegate中运用application(_:continue:restorationHandler:)处理过这个问题,但SwiftUI办法更简略的处理这个问题。要完成这个需求,首先要创立一个函数并承受参数NSUserActivity,可是,不需要在App结构体中履行此操作。

func handleWechatLogin(_ userActivity: NSUserActivity) {
    if let id = userActivity.userInfo?["targetContentIdentifier"] {
        print("Found identifier \(id)")
    }
}

运用办法

var body: some Scene {
    WindowGroup {
        FFCoreDataDeleteObjects()
            .environment(\.managedObjectContext, persistenceController.container.viewContext)
            .onContinueUserActivity("targetContentIdentifier", perform: { userActivity in
                handleWechatLogin(userActivity)
            })
    }
    .onChange(of: scenePhase) {
        persistenceController.save()
    }
}