Parse PFSubclassing no funciona con Swift
parse.com (5)
He copiado el ejemplo de Parse Swift para Subclassing:
class Armor : PFObject, PFSubclassing {
override class func load() {
self.registerSubclass()
}
class func parseClassName() -> String! {
return "Armor"
}
}
Obtengo los siguientes errores:
/Parse/Armor.swift:11:1: error: type ''Armor'' does not conform to protocol ''PFSubclassing''
class Armor : PFObject, PFSubclassing {
^
__ObjC.PFSubclassing:15:28: note: protocol requires function ''object()'' with type ''() -> Self!''
@objc(object) class func object() -> Self!
^
__ObjC.PFSubclassing:23:52: note: protocol requires function ''objectWithoutDataWithObjectId'' with type ''(String!) -> Self!''
@objc(objectWithoutDataWithObjectId:) class func objectWithoutDataWithObjectId(objectId: String!) -> Self!
^
__ObjC.PFSubclassing:30:27: note: protocol requires function ''query()'' with type ''() -> PFQuery!''
@objc(query) class func query() -> PFQuery!
^
__ObjC.PFSubclassing:35:38: note: protocol requires function ''registerSubclass()'' with type ''() -> Void''
@objc(registerSubclass) class func registerSubclass()
^
/Parse/Armor.swift:14:9: error: ''Armor.Type'' does not have a member named ''registerSubclass''
self.registerSubclass()
^ ~~~~~~~~~~~~~~~~
Vi esta respuesta: https://stackoverflow.com/a/24899411/843151 y probé esa solución sin suerte, obtengo los mismos errores.
Alguna sugerencia de por qué esto está sucediendo? Gracias por adelantado.
Necesitaba importar el análisis PFObject + Subclass.h en mi encabezado de puente Objective-C
#import <Parse/PFObject+Subclass.h>
Con xCode 6.1.1 pude hacerlo funcionar sin el encabezado de puente. Sólo:
import Parse
en la parte superior del módulo. Para la declaración de clase, necesité utilizar @NSManaged para los tipos de variables para que se vinculen con las variables de la clase Parse con éxito. Me gusta esto:
class PSCategory : PFObject, PFSubclassing {
override class func load() {
self.registerSubclass()
}
class func parseClassName() -> String! {
return "Category"
}
@NSManaged var Name: String
}
Luego, en mi consulta, todos los nombres están vinculados dinámicamente:
var query = PSCategory.query() // PFQuery(className: "Category")
query.cachePolicy = kPFCachePolicyCacheElseNetwork // kPFCachePolicyNetworkElseCache
query.maxCacheAge = 60 * 60 * 24 // One day, in seconds.
query.findObjectsInBackgroundWithBlock {
(categories: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for abstractCategory in categories {
let category = abstractCategory as PSCategory
NSLog("Category Name: %@", category.Name)
}
} else {
NSLog("Unable to retrieve categories from local cache or network")
}
}
Parse recomienda initialize () en lugar de load ()
class Armor : PFObject, PFSubclassing {
override class func initialize() {
var onceToken : dispatch_once_t = 0;
dispatch_once(&onceToken) {
self.registerSubclass()
}
}
static func parseClassName() -> String! {
return "Armor"
}
}
Parece que PFSubclassing no funciona correctamente en Swift como está, a partir de v1.7.2, lanzado el 27 de abril de 2015.
Pude hacerlo funcionar implementando getters y setters personalizados para las propiedades como una solución temporal, lo que en cierto modo frustra el propósito, pero al menos este enfoque dará como resultado una refactorización menor una vez que PFSubclassing esté listo para Swift.
No es necesario agregar #import <Parse/PFObject+Subclass.h>
al encabezado de puente. Pero, como se indica en la Referencia del protocolo PFSubclassing, " Warning: This method must be called before [Parse setApplicationId:clientKey:]
", debe registrar todas las subclases personalizadas de PFObject antes de llamar a Parse.setApplicationId(_:, clientKey:)
.
Aquí hay un ejemplo para una subclase de PFObject personalizada llamada PFChatLOCMessage:
// In ProjectName-Bridging-Header.h
#import <Parse/Parse.h>
// In AppDelegate.swift
@UIApplicationMain
final class AppDelegate: UIResponder, UIApplicationDelegate {
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
configureParse()
return true
}
func configureParse() {
registerParseSubclasses()
Parse.setApplicationId("...", clientKey: "...")
}
func registerParseSubclasses() {
PFChatLOCMessage.registerSubclass()
}
}
// In PFChatLOCMessage.swift
private let PFChatLOCMessageClassName = "PFChatLOCMessage"
private let userKey = "user"
class PFChatLOCMessage: PFObject {
var user: PFUser! {
get { return self[userKey] as! PFUser }
set { self[userKey] = newValue }
}
override init() {
super.init()
}
override init(user: PFUser) {
super.init()
self.user = user
}
}
extension PFChatLOCMessage: PFSubclassing {
class func parseClassName() -> String {
return PFChatLOCMessageClassName
}
}
Obtuve deadlocks / cuelga utilizando la override class func initialize
, aunque eso se recomienda en la documentación de análisis.
Al pensar en ello, no es una buena idea hacer el enhebrado en los métodos init de las clases; nunca se sabe cuándo ni en qué contexto se los llamará.
Esto funcionó para mí: para todas mis subclases personalizadas, antes de llamar a los métodos de inicio de análisis sintáctico, los registro explícitamente como subclases.
De esta forma, el orden en que se llaman las cosas está bien definido. Además, funciona para mí;)
MyPFObjectSubclass.registerSubclass()
MyPFObjectSubclass2.registerSubclass()
MyPFObjectSubclass3.registerSubclass()
// etc...
let configuration = ParseClientConfiguration {
$0.applicationId = "fooBar"
$0.server = "http://localhost:1337/parse"
}
Parse.initializeWithConfiguration(configuration)