Update 1: I have changed some of the code in line with what is presented in the next of the Stanford lectures. I also removed the code I added to AppDelegate because I saw there was a name clash there. The error message remains the same.

我正在开发自己的iOS应用程序,同时遵循斯坦福开发的iOS 10应用程序和Swift课程 . 我正在努力设置我的数据库 . 我已经创建了一个我遇到的问题的最小例子 . 我正在尝试在我的数据库中为实体创建扩展 . 我正在尽力遵循课程第10讲幻灯片中的示例代码,因为我只需要完成这个简单的应用程序 . 然而,我遇到了一些阻碍我做出改变的障碍:

1)虽然我被指示将模块设置为当前产品模块但这不起作用(xcode无法找到那样的文件)所以我将其设置为Global Namespace而不是

2)教授使用以下方法在他的示例中创建Tweet实体:

if let tweet = Tweet(context: context) {
    tweet.text = “some string”
    // more code
    }

然而,这不起作用,xcode抱怨Tweet不是可选的( “initializer for conditional binding must have optional type” ) .

除此之外,在我的例子中,我试图做一些非常简单的事情 . 我只是想设置一个CoreData数据库并为其添加一个条目 . 我这样做的错误信息是: “An NSManagedObject of class 'database_test.Subject' must have a valid NSEntityDescription.”

以下是我为测试此问题而生成的代码的分步操作:

1)使用CoreData创建了一个新的Swift项目

2)这是我的AppDelegate.swift

//
//  AppDelegate.swift
//  database_test
//

import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

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

    // MARK: - Core Data stack

    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: "database_test")
        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)")
            }
        }
    }

}

3)创建具有一个属性(uniqueID)的Subject实体 . 这是在全局命名空间(see linked)中 . 我为类别/扩展做Codegen . 然后我创建Subject.swift并修复它以导入CoreData并添加一些代码以添加唯一主题:

//
//  Subject.swift
//  database_test
//

import UIKit
import CoreData

class Subject: NSManagedObject {

    class func addSubject(adding newSubject: String, in context: NSManagedObjectContext) throws {
        let request: NSFetchRequest<Subject> = Subject.fetchRequest()
        request.predicate = NSPredicate(format: "uniqueID = %@", newSubject)
        do {
            let matches = try context.fetch(request)
            if matches.count > 0 {
                assert(matches.count==1, "Subject.addSubject - matches.count > 1")
            }
        } catch {
            throw(error)
        }

        let subject = Subject(context: context)
        subject.uniqueID = newSubject
    }

}

4)以下是DerivedData的codegen文件:

//  Subject+CoreDataProperties.swift
//
//  This file was automatically generated and should not be edited.
//

import Foundation
import CoreData


extension Subject {

    @nonobjc public class func fetchRequest() -> NSFetchRequest<Subject> {
        return NSFetchRequest<Subject>(entityName: "Subject");
    }

    @NSManaged public var uniqueID: String?

}

//  database_test+CoreDataModel.swift
//
//  This file was automatically generated and should not be edited.
//

import Foundation
import CoreData

5)然后我测试向数据库添加内容

//
//  ViewController.swift
//  database_test
//
//  Created by Alex B on 2017-07-10.
//  Copyright © 2017 Alex B. All rights reserved.
//

import UIKit
import CoreData

class ViewController: UIViewController {

    //let context = AppDelegate.viewContext
    var container: NSPersistentContainer? = (UIApplication.shared.delegate as? AppDelegate)?.persistentContainer

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        testAdding(subjectID: "John")
    }

    func testAdding(subjectID: String) {
        container?.performBackgroundTask { context in
            try?Subject.addSubject(adding: subjectID, in: context)
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }


}

在模拟器中运行它我终止了NSException类型的未捕获异常,它追溯到 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'An NSManagedObject of class 'database_test.Subject' must have a valid NSEntityDescription.'