核心数据validation:从Objective-C到Swift
我正在构build一个虚拟iOS项目,以了解如何在Swift中实现核心数据validation。 项目的核心数据模型有一个名为Person
实体,它包含两个属性: firstName
和lastName
。 该项目是基于Swift,但为了启动它,我使用Objective-C来定义NSManagedObject
子类:
Person.h
@interface Person : NSManagedObject @property (nonatomic, retain) NSString *firstName; @property (nonatomic, retain) NSString *lastName; @end
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
人桥接,Header.h
#import "Person.h"
在核心数据模型编辑器中,我已经按照指示在数据模型检查器中设置了实体类:
class: Person
第一次启动项目时,我使用下面的代码在AppDelegate
application:didFinishLaunchingWithOptions:
创build了一个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() }
该项目有一个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:
被链接到一个UIButton
。 因此,每当我点击这个button,一个新的String
是随机生成的,并分配给person.firstName
。 如果这个新的String
是空的, validateFirstName:error:
生成一个NSError
,保存操作失败。
这很好,但为了有一个纯Swift项目,我决定删除Person.h
, Person.m
和Person-Bridging-Header.h
,并用一个Swift文件replace它们:
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 } }
在“核心数据模型编辑器”中,我还根据指示更改了“数据模型”检查器中的实体类:
class: Person.Person //<Project name>.Person
现在的问题是,只要我调用changeFirstName:
,项目就会崩溃。 最奇怪的是,如果我把一个断点放在validateFirstName:
,我可以看到这个方法从来没有被调用过。
我究竟做错了什么?
我在这里有点猜测,但(id *)ioValue
参数被映射为Swift
ioValue: AutoreleasingUnsafeMutablePointer<AnyObject?>
因此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 }
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 }
正如@SantaClaus正确注意到的那样,如果validation失败,validation函数现在必须抛出一个错误。
现在,Apple的Core Data Programming Guide已更新为Swift 3.下面是pipe理对象生命周期>对象validation页面中的示例代码( 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 }
编辑:这个例子是不完全正确的。 为了得到它的工作,我已经将AutoreleasingUnsafeMutablePointer<AnyObject?>
更改为一个展开的可选value?.pointee
和value?.pointee
以value.pointee
。