An older version of the app has a NSCoding
-compliant class (lets call it OldItem
), written in Objective C. This class has about 20 properties with different types:
@property (nonatomic) NSInteger x;
// ...
- (id)initWithCoder:(NSCoder *)coder {
if (self = [super init]) {
_x = [coder decodeIntegerForKey:@"x"];
// all 20 fields are decoded in the similar way
}
return self;
}
- (void)encodeWithCoder:(NSCoder *)coder {
[self.x encodeWithCoder:coder];
// all 20 fields encoded in the similar way
}
It's used in another class (lets call it Controller
) as array:
@property (nonatomic, strong, nonnull) NSMutableArray<OldItem *> *items;
Controller
also caches items to disk like this:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:self.items];
[data writeToFile:@"myfile.txt" options:NSDataWritingFileProtectionComplete error:nil];
Now I need to migrate OldItem
to Swift, while Controller
only changes the type of item it handles:
@property (nonatomic, strong, nonnull) NSMutableArray<NewItem *> *items;
Question is: how do I migrate an old data, saved in myfile.txt
in previous version of the application into new version?
NewItem
, but I'm open to keeping the old name, if it significantly simplifies the migration.OldItem
and NewItem
. Treat them as direct port.NewItem
should still be NSCoding
-compliant, and reading/writing NewItem
into the file is not an issue.There is nothing to migrate. NSCoder is a Cocoa thing, not a language thing; it works just the same whether you talk to it in Objective C or Swift. Just translate all the code line for line into Swift and you're done. The old archived data keeps right on working, because an archive is still an archive and a coder is still a coder. Edit If the question is how to give your Swift class the old Objective C class's name so that the archive can be read into it, note that Objective C MyClass
and Swift MyClass
have different names because of Swift name mangling; you have to say
@objc(MyClass) class Whatever : NSObject {
After looking around for a bit, the actual solution is:
Make sure public required init?(coder: NSCoder)
in NewItem class properly unarchives all fields you want to preserve from OldItem. For instance enums, or number type conversions may require special handling, even if you didn't change fields per se.
Right before unarchiving, tell NSKeyedUnarchiver
that NewItem should be used in place of OldItem, using NSKeyedUnarchiver.setClass
method.
Example:
let archivedData = try Data(contentsOf: filePath)
NSKeyedUnarchiver.setClass(NewItem.self, forClassName: "OldItem")
let items = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedData)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.