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 propertylistrepresentation convert value property list compliant type.
  • an initializer init(propertylist contrary.

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