Search code examples
swiftpointersparse-platformsubclassmismatch

Parse - Query with 'includeKey' not getting objects in proper subclass


I am trying to get a Parse object, 'Pack' that has an array of pointers to another parse class, 'Cards'. When I query for 'Pack' and include the 'includeKey("cards")', it does in fact download those objects, but fails to cast them as my subclass 'Card' and instead defaults to "PFObject". When attempting to loop through 'cards' in a 'pack' and print the title, it gives me a type mismatch error:

fatal error: NSArray element failed to match the Swift Array Element type

Inspecting the 'pack' object in the debugger, I see that the 'cards' property (array) has all the expected objects BUT they are showing as having the 'PFObject' class.

Parse 'Pack' Subclass:

class Pack: PFObject, PFSubclassing {    
    override class func initialize() {
        var onceToken : dispatch_once_t = 0;
        dispatch_once(&onceToken) {
            self.registerSubclass()
        }
    }

    static func parseClassName() -> String {
        return "Pack"
    }

    var cards: [Card] {
        get {
            return self["cards"] as! [Card]
        }
        set {
            self["cards"] = newValue
        }
    }

    ... other properties...
}

Fetching Parse Object "Pack":

func fetchPackWithTitle(title: String) -> Pack {
    if let query = Pack.query() {
        query.whereKey("title", equalTo:"\(title)")
        query.includeKey("cards")
        do {
            let results = try query.findObjects()
            if results.count > 0 {
                // Found it...
                return results.first as? Pack
            }
        } catch {
            return nil
        }
    }
    return nil
}

Trying to print out card titles for the pack's cards:

if let pack = fetchPackWithTitle("Basic") {
    for card in pack.cards {
        print("Title: \(card.title)")
    }
}

Solution

  • Figured out the problem. That class in question actually had a bad object saved that contained no pointers as expected, and the subclass was implicitly unwrapping the array of pointers:

    var cards: [Card] {
        get {
            return self["cards"] as! [Card]
        }
        set {
            self["cards"] = newValue
        }
    }
    

    Updated to this and fixed the problem:

    var cards: [Card]? {
        get {
            return self["cards"] as? [Card]
        }
        set {
            self["cards"] = newValue
        }
    }