Ennabah
Ennabah

Reputation: 2533

Downcast set of type X that are subclass of of Y

Note: although the question has a CoreData example, it's not related to CoreData, it's just an example

We are working on a Swift project with CoreData as a caching layer.

We make use of Notifications in our mainViewController a lot to listen to the changes after our NSManagedObjectContext has new changes.

This is working great until we added new entities with the following hierarchy:

Entities

The problem is in the following:
when a new Car object is added, the notification fires, and in the mainViewController, we need to check if it's of type Car, like this:

if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<Car> {
    print("we have some cars") // this will never execute
}

The type downcast Set<Car> will never evaluate to true because the Set has elements of type Car and also Human.

What I want:
Check if the Set has NSManagedObject subclass of type Car or Human as I downcast it.

What I tried to do:
downcast it to NSManagedObject, and check if the Set contains Car by the adding the following where condition:
insertedObjects.contains(Car), but it has a compile-time error:

Cannot convert value of type '(Car).Type' to expected argument type 'NSManagedObject'

Let me know if you have any question instead of just downvoting.

Upvotes: 3

Views: 131

Answers (2)

Milan Nos&#225;ľ
Milan Nos&#225;ľ

Reputation: 19757

Not sure about the type casting (I think I remember doing it the same way and it worked, although it was with an array), but checking if there is a car in the set is different:

set.contains { (element) -> Bool in
    return element is Car
}

Or shorter (more concise) version of the same call:

set.contains(where: { $0 is Car })

Upvotes: 1

Martin R
Martin R

Reputation: 540055

First downcast the inserted object to Set<NSManagedObject>. To check if any car has been inserted, use

if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> {

    if insertedObjects.contains(where: { $0 is Car }) {
        print("we have some cars")
    }

}

To get the inserted car objects as a (possibly empty) array, use flatMap():

if let insertedObjects = notification.userInfo?[NSInsertedObjectsKey] as? Set<NSManagedObject> {

    let insertedCars = insertedObjects.flatMap { $0 as? Car }

}

Your approach

if insertedObjects.contains(Car)

does not compile because

func contains(_ member: Set.Element) -> Bool

expects an instance of the element type as argument. As shown above, you can use the predicate-based variant

func contains(where predicate: (Element) throws -> Bool) rethrows -> Bool

instead.

Upvotes: 1

Related Questions