Search code examples
iosswiftnsarraynsdata

Convert array of custom object to AnyObject in Swift


In my app I am doing something like this:

struct Record {
    var exampleData : String
}

class ExampleClass : UIViewController {
    let records = [Record]()

    override func viewDidLoad() {
        super.viewDidLoad()

        let data = NSKeyedArchiver.archivedDataWithRootObject(self.records) as! NSData
    }

    ...

}

But in the last line of viewDidLoad() I got this error:

Argument type '[Record]' does not conform to expected type 'AnyObject'

How can I fix this? Thanks.


Solution

  • If you want to keep struct, you can encode data using withUnsafePointer(). Here's an example, which I adapted from this Gist:

    import UIKit
    
    enum EncodingStructError: ErrorType {
        case InvalidSize
    }
    
    func encode<T>(var value: T) -> NSData {
        return withUnsafePointer(&value) { p in
            NSData(bytes: p, length: sizeofValue(value))
        }
    }
    
    func decode<T>(data: NSData) throws -> T {
        guard data.length == sizeof(T) else {
            throw EncodingStructError.InvalidSize
        }
    
        let pointer = UnsafeMutablePointer<T>.alloc(1)
        data.getBytes(pointer, length: data.length)
    
        return pointer.move()
    }
    
    enum Result<T> {
        case Success(T)
        case Failure
    }
    

    I added some error handling and marked the method as throws. Here's one way you can use it, in a docatch block:

    var res: Result<String> = .Success("yeah")
    
    var data = encode(res)
    
    do {
        var decoded: Result<String> = try decode(data)
    
        switch decoded {
        case .Failure:
            "failure"
        case .Success(let v):
            "success: \(v)" // => "success: yeah"
        }
    } catch {
        print(error)
    }
    

    The error handling I added will not decode if the NSData length doesn't match the type size. This can commonly happen if you write the data to disk, the user updates to a newer version of the app with a different-sized version of the same type, and then the data is read in.

    Also note that sizeof() and sizeofValue() may return different values on different devices, so this isn't a great solution for sending data between devices (NSJSONSerialization might be better for that).