iosobjective-cswiftnscoding

Swift 3 - NSCoding without the annoyance of inheriting from NSObject


Getting a crash from NSKeyedArchiver

2016-10-06 17:06:06.713 MessagesExtension[24473:2175316] *** NSForwarding: 
warning: object 0x61800009d740 of class '' does not implement 
methodSignatureForSelector: -- trouble ahead
Unrecognized selector -[MessagesExtension.Model replacementObjectForKeyedArchiver:]

I have created a protocol called Coding that's entire purpose is to simplify NSCoding and NSKeyedArchiver without the need for using pieces of Objective-C.

protocol Coding {
    static var directory: FileManager.SearchPathDirectory { get }
    static var domainMask: FileManager.SearchPathDomainMask { get }

    func encode() -> [String: AnyObject]
    init()
    init?(dict: [String: AnyObject]?)
}

extension Coding {
    static var directory: FileManager.SearchPathDirectory {
        return .documentDirectory
    }

    static var domainMask: FileManager.SearchPathDomainMask {
        return .userDomainMask
    }

    static var directoryURL: String? {
        return NSSearchPathForDirectoriesInDomains(Self.directory, Self.domainMask, true).last?.appending("/")
    }

    func save(to path: String) -> Bool {
        guard let url = Self.directoryURL else { return false }

        return NSKeyedArchiver.archiveRootObject(self.encode() as NSDictionary, toFile: url + path)
    }

    static func create(from path: String) -> Self {
        guard let url = Self.directoryURL,
              let dict = NSKeyedUnarchiver.unarchiveObject(withFile: url + path) as? [String: AnyObject] else { return self.init() }

        return self.init(dict: dict) ?? self.init()
    }
}

This protocol and extension is suppose to simplify NSCoding and allow for the protocol to be used on Struts. Yet, I am running into the crash above when attempting to save the object.

More specifically, I am getting that crash on the return line of

func save(to path: String) -> Bool {
    guard let url = Self.directoryURL else { return false }

    return NSKeyedArchiver.archiveRootObject(self.encode() as NSDictionary, toFile: url + path)
}

I have a feeling it has something to do with NSDictionary but I am unsure how to proceed.

Any suggestions??


Solution

  • The Foundation archiving system (NSCoding and related types) was designed for and implemented in Objective-C a long time ago (parts of it are over 20 years old), and expects "objects" to be instances of NSObject. It is simply not realistic to try to use it to encode object graphs containing non-NSObject-like objects. You may well be able to simplify its use in Swift, but you're going to need to make sure everything that the archiver thinks is an NSObject implements the necessary parts of the NSObject API. Since there is no documentation of which parts are used by the archiver, the only sane choice is to subclass NSObject.

    I could be wrong of course, but you didn't show us the relevant parts of the code (specifically the implementations of encode).