[英]Getting Swift 4.2 to load Core Data Store from previous Objective-C build
[英]Core Data validation: from Objective-C to Swift
我正在构建一个虚拟iOS项目,以了解如何使用Swift在Core Data中实现验证。 项目的核心数据模型有一个名为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:
创建了一个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
。 因此,每当我单击此按钮时,将随机生成一个新String
并将其分配给person.firstName
。 如果此新String
为空,则validateFirstName:error:
生成NSError
并且保存操作失败。
这很好但是,为了有一个纯粹的Swift项目,我决定删除Person.h
, Person.m
和Person-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
}
}
在核心数据模型编辑器中,我还更改了数据模型检查器中的实体类,如下所示:
class: Person.Person //<Project name>.Person
现在的问题是每当我调用changeFirstName:
时项目都会崩溃changeFirstName:
。 最奇怪的是,如果我在validateFirstName:
放置一个断点,我可以看到永远不会调用此方法。
我究竟做错了什么?
我在这里有点猜测,但是(id *)ioValue
参数映射到Swift as
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正确注意到的那样,验证函数现在必须在验证失败时抛出错误。
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
}
编辑:这个例子不太正确。 为了使它工作,我已经将AutoreleasingUnsafeMutablePointer<AnyObject?>
更改为一个未包装的可选value?.pointee
和value?.pointee
到value.pointee
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.