iOS 12.0 Alternative to Using Deprecated archiveRootObject:toFile:
Asked Answered
K

4

27

With iOS 12, archiveRootObject:toFile: has been deprecated. Can anyone suggest a streamlined alternative to archiving objects to a file?

//Generic example of archiver prior to iOS 12.0    
-(BOOL) archive:(id)archiveObject withFileName:(NSString*)filename
{
    NSString *path = [NSHomeDirectory() stringByAppendingPathComponent:@"Documents"];
    return [NSKeyedArchiver archiveRootObject:archiveObject toFile:path];
}
Kumiss answered 2/12, 2018 at 12:32 Comment(0)
K
26

Thanks to @vadian for the hint, here's what I've come up with to do archiving and unarchiving under iOS 12:

NSError *error = nil;

NSString *docsDir;
NSArray *dirPaths;

//Get the device's data directory:
dirPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
docsDir = [dirPaths objectAtIndex:0];
NSString *databasePath = [[NSString alloc] initWithString: [docsDir stringByAppendingPathComponent:@"appData.data"]];

//Archive using iOS 12 compliant coding:
NSData *data = [NSKeyedArchiver archivedDataWithRootObject:@"foo" requiringSecureCoding:NO error:&error];
[data writeToFile:databasePath options:NSDataWritingAtomic error:&error];
NSLog(@"Write returned error: %@", [error localizedDescription]);

//Unarchive the data:
NSData *newData = [NSData dataWithContentsOfFile:databasePath];
NSString *fooString = [NSKeyedUnarchiver unarchivedObjectOfClass:[NSString class] fromData:newData error:&error];
Kumiss answered 2/12, 2018 at 14:43 Comment(2)
Did you (or anyone else) manage to implement this with requiringSecureCoding:YES ?Campy
Here, if you are saving data in NSString formate use [NSString class] or if you are saving data in NSDictionary formate use [NSDictionary class] in unarchivedObjectOfClass:Eraste
C
15

unArchivedObjectOfClass threw an error for me when trying to decode an object that didn't use secure encoding. After much trial and error, this is what finally worked without triggering an iOS 12/13 deprecation warning:

// Archive the object
NSData* data = [NSKeyedArchiver archivedDataWithRootObject:theObject requiringSecureCoding:NO error:nil];

// Unarchive the object
NSKeyedUnarchiver* unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
unarchiver.requiresSecureCoding = NO;
id theCopy = [unarchiver decodeTopLevelObjectForKey:NSKeyedArchiveRootObjectKey error:nil];
Chamkis answered 16/7, 2019 at 0:21 Comment(1)
Thanks, the NSKeyedArchiveRootObjectKey did it!Keeshakeeshond
R
11

As suggested by Apple, we should use FileManager for read/write the archived file.

func archiveURL() -> URL? {
    guard let documentURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first 
        else { return nil }

    return documentURL.appendingPathComponent("MyArchive.data")
}

func archive(customObject: CustomObject) {
    guard let dataToBeArchived = try? NSKeyedArchiver.archivedData(withRootObject: customObject, requiringSecureCoding: true), 
        let archiveURL = archiveURL() 
        else  {
        return
    }

    try? dataToBeArchived.write(to: archiveURL)
}

func unarchive() -> CustomObject? {
    guard let archiveURL = archiveURL(),
        let archivedData = try? Data(contentsOf: archiveURL),
        let customObject = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(archivedData)) as? CustomObject 
        else {
        return nil
    }

    return customObject
}
Resuscitate answered 15/1, 2020 at 9:56 Comment(0)
D
6

The replacement is archivedDataWithRootObject:requiringSecureCoding:error:

+ (NSData *)archivedDataWithRootObject:(id)object 
             requiringSecureCoding:(BOOL)requiresSecureCoding 
                             error:(NSError * _Nullable *)error;

plus an extra step to write the data to disk.

Please see Foundation iOS 11.4 to 12.0 API Differences

Davenport answered 2/12, 2018 at 13:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.