uaknight
uaknight

Reputation: 695

How do I make a referential type comparison in Swift using 'is'?

I can't figure out how to make a type comparison in Swift using the is operator, if the right side is a reference and not a hard-coded type.

For example,

class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }

class GmStreet {
    var buildings: [GmBuilding] = []
    func findAllBuildingsOfType(buildingType: GmBuilding.Type) -> [GmBuilding] {
        var result: [GmBuilding] = []
        for building in self.buildings {
            if building is buildingType { // complains that buildingType is not a type
                result.append(building)
            }
        }
        return result
    }
}

let myStreet = GmStreet()
var buildingList: [GmBuilding] = myStreet.findAllBuildingsOfType(GmOffice.self)

It complains that 'buildingType is not a type'. How can it be made to work?

Upvotes: 2

Views: 133

Answers (4)

rintaro
rintaro

Reputation: 51911

As a final resort, using Obj-C reflect function:

import ObjectiveC

func isinstance(instance: AnyObject, cls: AnyClass) -> Bool {
    var c: AnyClass? = instance.dynamicType
    do {
        if c === cls {
            return true
        }
        c = class_getSuperclass(c)
    } while c != nil
    return false
}

class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }

isinstance(GmOffice(), GmOffice.self) // -> true
isinstance(GmOffice(), GmFactory.self) // -> false
isinstance(GmOffice(), GmBuilding.self) // -> true

Upvotes: 0

JeremyP
JeremyP

Reputation: 86701

I'm sure there must be a better way than this, but it doesn't require inheritance from NSObject and it works at runtime - according to my playground

class GmBuilding { }
class GmOffice: GmBuilding { }
class GmFactory: GmBuilding { }

func thingIs(thing: GmBuilding, #sameTypeAs: GmBuilding) -> Bool
{
    return thing.dynamicType === sameTypeAs.dynamicType
}


var foo: GmOffice = GmOffice()

thingIs(foo, sameTypeAs: GmOffice()) // true
thingIs(foo, sameTypeAs: GmFactory()) // false

The main reason I instantiate an object (you can use a singleton instead) is because I can't figure out how to declare a parameter to be a metatype.

It also doesn't work for

thingIs(foo, sameTypeAs: GmBuilding()) // false :=(

Upvotes: 0

Airspeed Velocity
Airspeed Velocity

Reputation: 40973

A generic method may do what you want:

func findAllBuildingsOfType<T: GmBuilding>(buildingType: T.Type) -> [GmBuilding] {
    // you can use `filter` instead of  var/for/append
    return buildings.filter { $0 is T }
}

This will work so long as you really do only want to determine the type at compile time:

let myStreet = GmStreet()
let buildingList = myStreet.findAllBuildingsOfType(GmOffice.self)
//   T is set at compile time to GmOffice  --------^

However, often when this question comes up, the follow-up question is, how do I store GmOffice.self in a variable and then have the type be determined at runtime? And that will not work with this technique. But if statically fixed types at compile time are enough for you, this should do it.

Upvotes: 3

Aaron Brager
Aaron Brager

Reputation: 66302

If AirSpeed Velocity's answer doesn't work for you, you can also accomplish this by bridging to Objective-C.

Make GmBuilding inherit from NSObject:

class GmBuilding: NSObject { }

And use isKindOfClass(_:) to check the type:

for building in self.buildings {
    if building.isKindOfClass(buildingType) {
        result.append(building)
    }
}

Not as Swifty, but it works.

Upvotes: 2

Related Questions