Pfobject Unable to Be Cast to Custom Subclass

PFObject unable to be cast to custom subclass

this one kept me busy some hours - Parse-Documentation mentions:

Strongly-typed subclasses of PFObject must conform to the PFSubclassing protocol and must call before [Parse setApplicationId:clientKey:] is called."

By calling MyParseClass.registerSubclass() in AppDelegate I got my issues fixed!

PFObject Subclassing: NSArray element failed to match the Swift Array Element type

The value you return from parseClassName must match the class name that is defined in Parse.com, so in your case, parseClassName would need to return MyCustomClassInParse

This enables the Parse framework to match the PFSubclassing class to the Parse.com class and return the appropriate object. If there is no match then you will just get plain-old PFObject instances, which is why you get a runtime error when you try to downcast.

Back4app - Swift - Cast PFObject to Class

You can use PFSubclassing. I see that you're declaring a user class so you could just subclass PFUser and then write something like this:

class User: PFUser, PFSubclassing {

//MARK: Propriedades
dynamic var nome: String?
dynamic var foto: String?
dynamic var dataNascimento: Date?
dynamic var numeroTelefone: String?
dynamic var pais: PaisCodigo?
dynamic var telefoneE164: String?
dynamic var objectId: String?
dynamic var created: Date?
dynamic var updated: Date?

}

Of course if you're using the init(with:) and encode(with:) you have to implement it...

How to retrofit Parse’s PFObject to an existing, complex, aggregate model class?

You should definitely check out the subclassing guide. A few things to note that are particularly important for your use case:

  1. If you subclass a built-in class (e.g. User : PFUser) then you only need to register that subclass. All the constructor methods return instancetype so you can call them on User and you'll get an actual User back. Similarly, any query for a Parse user class will return results deserialized as your type.
  2. If you subclass PFObject for your own types, you must also implement +parseClassName. This maps your class to the name you want to see in the data browser. This name must be consistent across all languages to make sure you're talking to the same data.
  3. The PFObject subclassing library was written to only implement @dynamic properties. If you @static it or define the method then PFObject leaves it alone. If you have an ivar with the same name, the property isn't saved to Parse, but we will instead create an accessor/mutator pair that respects the PFObject's internal lock that we take when merging in server data or preparing a save operation.
  4. PFObject can handle properties of any JSON serializable primitive, pointers to other PFObject subtypes, arrays/dictionaries thereof, and PFRelation pointers. I haven't played with Swift yet, so I don't know how it's going to interact with optional properties; I assume they're NSNumbers under the covers and PFObject handles NSNumbers as well as primitives (which are auto-boxed/unboxed).
  5. [Bonus] To make your subclasses self-contained you can add the following to their definition:

    // Note: +initialize is Objective-C's only non-polymorphic function; every class
    // must implement this directly.
    +(void) initialize {
    [self registerSubclass];
    }

Custom PFQuery and PFObject Connection in Swift 2.0

Probably not the core issue here but your CustomObject subclasses from PFQbject (Notice there is a 'Q' instead of an 'O'). Also you will need to use PFObject in the closure inside of the GetAsyncObjects function because the signature of the block expects the returned objects to be of type [PFObject]?. You can circumvent the issue by doing something like this:

// in CustomQuery class
func GetAsyncObjects(closure: (dataObjects:[CustomObject]) -> ()) {
self.findObjectsInBackgroundWithBlock {(returnedObjects:[PFObject]?, error:NSError?) -> Void in
// explicitly cast to your custom object
closure(dataObjects: returnedObjects as! [CustomObject])
}
}

So in order for the closure to get an array of CustomObjects back, you can just cast the [PFObject]? data you get back from Parse to your desired array type, in this case [CustomObject].

PFImageView not working on sublass of PFCollectionViewCell

The problem you are facing is that you are registering the class in your -viewDidLoad method, but you are setting up your cell via the Prototype cell in Storyboard.

If you refer to the "Cell and View Reuse" section here, you will read (emphasis mine):

... if the cell or supplementary views are created using prototypes within
a storyboard it is not necessary to register the class in code and, in
fact, doing so will prevent the cell or view from appearing when the
application runs
.

This is why your PFImageViews were not behaving, because you were breaking the Prototype connections you made in Storyboards after -registerClass:forCellWithReuseIdentifier: was called, and your PFImageViews were lost in runtime limbo.

So, to solve your problem, simply REMOVE -registerClass:forCellWithReuseIdentifier from your view controller.

Et voila, problem solved...

So this code below is not needed anywhere in your view controller, because again, if you are using prototype cells, there is no need to register the class:

// Not needed when using Prototype cells in Storyboards!
[self.collectionView registerClass:[PA_ProfileCollectionViewCell class]
forCellWithReuseIdentifier:_cellIdentifier];

Once you remove the registerClass code, the code you have working with your PFImageView in your question's code examples above should work just fine.

Hope this helps!


On a side note, there is no need to cast your custom cell when instantiating it in -collectionView:cellForItemAtIndexPath:object

// this is all you need
SomeCustomCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:_cellId];

// the casting example below is redundant and unnecessary, stick with the top example
SomeCustomCell *cell = (SomeCustomCell *)[collectionView dequeueReusableCellWithReuseIdentifier:_cellId];

Pass Parse PFobject objectID to map annotation view button title

The viewForAnnotation delegate is not necessarily called immediately after you call addAnnotation nor is it necessarily called only once for each annotation.

The map view will call the delegate method when it wants to show the annotation. This may happen a little later than when you add it or it could happen again for the same annotation if the user pans or zooms the map and the annotation comes onto the screen again.

Therefore, you cannot use the view-controller-level restaurantID variable the way you are (by assuming that it was just set in relation to the annotation the map view is calling viewForAnnotation for). By the time the map view calls viewForAnnotation, restaurantID is set to some value that is unrelated to the annotation the map view is currently getting the view for.


Instead, you should put the objectId (or even the whole object) in each annotation individually.

Right now, since you're using the MKPointAnnotation class, the only properties you can set are coordinate, title, and subtitle.

You need to subclass MKPointAnnotation or create your own custom class that implements the MKAnnotation protocol and add a restaurantID property to it.

Set this property when adding the annotation:

CustomAnnotationClass *foodPoint = [[CustomAnnotationClass alloc] init];
foodPoint.coordinate = (spot);
foodPoint.restaurantID = [object objectId]; //<--put objectId in foodPoint
foodPoint.title = [object objectForKey:@"restaurantName"];

Then in viewForAnnotation, use the annotation parameter to get the values relevant to the annotation the map view is currently calling for:

//first make sure annotation is our custom type...
if ([annotation isKindOfClass:[CustomAnnotationClass class]])
{
UIButton* rightButton = [UIButton buttonWithType:UIButtonTypeDetailDisclosure];

//cast annotation parameter to our class so compiler understands...
CustomAnnotationClass *fp = (CustomAnnotationClass *)annotation;

//get restaurantID from the annotation parameter ("fp")...
[rightButton setTitle:fp.restaurantID forState:UIControlStateNormal];

...
}

Also be sure to handle annotation view re-use properly. That is, if dequeue returns a view, update its annotation property to the current one (eg. annotationView.annotation = annotation;). If this is not done, it's again possible for the view or callout to show data for a different annotation.


That should resolve the issue with the button showing the wrong title.

However, I assume you're setting the button's title just so you can figure out which annotation the user tapped. That will "work" but it's a bit kludgy since the title (restaurantID) will appear to the right of the disclosure icon (though it may not be visible in the callout).

Instead of that approach, I highly recommend using the map view's calloutAccessoryControlTapped delegate method or the map view's selectedAnnotations property. See these questions for examples:

  • How to recognize which pin was tapped
  • How to keep data associated with MKAnnotation from being lost after a callout pops up and user taps disclosure button?

This way, you can get a reference to the annotation object itself and from that get the restaurantID (same way as in viewForAnnotation).



Related Topics



Leave a reply



Submit