【问题标题】:How to add Core Data to existing Xcode 9 Swift 4 iOS 11 project?如何将核心数据添加到现有的 Xcode 9 Swift 4 iOS 11 项目?
【发布时间】:2018-07-21 07:05:01
【问题描述】:

请求更新,因为这个问题当然已经回答了以前的版本,日期为 12/16 的最新搜索结果与以前的 iOS 9 和 10 项目产生了不相关的兼容性。

文档当然说在开始一个新项目时选择使用核心数据复选框,我没有选择,但现在认为需要添加 iCloud + 核心数据才能将我的应用程序带到下一个阶段 -> 其中一些像 NSFileCoordinator 和 NSFilePresenter 是必需的,因为在我的应用程序 UI 中,用户会看到许多主题,每个主题都有三个选项,关于哪些用户将选择一个选项。对于每个主题,UI 会显示选择每个选项的用户总数以及每个选项的总数百分比。

现在,每个选项的选择数量和总数的百分比当然只是在我的本地应用程序中计算 -> 但实际上需要在云等中心或最有可能在网站上计算......但是然后网站提出了 NSFileCoordinator 和 NSFilePresenter 已经解决的同时读/写问题。

因此,如果 iCloud + Core Data 系统可以在发送新的 Ubiquitous Container 数值总数和百分比值之前,在接收到单个用户的写入数值命令后在云中插入基本算术计算- 那么我非常感谢有关如何修复下面在尝试创建和初始化核心数据堆栈时生成的错误的建议。否则我想我将不得不爬取 Xcode 并使用像 PhoneGap 这样的混合应用程序,如果这是最适合这项工作的应用程序的话。

因此,请参阅核心数据编程指南:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/InitializingtheCoreDataStack.html#//apple_ref/doc/uid/TP40001075-CH4-SW1

并在我现有项目的开头粘贴以下代码,生成

使用未解析的标识符“persistentContainer”...“managedObjectContext”

... 错误。还有那行

init(completionClosure: @escaping () -> ()) { 

...生成

初始化器只能在类型中声明

import UIKit

import CoreData
class DataController: NSObject {
  var managedObjectContext: NSManagedObjectContext
  init(completionClosure: @escaping () -> ()) {
    persistentContainer = NSPersistentContainer(name: "DataModel")
    persistentContainer.loadPersistentStores() { (description, error) in
      if let error = error {
        fatalError("Failed to load Core Data stack: \(error)")
      }
      completionClosure()
    }
  }
}

init(completionClosure: @escaping () -> ()) {
  //This resource is the same name as your xcdatamodeld contained in your project
  guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension:"momd") else {
    fatalError("Error loading model from bundle")
  }
  // The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
  guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
    fatalError("Error initializing mom from: \(modelURL)")
  }

  let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)

  managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)
  managedObjectContext.persistentStoreCoordinator = psc

  let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background)
  queue.async {
    guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
      fatalError("Unable to resolve document directory")
    }
    let storeURL = docURL.appendingPathComponent("DataModel.sqlite")
    do {
      try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
      //The callback block is expected to complete the User Interface and therefore should be presented back on the main queue so that the user interface does not need to be concerned with which queue this call is coming from.
      DispatchQueue.main.sync(execute: completionClosure)
    } catch {
      fatalError("Error migrating store: \(error)")
    }
  }
}

// followed by my existing working code:

class ViewController: UIViewController {

【问题讨论】:

    标签: ios xcode core-data


    【解决方案1】:

    使用以下代码

    lazy var persistantCoordinator :NSPersistentStoreCoordinator = {
        
        let poc = NSPersistentStoreCoordinator(managedObjectModel:managedObjectModel)
        
        let documentFolderUrl = FileManager.default.urls(for: .documentDirectory, in:.userDomainMask).last
        
        let path = documentFolderUrl!.appendingPathComponent("Database.sqlite")
        
        let options = [NSMigratePersistentStoresAutomaticallyOption: true,NSInferMappingModelAutomaticallyOption: true]
        do{
            try poc.addPersistentStore(ofType:NSSQLiteStoreType, configurationName: nil, at: path, options: options)
        }catch{
            print(error.localizedDescription)
        }
        
        return poc
    }()
    
    
    private lazy var managedObjectModel:NSManagedObjectModel = {
        
        let url = Bundle.main.url(forResource:"Database", withExtension:"momd")
        return NSManagedObjectModel(contentsOf:url!)!
    }()
    
    
    fileprivate lazy var managedObjectContext:NSManagedObjectContext = {
        
        let moc = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)
        moc.persistentStoreCoordinator = persistantCoordinator
        
        return moc
    }()
    

    【讨论】:

      【解决方案2】:

      如果你像我一样懒惰,这里有你需要从新的 Core Data 项目中复制的所有代码......(为什么要让每个人都创建一个新项目?)。更改 YOUR_APP_NAME_HERE

      在 AppDelegate.swift 文件的顶部:

      import CoreData
      

      在 AppDelegate.swift 文件的底部,结束大括号之前:

      // MARK: - Core Data stack
      
      func applicationWillTerminate(_ application: UIApplication) {
              // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
              // Saves changes in the application's managed object context before the application terminates.
              self.saveContext()
          }
      
      lazy var persistentContainer: NSPersistentContainer = {
          /*
           The persistent container for the application. This implementation
           creates and returns a container, having loaded the store for the
           application to it. This property is optional since there are legitimate
           error conditions that could cause the creation of the store to fail.
          */
          let container = NSPersistentContainer(name: "YOUR_APP_NAME_HERE")
          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)")
              }
          })
          return container
      }()
      
      // MARK: - Core Data Saving support
      
      func saveContext () {
          let context = persistentContainer.viewContext
          if context.hasChanges {
              do {
                  try context.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)")
              }
          }
      }
      

      【讨论】:

      • 所以在哪里把这个代码放在现有项目中,我创建了项目,现在几乎完成了我在控制台中收到消息 Failed to load /System/Library/PrivateFrameworks/CorePDF.framework/Versions/ A/CorePDF 我在想我可能需要在我的应用中启用核心数据
      • 请将此代码放在 AppDelegate.swift 中。还要在 AppDelegate.swift 的顶部添加“import CoreData”
      【解决方案3】:

      转到File > new file...选择iOS下的core Data,然后选择Data Model 每当您在项目创建期间选择核心数据时,您仍然需要一些 xcode 自动生成的代码。 要获得它,只需使用 checked 核心数据选项创建新项目,然后复制 AppDelegate.swift 中的 ** //Mark: - Core Data Stack** 注释下编写的所有代码> 并添加

      import CoreData
      

      上面

      可选

      并且不要忘记在复制 lazy var persistentContainer 的完成块后更改应用的名称。在这部分更改你的应用程序的名称 *NSPersistentContainer(name: "SHOULD-BE-THE-NAME-OF-YOUR-APP") 和你刚刚复制的代码的 managedObjectModel 函数**

      【讨论】:

      • 好的,谢谢杰。在我现有项目中“转到文件>新文件...在iOS下选择核心数据并选择数据模型”之后,我现在有了文件Model.xcdatamodel ...然后创建了一个新项目(不将其添加到任何现有项目中) 并在 AppDelegate.swift 中,class AppDelegate: ...{ 在我现有的代码之后,粘贴“// MARK: - Core Data stack ... // MARK: - Core Data Saving support”行,在结束内} .尚未显示预编译错误,但我不明白您的可选建议。我不想更改我的应用程序的名称。我的临时新项目还没结束吗?
      • 好吧,我现有的应用程序在创建一些具有属性的实体之后构建,然后运行......没有执行任何可选步骤......所以我会检查这个绿色。坦克!
      • 对于后来出现的人,您可能还想在 AppDelegate.swift 的 applicationWillTerminate() 和 applicationDidEnterBackground() 方法中添加self.saveContext()
      • 更改应用名称表示managedObjectModel(或现在的persistentContainer代码)中的型号名称。例如:persistentContainer = NSPersistentContainer(name: "DataModel"),其中 DataModel 应替换为您的应用程序的名称,因为这是 Core Data 创建的商店的默认名称。 (注意:用破折号替换名称中的任何空格,因为在名为 My App 的应用程序中将有一个名为 My-App 的默认存储)
      • 为什么 Xcode 如此愚蠢以至于它不能在没有开发人员额外步骤的情况下添加更多功能。 Xcode 太糟糕了,我可以这么说是因为我使用了 IntelliJ IDEA 之类的想法,非常棒。感谢您的回答
      【解决方案4】:

      我知道这已得到解答,但我相信实际问题出在 Apple 的文档上。如果您将 Objective-C 代码与 Swift 代码进行比较,您会发现 var managedObjectContext: NSManagedObjectContext 实际上并没有定义。您应该将该行替换为var persistentContainer: NSPersistentContainer。这是Objective-c接口

      @interface MyDataController : NSObject
      @property (strong, nonatomic, readonly) NSPersistentContainer *persistentContainer; 
      - (id)initWithCompletionBlock:(CallbackBlock)callback;
      @end
      

      所以DataController.swift 应该是:

      class DataController: NSObject {
       // Delete this line   var managedObjectContext: NSManagedObjectContext
          var persistentContainer: NSPersistentContainer
          init(completionClosure: @escaping () -> ()) {
              persistentContainer = NSPersistentContainer(name: "DataModel")
              persistentContainer.loadPersistentStores() { (description, error) in
                if let error = error {
                    fatalError("Failed to load Core Data stack: \(error)")
                }
                completionClosure()
             }
          }
      }
      

      至于您的其余代码,不需要Apple Docs

      在 iOS 10 和 macOS 10.12 之前,Core Data 堆栈的创建更加复杂

      那段代码向您展示了旧方法。

      【讨论】:

      • 这与文档无关。这是关于您在创建一个选中 Core Data 的新项目时获得的 模板
      • DataController 类不在默认模板中。他是从我提供的文档中得到的。如果您选中 Core Data,它会在 AppDelegate 中添加所有默认的 Core Data 代码。在文档中,他们提到了如何通过 DataController 将其分解到自己的类中。
      • 是的,总共 3 个步骤:1) add the model file,2) set up the DataController class,将 managedObjectContext 替换为 persistentContainer,如本答案所示;然后,3)add a property and call the DataController in the AppDelegate.
      猜你喜欢
      • 2011-10-12
      • 2017-10-08
      • 2012-09-23
      • 2011-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-03-01
      相关资源
      最近更新 更多