ios - How to save a generic custom object to UserDefaults? -
this generic class:
open class smstate<t: hashable>: nsobject, nscoding { open var value: t open var didenter: ( (_ state: smstate<t>) -> void)? open var didexit: ( (_ state: smstate<t>) -> void)? public init(_ value: t) { self.value = value } convenience required public init(coder decoder: nscoder) { let value = decoder.decodeobject(forkey: "value") as! t self.init(value) } public func encode(with acoder: nscoder) { acoder.encode(value, forkey: "value") } } then want this:
let stateencodedata = nskeyedarchiver.archiveddata(withrootobject: currentstate) userdefaults.standard.set(stateencodedata, forkey: "state") in case currentstate of type smstate<someenum>.
but when call nskeyedarchiver.archiveddata, xcode (9 beta 5) shows message in purple saying:
attempting archive generic swift class 'stepup.smstate<stepup.routineviewcontroller.routinestate>' mangled runtime name '_ttgc6stepup7smstateocs_21routineviewcontroller12routinestate_'. runtime names generic classes unstable , may change in future, leading non-decodable data. i not sure tries say. not possible save generic object ?
is there other way save generic custom object ?
edit:
even if use anyhashable instead of generics same error on runtime when calling nskeyedarchiver.archiveddata:
terminating app due uncaught exception 'nsinvalidargumentexception', reason: : unrecognized selector sent instance
if want make generic class adopt nscoding , generic type t going encoded , decoded t must 1 of property list compliant types.
property list compliant types nsstring, nsnumber, nsdate , nsdata
a possible solution create protocol propertylistable , extend swift equivalents of property list compliant types protocol
the protocol requirements
- an
associated type. - a computed property
propertylistrepresentationconvert value property list compliant type. - an initializer
init(propertylistcontrary.
public protocol propertylistable { associatedtype propertylisttype var propertylistrepresentation : propertylisttype { } init(propertylist : propertylisttype) } here exemplary implementations string , int.
extension string : propertylistable { public typealias propertylisttype = string public var propertylistrepresentation : propertylisttype { return self } public init(propertylist: propertylisttype) { self.init(stringliteral: propertylist) } } extension int : propertylistable { public typealias propertylisttype = int public var propertylistrepresentation : propertylisttype { return self } public init(propertylist: propertylisttype) { self.init(propertylist) } } lets declare sample enum , adopt propertylistable
enum foo : int, propertylistable { public typealias propertylisttype = int case north, east, south, west public var propertylistrepresentation : propertylisttype { return self.rawvalue } public init(propertylist: propertylisttype) { self.init(rawvalue: propertylist)! } } finally replace generic class
open class smstate<t: propertylistable>: nsobject, nscoding { open var value: t open var didenter: ( (_ state: smstate<t>) -> void)? open var didexit: ( (_ state: smstate<t>) -> void)? public init(_ value: t) { self.value = value } convenience required public init(coder decoder: nscoder) { let value = decoder.decodeobject(forkey: "value") as! t.propertylisttype self.init(t(propertylist: value)) } public func encode(with acoder: nscoder) { acoder.encode(value.propertylistrepresentation, forkey: "value") } } with implementation can create instance , archive it
let currentstate = smstate<foo>(foo.north) let stateencodedata = nskeyedarchiver.archiveddata(withrootobject: currentstate) and unarchive again
let restoredstate = nskeyedunarchiver.unarchiveobject(with: stateencodedata) as! smstate<foo> print(restoredstate.value) the whole solution seems cumbersome have fulfill restriction nscoding requires property list compliant types. if don't need custom type enum implementation easier (and shorter).
Comments
Post a Comment