Implementing NSDocument in a right way

57 Views Asked by At

I have a large legacy UIKit project in Objective C, which I need to rewrite for AppKit in Swift. The part I struggling the most is converting UIDocument to NSDocument. A file that UIDocument represents is a container and this adds another level of complexity for me. Here's the reading and writing code:

#pragma mark - writing

- (void)encodeObject:(id<NSCoding>)object toWrappers:(NSMutableDictionary *)wrappers preferredFilename:(NSString *)preferredFilename {
    @autoreleasepool {
        NSKeyedArchiver * archiver = [[NSKeyedArchiver alloc] initRequiringSecureCoding:false];
        [archiver encodeObject:object forKey:@"data"];
        [archiver finishEncoding];
        
        NSData *data = [archiver encodedData];
        
        NSFileWrapper * wrapper = [[NSFileWrapper alloc] initRegularFileWithContents:data];
        [wrappers setObject:wrapper forKey:preferredFilename];
        
    }
}

- (id)contentsForType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    
    if (self.backupData == nil) {
        return nil;
    }
    
    NSMutableDictionary * wrappers = [NSMutableDictionary dictionary];
    [self encodeObject:self.backupData toWrappers:wrappers preferredFilename:@"backup.data"];
    NSFileWrapper * fileWrapper = [[NSFileWrapper alloc] initDirectoryWithFileWrappers:wrappers];
    
    return fileWrapper;
    
}

#pragma mark - reading

- (id)decodeObjectFromWrapperWithPreferredFilename:(NSString *)preferredFilename {
    
    NSFileWrapper * fileWrapper = [self.fileWrapper.fileWrappers objectForKey:preferredFilename];
    if (!fileWrapper) {
        NSLog(@"Unexpected error in BackupDocument -decodeObjectFromWrapperWithPreferredFilename: Couldn't find %@ in file wrapper!", preferredFilename);
        return nil;
    }
    
    NSData * data = [fileWrapper regularFileContents];
    NSKeyedUnarchiver * unarchiver = [[NSKeyedUnarchiver alloc] initForReadingFromData:data error:nil];
    unarchiver.requiresSecureCoding = NO;
    return [unarchiver decodeObjectForKey:@"data"];
    
}

- (BNGBackupData *)backupData {
    
    if (_backupData == nil) {
        if (self.fileWrapper != nil) {
            self.backupData = [self decodeObjectFromWrapperWithPreferredFilename:DATA_FILENAME];
        } else {
            self.backupData = [[BNGBackupData alloc] init];
        }
    }
    
    return _backupData;
}

- (BOOL)loadFromContents:(id)contents ofType:(NSString *)typeName error:(NSError *__autoreleasing *)outError {
    
    self.fileWrapper = (NSFileWrapper *) contents;
    
    // The rest will be lazy loaded...
    self.backupData = nil;
    
    return YES;
    
}

I honestly don’t quite understand how it works, especially all of these @autoreleasepool operators and id return types instead of more clear type notation in Swift, so after some attempts I tried to do it light way, just renaming UIDocument to NSDocument and extending it with required methods calling existing Objective C methods from it, but didn’t succeed too.

extension BackupDocument {
    
    open override func data(ofType typeName: String) throws -> Data {
        if let data = decodeObjectFromWrapper(withPreferredFilename: "backup.data") as? Data {
            return data
        } else {
            Swift.print("\n\nNo Data esquired from decodeObjectFromWrapper(withPreferredFilename:\n\n")
            return Data()
        }
//        return self.data.
    }
    
    open override nonisolated func read(from data: Data, ofType typeName: String) throws {
        DispatchQueue.main.async {
            let wrappers = NSMutableDictionary()
            self.encodeObject(self.data, toWrappers: wrappers, preferredFilename: "backup.data")
        }
    }
}

Can anybody explain the right way implementing NSDocument for this type of files?

0

There are 0 best solutions below