美文网首页CoreData/SQLite/FMDB
SwiftUI中使用CoreData

SwiftUI中使用CoreData

作者: 凉风起君子意如何 | 来源:发表于2022-03-13 20:19 被阅读0次

    今天要学习的是如何在SwiftUI中使用CoreData。

    知识点:
    1、已有项目添加CoreData
    2、SwiftUI中处理CoreData中的entity,包括读取,添加,删除
    3、SwiftUI中保存并读取环境变量

    学习链接来自该篇文章,讲的很细

    CoreData

    什么是CoreData?它有什么作用?它的优势?网上有很多相关资料,这里就不多说了。可以大致的看下如下总体结构图 引用自该文章

    • 已有项目添加CoreData
      添加CoreData方式有二,一是新创建项目时,勾选CoreData,创建完项目后,自动添加了涉及的相关功能。二就是下面要说的,如何在已有的项目添加CoreData,主要分下面三步:
      1、手动创建xcdatamodeld文件,文件名称一般命名工程名称



      2、在SeneDelegate.swift里添加相应代码。其实就是创建并设置container。

    func sceneDidEnterBackground(_ scene: UIScene) {
            saveContext()
        }
    //MARK: -CoreData
        lazy var persistentContainer: NSPersistentContainer = {
            let container = NSPersistentContainer(name: "CoredataSwiftUI")
            container.loadPersistentStores { _, error in
                if let error = error as NSError? {
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            }
            return container
        }()
        
        func saveContext() {
            let context = persistentContainer.viewContext
            if context.hasChanges {
                do {
                    try context.save()
                } catch  {
                    let error = error as NSError
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            }
        }
    

    3、打开xcdatamodeld文件,添加相应的model也就是entity。这里我们创建Movie模型


    SwiftUI使用CoreData

    • 设置根视图CoreData viewContext.如下代码
    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            let context = persistentContainer.viewContext
            let contentView = MovieList().environment(\.managedObjectContext, context)
            
            guard let windowScene = (scene as? UIWindowScene) else { return }
            window = UIWindow(windowScene: windowScene)
            window?.rootViewController = UIHostingController(rootView: contentView)
            window?.makeKeyAndVisible()
        }
    
    • 通过读取环境变量中的CoreData viewContext,执行后续添加/删除保存。这里有个疑问点 在读取Movie对象的时候,没有告诉到底是哪个container,难道一个工程下就只能存在一个container???核心代码如下,注意看注释
    import SwiftUI
    
    struct MovieList: View {
        // 读取viewContext,赋值给managedObjectContext属性
        @Environment(\.managedObjectContext) var managedObjectContext
        // 读取Movie对象集,赋值给movies数组。没有要求传入任何name
        @FetchRequest(entity: Movie.entity(), sortDescriptors: [NSSortDescriptor(keyPath: \Movie.title, ascending: true)]) var movies: FetchedResults<Movie>
        
        @State var isPresented = false
    
        var body: some View {
          NavigationView {
            List {
              ForEach(movies, id: \.title) {
                MovieRow(movie: $0)
              }
              .onDelete(perform: deleteMovie)
            }
            .sheet(isPresented: $isPresented) {
              AddMovieView { title, genre, release in
                self.addMovie(title: title, genre: genre, releaseDate: release)
                self.isPresented = false
              }
            }
            .navigationBarTitle(Text("Fave Flicks"))
            .navigationBarItems(trailing:
              Button(action: { self.isPresented.toggle() }) {
                Image(systemName: "plus")
              }
            )
          }
        }
    
        // 删除一个movie,并更新coredata数据库
        func deleteMovie(at offsets: IndexSet) {
          // 1.
          offsets.forEach { index in
            // 2.
            let movie = self.movies[index]
    
            // 3.
            self.managedObjectContext.delete(movie)
          }
    
          // 4.
          saveContext()
        }
    
        // 添加一个movie,并更新保存数据库
        func addMovie(title: String, genre: String, releaseDate: Date) {
          // 1 从viewContext获取一个Movie对象
          let newMovie = Movie(context: managedObjectContext)
    
          // 2
          newMovie.title = title
          newMovie.genre = genre
          newMovie.releaseDate = releaseDate
    
          // 3
          saveContext()
        }
    
    
        func saveContext() {
          do {
            try managedObjectContext.save()
          } catch {
            print("Error saving managed object context: \(error)")
          }
        }
    }
    

    SwiftUI中使用CoreData,总结几个点
    1、如何将内存中管理coredata的persistentContainer.viewContext传给相应的view。当然在非SwiftUI项目中,可能直接在AppDelegate定义成相应的熟悉,之后用的地方通过AppDelegate获取即可
    2、SwiftUI view中如何获取viewContext,继而通过viewContext处理添加或删除并保存更新操作
    3、如何读取coredata里面相应的entity

    SwiftUI中保存并读取环境变量

    从上面的代码,很容易发现SwiftUI中是如何保存并读取环境变量的。
    保存设置环境变量:
    SceneDelegate.swift文件

    let context = persistentContainer.viewContext
            let contentView = MovieList().environment(\.managedObjectContext, context)
    

    从当前环境中读取变量:
    MovieList.swift文件

    // 读取viewContext,赋值给managedObjectContext属性
        @Environment(\.managedObjectContext) var managedObjectContext
    

    End

    • 还记的上面那个疑问点吗?
      疑问点 在读取Movie对象的时候,没有告诉到底是哪个container,难道一个工程下就只能存在一个container???
      查看官方文档找到了答案:

      如上步骤搜索NSFetchRequest会发现这么一段话,当然你还会发现其它读取entity时的一些重要设置,如从什么地方开始读,最大读多少,应该读哪个store,返回哪些数据等

    对于移动端中小型项目来说一个是container,已经够用,也建议用一个。对于多个数据库container使用场景,目前还没接触到。

    • 如何避免读取重复的数据呢? 例如,一个学生列表中有很多同名的学生 分数可能不同,现在不需要重复读取多个同名学生信息。如下2步设置即可
      1、改动xcdatamodeld文件


      约束name属性不重名

    2、代码里设置viewContext

    添加红框代码
    参考该篇文章,解析很详细,以及hash的介绍
    • CoreData常用的几个谓词,即筛选器


    相关文章

      网友评论

        本文标题:SwiftUI中使用CoreData

        本文链接:https://www.haomeiwen.com/subject/jcjslrtx.html