S.Moore
S.Moore

Reputation: 1286

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??

Upvotes: 2

Views: 792

Answers (2)

ArtbyKGH
ArtbyKGH

Reputation: 173

Vishal S has put together a very nice article on the different ways one can (currently) save data in a structure.

Archiving and Unarchiving Swift Structure Instances

or as Vishal put it:

Swift introduced a tremendous anmount of type-safety. But, archiving and unarchiving always loses the types of the objects. Until a better way comes along to do this by supporting all of Swift’s principles, we should make do with what we have.

Of course one might suggest that the Apple Gurus put a little effort into developing a structure (& type) friendly means of saving your data (!).

Upvotes: 3

rob mayoff
rob mayoff

Reputation: 385670

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).

Upvotes: 6

Related Questions