简体   繁体   English

核心数据验证:从Objective-C到Swift

[英]Core Data validation: from Objective-C to Swift

I'm building a dummy iOS project in order to understand how to implement validation in Core Data with Swift. 我正在构建一个虚拟iOS项目,以了解如何使用Swift在Core Data中实现验证。 The Core Data model of the project has one entity called Person that contains two attributes: firstName and lastName . 项目的核心数据模型有一个名为Person实体,它包含两个属性: firstNamelastName The project is based on Swift but, in order to start it, I'm using Objective-C to define the NSManagedObject subclass: 该项目基于Swift,但为了启动它,我使用Objective-C来定义NSManagedObject子类:

Person.h Person.h

@interface Person : NSManagedObject

@property (nonatomic, retain) NSString *firstName;
@property (nonatomic, retain) NSString *lastName;

@end

Person.m Person.m

@implementation Person

@dynamic firstName;
@dynamic lastName;    

-(BOOL)validateFirstName:(id *)ioValue error:(NSError **)outError {
    if (*ioValue == nil || [*ioValue isEqualToString: @""]) {
        if (outError != NULL) {
            NSString *errorStr = NSLocalizedStringFromTable(@"First name can't be empty", @"Person", @"validation: first name error");
            NSDictionary *userInfoDict = @{ NSLocalizedDescriptionKey : errorStr };
            NSError *error = [[NSError alloc] initWithDomain:@"Domain" code: 101 userInfo: userInfoDict];
            *outError = error;
        }
        return NO;
    }
    return YES;
}

@end

Person-Bridging-Header.h 人桥接,Header.h

#import "Person.h"

In the Core Data Model Editor, I've set the entity class inside the Data Model Inspector as indicated: 在核心数据模型编辑器中,我按照指示在数据模型检查器中设置了实体类:

class: Person

The first time I launch the project, I create an instance of Person in the AppDelegate application:didFinishLaunchingWithOptions: method with the following code: 第一次启动项目时,我在AppDelegate application:didFinishLaunchingWithOptions:创建了一个Person实例application:didFinishLaunchingWithOptions:方法,其代码如下:

if !NSUserDefaults.standardUserDefaults().boolForKey("isNotInitialLoad") {
    let person = NSEntityDescription.insertNewObjectForEntityForName("Person", inManagedObjectContext: managedObjectContext!) as Person
    person.firstName = "John"
    person.lastName = "Doe"

    var error: NSError?
    if !managedObjectContext!.save(&error) {
        println("Unresolved error \(error), \(error!.userInfo)")
        abort()
    }

    NSUserDefaults.standardUserDefaults().setBool(true, forKey: "isNotInitialLoad")
    NSUserDefaults.standardUserDefaults().synchronize()
}

The project has one UIViewController with the following code: 该项目有一个UIViewController ,代码如下:

class ViewController: UIViewController {

    var managedObjectContext: NSManagedObjectContext!
    var person: Person!

    override func viewDidLoad() {
        super.viewDidLoad()

        //Fetch the Person object
        var error: NSError?
        let fetchRequest = NSFetchRequest(entityName: "Person")
        let array = managedObjectContext.executeFetchRequest(fetchRequest, error:&error)
        if array == nil {
            println("Unresolved error \(error), \(error!.userInfo)")
            abort()
        }
        person = array![0] as Person
    }

    @IBAction func changeFirstName(sender: AnyObject) {
        //Generate a random firstName
        let array = ["John", "Jimmy", "James", "Johnny", ""]
        person.firstName = array[Int(arc4random_uniform(UInt32(5)))]

        var error: NSError?
        if !managedObjectContext.save(&error) {
            println("Unresolved error \(error), \(error!.userInfo)")
            return
        }

        //If success, display the new person's name
        println("\(person.firstName)" + " " + "\(person.lastName)")
    }

}

changeFirstName: is linked to a UIButton . changeFirstName:链接到UIButton Therefore, whenever I click on this button, a new String is randomly generated and assigned to person.firstName . 因此,每当我单击此按钮时,将随机生成一个新String并将其分配给person.firstName If this new String is empty, validateFirstName:error: generates a NSError and the save operation fails. 如果此新String为空,则validateFirstName:error:生成NSError并且保存操作失败。

This works great but, in order to have a pure Swift project, I've decided to delete Person.h , Person.m and Person-Bridging-Header.h and to replace them with a single Swift file: 这很好但是,为了有一个纯粹的Swift项目,我决定删除Person.hPerson.mPerson-Bridging-Header.h并用一个Swift文件替换它们:

class Person: NSManagedObject {

    @NSManaged var firstName: String
    @NSManaged var lastName: String

    func validateFirstName(ioValue: AnyObject, error: NSErrorPointer) -> Bool {
        if ioValue as? String == "" {
            if error != nil {
                let myBundle = NSBundle(forClass: self.dynamicType)
                let errorString = myBundle.localizedStringForKey("First name can't be empty", value: "validation: first name error", table: "Person")
                let userInfo = NSMutableDictionary()
                userInfo[NSLocalizedFailureReasonErrorKey] = errorString
                userInfo[NSValidationObjectErrorKey] = self
                var validationError = NSError(domain: "Domain", code: NSManagedObjectValidationError, userInfo: userInfo)
                error.memory = validationError
            }
            return false
        }

        return true
    }

}

In the Core Data Model Editor, I've also changed the entity class inside the Data Model Inspector as indicated: 在核心数据模型编辑器中,我还更改了数据模型检查器中的实体类,如下所示:

class: Person.Person //<Project name>.Person

The problem now is that the project crashes whenever I call changeFirstName: . 现在的问题是每当我调用changeFirstName:时项目都会崩溃changeFirstName: The weirdest thing is that if I put a breakpoint inside validateFirstName: , I can see that this method is never called. 最奇怪的是,如果我在validateFirstName:放置一个断点,我可以看到永远不会调用此方法。

What am I doing wrong? 我究竟做错了什么?

I am a little bit guessing here, but the (id *)ioValue parameter is mapped to Swift as 我在这里有点猜测,但是(id *)ioValue参数映射到Swift as

ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>

therefore the Swift variant should probably look like 因此,Swift变体应该看起来像

func validateFirstName(ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>, error: NSErrorPointer) -> Bool {
    if let firstName = ioValue.memory as? String {
        if firstName == "" {
            // firstName is empty string
            // ...
        }
    } else {
        // firstName is nil (or not a String)
        // ...
    }
    return true
}

Update for Swift 2: Swift 2更新

func validateFirstName(ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
    guard let firstName = ioValue.memory as? String where firstName != ""  else {
        // firstName is nil, empty, or not a String
        let errorString = "First name can't be empty"
        let userDict = [ NSLocalizedDescriptionKey: errorString ]
        throw NSError(domain: "domain", code: NSManagedObjectValidationError, userInfo: userDict)
    }
    // firstName is a non-empty string
}

As @SantaClaus correctly noticed, the validation function must now throw an error if the validation fails. 正如@SantaClaus正确注意到的那样,验证函数现在必须在验证失败时抛出错误。

Apple's Core Data Programming Guide is now updated for Swift 3. Here's the example code from the Managing Object Life Cycle > Object Validation page ( memory has been renamed to pointee ): Apple的核心数据编程指南现在已针对Swift 3进行了更新。以下是管理对象生命周期>对象验证页面中的示例代码( memory已重命名为pointee ):

func validateAge(value: AutoreleasingUnsafeMutablePointer<AnyObject?>!) throws {
    if value == nil {
        return
    }

    let valueNumber = value!.pointee as! NSNumber
    if valueNumber.floatValue > 0.0 {
        return
    }
    let errorStr = NSLocalizedString("Age must be greater than zero", tableName: "Employee", comment: "validation: zero age error")
    let userInfoDict = [NSLocalizedDescriptionKey: errorStr]
    let error = NSError(domain: "EMPLOYEE_ERROR_DOMAIN", code: 1123, userInfo: userInfoDict)
    throw error
}

EDIT: The example is not quite right. 编辑:这个例子不太正确。 To get it to work, I've changed AutoreleasingUnsafeMutablePointer<AnyObject?> to an unwrapped optional and value?.pointee to value.pointee . 为了使它工作,我已经将AutoreleasingUnsafeMutablePointer<AnyObject?>更改为一个未包装的可选value?.pointeevalue?.pointeevalue.pointee

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM