youtubers tesis sobre sirve resumida que para investigaciones investigacion historia caracteristicas ios swift watchkit

ios - tesis - No se puede decodificar objeto de clase



tesis sobre youtube pdf (4)


Estoy intentando enviar una "Clase" a mi extensión de Watchkit, pero recibo este error.

* Aplicación de terminación debido a la excepción no detectada ''NSInvalidUnarchiveOperationException'', razón: ''* - [NSKeyedUnarchiver decodeObjectForKey:]: no se puede descodificar objeto de la clase (MyApp.Person)

Archivar y desarchivar funciona bien en la aplicación iOS, pero no mientras se comunica con la extensión watchkit. Que pasa

InterfaceController.swift

let userInfo = ["method":"getData"] WKInterfaceController.openParentApplication(userInfo, reply: { (userInfo:[NSObject : AnyObject]!, error: NSError!) -> Void in println(userInfo["data"]) // prints <62706c69 7374303... if let data = userInfo["data"] as? NSData { if let person = NSKeyedUnarchiver.unarchiveObjectWithData(data) as? Person { println(person.name) } } })

AppDelegate.swift

func application(application: UIApplication!, handleWatchKitExtensionRequest userInfo: [NSObject : AnyObject]!, reply: (([NSObject : AnyObject]!) -> Void)!) { var bob = Person() bob.name = "Bob" bob.age = 25 reply(["data" : NSKeyedArchiver.archivedDataWithRootObject(bob)]) return }

Cambio de persona

class Person : NSObject, NSCoding { var name: String! var age: Int! // MARK: NSCoding required convenience init(coder decoder: NSCoder) { self.init() self.name = decoder.decodeObjectForKey("name") as! String? self.age = decoder.decodeIntegerForKey("age") } func encodeWithCoder(coder: NSCoder) { coder.encodeObject(self.name, forKey: "name") coder.encodeInt(Int32(self.age), forKey: "age") } }


De acuerdo con la interacción con las API de Objective-C :

Cuando usa el @objc( nombre ) en una clase Swift, la clase está disponible en Objective-C sin ningún espacio de nombre. Como resultado, este atributo también puede ser útil cuando migra una clase de Objective-C archivable a Swift. Debido a que los objetos archivados almacenan el nombre de su clase en el archivo, debe usar el @objc( nombre ) para especificar el mismo nombre que su clase de Objective-C para que su nueva clase Swift no pueda archivar los archivos más antiguos.

Al agregar la anotación @objc(name) , el espacio de nombres se ignora incluso si solo estamos trabajando con Swift. Vamos a demostrar. Imagina que el objetivo A define tres clases:

@objc(Adam) class Adam:NSObject { } @objc class Bob:NSObject { } class Carol:NSObject { }

Si el objetivo B llama a estas clases:

print("/(Adam().classForCoder)") print("/(Bob().classForCoder)") print("/(Carol().classForCoder)")

La salida será:

Adam B.Bob B.Carol

Sin embargo, si el objetivo A llama a estas clases, el resultado será:

Adam A.Bob A.Carol

Para resolver su problema, solo agregue la directiva @objc (nombre):

@objc(Person) class Person : NSObject, NSCoding { var name: String! var age: Int! // MARK: NSCoding required convenience init(coder decoder: NSCoder) { self.init() self.name = decoder.decodeObjectForKey("name") as! String? self.age = decoder.decodeIntegerForKey("age") } func encodeWithCoder(coder: NSCoder) { coder.encodeObject(self.name, forKey: "name") coder.encodeInt(Int32(self.age), forKey: "age") } }


Tuve que agregar las siguientes líneas después de configurar el marco para hacer que el NSKeyedUnarchiver funcione correctamente.

Antes de desarchivar:

NSKeyedUnarchiver.setClass(YourClassName.self, forClassName: "YourClassName")

Antes de archivar:

NSKeyedArchiver.setClassName("YourClassName", forClass: YourClassName.self)


Tuve una situación similar en la que mi aplicación usó mi marco Core en el que conservé todas las clases modelo. Por ejemplo, NSKeyedArchiver y NSKeyedUnarchiver objeto UserProfile usando NSKeyedArchiver y NSKeyedUnarchiver , cuando decidí mover todas mis clases a MyApp NSKeyedUnarchiver comenzó a lanzar errores porque los objetos almacenados eran como Core.UserProfile y no MyApp.UserProfile como esperaba el desarchivador. Cómo lo resolví fue crear una subclase de NSKeyedUnarchiver y anular la función classforClassName :

class SKKeyedUnarchiver: NSKeyedUnarchiver { override open func `class`(forClassName codedName: String) -> Swift.AnyClass? { let lagacyModuleString = "Core." if let range = codedName.range(of: lagacyModuleString), range.lowerBound.encodedOffset == 0 { return NSClassFromString(codedName.replacingOccurrences(of: lagacyModuleString, with: "")) } return NSClassFromString(codedName) } }

Luego agregó @objc(name) a las clases que necesitaban archivarse, como se sugiere en una de las respuestas aquí.

Y llámalo así:

if let unarchivedObject = SKKeyedUnarchiver.unarchiveObject(withFile: UserProfileServiceImplementation.archiveURL.path) as? UserProfile { currentUserProfile = unarchivedObject }

Funcionó muy bien.

La razón por la que la solución NSKeyedUnarchiver.setClass(YourClassName.self, forClassName: "YourClassName") no fue para mí porque no funciona para objetos anidados, como cuando UserProfile tiene una var address: Address . Unarchiver tendrá éxito con el UserProfile pero fallará cuando llegue a un nivel más profundo de Address .

Y la razón por la que la @objc(name) solo no lo hizo por mí es porque no me moví de OBJ-C a Swift, por lo que el problema no era UserProfile -> MyApp.UserProfile sino Core.UserProfile - > MyApp.UserProfile .


NOTA: Si bien la información de esta respuesta es correcta, la mejor forma de responder es la siguiente: @agy.

Esto se debe a que el compilador está creando MyApp.Person & MyAppWatchKitExtension.Person de la misma clase. Generalmente se produce al compartir la misma clase en dos objetivos en lugar de crear un marco para compartirlo.

Dos correcciones:

La solución adecuada es extraer Person en un marco. Tanto la aplicación principal como la extensión de watchkit deben usar el marco y utilizarán la misma clase *.Person .

La solución consiste en serializar su clase en un objeto Foundation (como NSDictionary ) antes de guardarlo y pasarlo. El NSDictionary será codificado y decodificable tanto en la aplicación como en la extensión. Una buena manera de hacer esto es implementar el protocolo RawRepresentable en Person .