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

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?

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.

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.

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 :=(

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

Related

Not able to override extensions function in another extension - Swift 4 [duplicate]

I've been working on an iOS application in Swift (much of it being moved from Objective-C). I'm using Core Data and trying to use extensions to add functionality to classes auto-generated from my model. One thing I readily did in Objective-C was to add a method in a category on class A and override that method in a category on class B (which derived from A), and I was hoping to do the same in Swift.
For a while now I've had the following code in my project (and this is just one example), and though I have not used the functionality yet, the compiler has worked just fine compiling this code:
// From CellType.swift -- NOTE: Imports from Foundation and CoreData
#objc(CellType)
class CellType: NSManagedObject {
#NSManaged var maxUses: NSNumber
#NSManaged var useCount: NSNumber
// Other properties removed for brevity
}
// From SwitchCellType.swift -- NOTE: Imports from Foundation and CoreData
#objc(SwitchCellType)
class SwitchCellType: CellType {
#NSManaged var targetCellXIndex: NSNumber
#NSManaged var targetCellYIndex: NSNumber
#NSManaged var targetCellType: CellType
// Other properties removed for brevity
}
// From CellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension CellType
{
var typeLabel : String { get { return "Empty"; } }
func isEqualToType(otherCellType : CellType) -> Bool
{
return (self.typeLabel == otherCellType.typeLabel &&
self.maxUses.isEqualToNumber(otherCellType.maxUses) &&
self.useCount.isEqualToNumber(otherCellType.useCount));
}
// Code removed for brevity
}
// From SwitchCellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension SwitchCellType // YES, this compiles with the overrides!
{
override var typeLabel : String { get { return "Switch"; } }
override func isEqualToType(otherCellType : CellType) -> Bool
{
var answer = false;
if let otherSwitchCellType = otherCellType as? SwitchCellType
{
answer = super.isEqualToType(otherCellType) &&
self.targetCellXIndex.isEqualToNumber(otherSwitchCellType.targetCellXIndex) &&
self.targetCellYIndex.isEqualToNumber(otherSwitchCellType.targetCellYIndex) &&
self.targetCellType.isEqualToType(otherSwitchCellType.targetCellType);
}
return answer;
}
// Code removed for brevity
}
Hopefully some kind Swift expert out there already sees my issue, but here's how I found out about it: Recently I tried to add similar functionality using methods that have parameters and/or return values that are not built in types, but I started getting this error: Declarations in extensions cannot override yet.
To explore this issue I added the following to one of my swift files, thinking it would compile just fine:
class A
{
}
class B : A
{
}
extension A
{
var y : String { get { return "YinA"; } }
}
extension B
{
override var y : String { get { return "YinB"; } } // Compiler error (see below) -- What??
}
To my surprise, I received the same compiler error (Declarations in extensions cannot override yet). What? But I've used that patter several times already without compiler errors.
Questions:
First, are there certain rules about overriding in extensions such that in some cases it is supposed to work but in other cases it is not? Second (and more disconcerting) why does it seem that the Swift compiler is so inconsistent? What am I missing here? Please help me restore my faith in Swift.
UPDATE:
As noted in the correct answer by Martin R, it seems you can override methods in the current version of Swift (1.1 via Xcode 6.1) as long as they (1) involve only classes derived from NSObject and (2) do not use the inout modifier. Here's some examples:
class A : NSObject { }
class B : A { }
class SubNSObject : NSObject {}
class NotSubbed {}
enum SomeEnum { case c1, c2; }
extension A
{
var y : String { get { return "YinA"; } }
func f() -> A { return A(); }
func g(val: SubNSObject, test: Bool = false) { }
func h(val: NotSubbed, test: Bool = false) { }
func j(val: SomeEnum) { }
func k(val: SubNSObject, inout test: Bool) { }
}
extension B
{
// THESE OVERIDES DO COMPILE:
override var y : String { get { return "YinB"; } }
override func f() -> A { return A(); }
override func g(val: SubNSObject, test: Bool) { }
// THESE OVERIDES DO NOT COMPILE:
//override func h(val: NotSubbed, test: Bool = false) { }
//override func j(val: SomeEnum) { }
//override func k(val: SubNSObject, inout test: Bool) { }
}
It seems that overriding methods and properties in an extension works with the
current Swift (Swift 1.1/Xcode 6.1) only for Objective-C compatible
methods and properties.
If a class is derived from NSObject then all its members are automatically available
in Objective-C (if possible, see below). So with
class A : NSObject { }
your example code compiles and works as expected. Your Code Data extension overrides
work because NSManagedObject is a subclass of NSObject.
Alternatively, you can use the #objc attribute for a method or property:
class A { }
class B : A { }
extension A
{
#objc var y : String { get { return "YinA" } }
}
extension B
{
#objc override var y : String { get { return "YinB" } }
}
Methods which are not representable in Objective-C cannot be marked with #objc
and cannot be overridden in a subclass extension. That applies for example to
methods having inout parameters or parameters of an enum type.
I experienced this on Xcode9. Closing and reopening Xcode worked for me. Probably a bug in the compiler.

Get object type from optional?

Is it possible to get the object type from an optional?
For example, if I have a class that has a property that is an optional string, can I somehow just get back the string type?
The exact use case I have is I have many custom classes all of which have a property that is storing another custom class as an optional value. I would like to write a generic function that will create an instance of the object class stored in the optional.
Here is an example of what I am looking for, although .dynamicType does not work since it is an optional:
class Class1 {
}
class Class2 {
var myOp: Class1?
}
var c = Class2()
c.myOp = c.myOp.dynamicType()
Since you wanted to use this with Generics I tried it for you. It works, but it may not be so useful.
First some setup:
This is a helper protocol to make sure our Generic type will have a known init method.
protocol ZeroParameterInit {
init()
}
This is an extension to get the type from an optional:
extension Optional {
var dynamicWrappedType : Wrapped.Type {
return Wrapped.self
}
}
Implemented in your code:
class Class1 : ZeroParameterInit {
required init() {}
}
class Class2 {
var myOp: Class1?
}
var c = Class2()
c.myOp = c.myOp.dynamicWrappedType.init()
Generic implementation:
class Class1 : ZeroParameterInit {
required init() {}
}
class Class2<T where T : ZeroParameterInit> {
var attribute: Optional<T>// used long syntax to remind you of : Optional<Wrapped>
init(attr:T) {
attribute = attr
attribute = nil
}
}
The function to create the instance:
func myFunc<T>(instance: Class2<T>) -> T {
return instance.attribute.dynamicWrappedType.init()
}
Some tests:
let alpha = Class1()
let beta = Class2(attr: alpha)
beta.attribute = myFunc(beta)
The issue:
You can't create an instance of Class2 without informing it about the type of it's generic attribute. So you need to pass it some object/type and that complicates things again.
Some extra methods that might improve how it all works:
init() {
}
let delta = Class2<Class1>()
delta.attribute = myFunc(delta)
init(type:T.Type) {
}
let epsilon = Class2(type: Class1.self)
epsilon.attribute = myFunc(epsilon)
You just need to check if the optional exist:
func myFunc(c: Class2) -> Class1? {
if let c1 = c.myOp{
return c1.dynamicType()
}
return nil
}
OR
func myFunc(c: Class2) -> Class1? {
if c.myOp != nil{
return c.myOp!.dynamicType()
}
return nil
}
Note the your return type need to be optional as well.
Tried this in simulator, seems like doing the right thing, if I understood you
class Class1 {
}
class Class2 {
var myOp: Class1?
}
func myFunc(c: Class2) -> AnyObject {
if let c1 = c.myOp{
return c1.self
}
return c
}
var object = Class2()
object.myOp = Class1()
myFunc(object) // Class1

How to test a swift generic type another one?

I have some generic type class but no instance of object to test. What I would like to do is to alter the behavior of the function according to the runtime type.
class MyGenericUtility<SomeGenericClass> {
func myFunction() {
// so far I have tested "is", "==" and "==="
if SomeGenericClass is SomeRealClass {
println("some special stuff there")
}
println("some generic stuff as the name tells")
}
}
You can compare the class type, using SomeGenericClass.self == SomeRealClass.self as,
class MyGenericUtility<SomeGenericClass> {
func myFunction() {
if SomeGenericClass.self == SomeRealClass.self {
print("SomeRealClass stuffs")
} else if SomeGenericClass.self == String.self {
print("String stuffs")
}
}
}
let someRealUtility = MyGenericUtility<SomeRealClass>()
someRealUtility.myFunction()
let stringUtility = MyGenericUtility<String>()
stringUtility.myFunction()
Rather than testing at runtime, you should generally handle this at compile time with constrained extensions (this assumes Swift 2). Doing it this way avoids any need to do unsafe as! casting when you need to access type-specific parts of the instance.
class MyGenericUtility<SomeGenericClass> {
}
// Special handling for `SomeRealClass`
extension MyGenericUtility where SomeGenericClass: SomeRealClass {
func myFunction() {
print("SomeRealClass stuffs")
}
}
// Default handling for any unspecified class
extension MyGenericUtility {
func myFunction() {
print("Other stuffs")
}
}
let someRealUtility = MyGenericUtility<SomeRealClass>()
someRealUtility.myFunction()
let stringUtility = MyGenericUtility<String>()
stringUtility.myFunction()
Note that this is based on inheritance, not equality, so any subclass of SomeRealClass would get the SomeRealClass behavior.
You can't use the generic type directly, you need to use a property of that type when comparing with "is".
class MyGenericUtility<T> {
var a: T
func myFunction() {
if a is Int {
println("some special stuff there")
}
println("some generic stuff as the name tells")
}
init(value: T) {
a = value
}
}
let test = MyGenericUtility(value: 5)
test.myFunction()
// Output: some special stuff there
// some generic stuff as the name tells
let test2 = MyGenericUtility(value: "foo")
test2.myFunction()
// Output: some generic stuff as the name tells

Swift generic function calling function with return type overload

just a quick question. I have the following code, which works just fine:
class obA: Printable {
var description: String { get { return "obA" } }
}
class obB: Printable {
var description: String { get { return "obB" } }
}
func giveObject() -> obA { return obA() }
func giveObject() -> obB { return obB() }
var a: obA = giveObject()
var b: obB = giveObject()
println(a)
println(b)
The right variant of giveObject is being called and all is well. Of course this is just a simplified case, in reality in my project there are several dozens of overloads of 'giveObject', all differing in return type. Now, I want to make a generic function to parse all these things. So, next step:
func giveGeneric<T>() -> T {
return giveObject()
}
var c: obA = giveGeneric()
println(c)
And this complains about ambiguous use of giveObject. I can understand where the error comes from, but I don't see right away how I can solve it and use a construct like this...
First of all just a note.
If the generic type of giveGeneric is simply T, then it can be anything (a String, an Int, ...). So how should giveObject() react in this case?
I mean, if you write:
let word : String = giveGeneric()
internally your generic function calls something like:
let result : String = giveObject() // Ambiguous use of giveObject
My solution
I declared a protocol as follow:
protocol MyObject {
init()
}
Then I made your 2 classes conform to the protocol
class obA: Printable, MyObject {
var description: String { get { return "obA" } }
required init() {}
}
class obB: Printable, MyObject {
var description: String { get { return "obB" } }
required init() {}
}
Finally I can write this
func giveGeneric<T:MyObject>() -> T {
return T()
}
Now I can use it:
let a1 : obA = giveGeneric()
let b1 : obB = giveGeneric()
You decide if this is the solution you were looking for or simply a workaround.
That cannot work, even if you implement a giveObject function for any possible type. Since T can be any type, the giveGeneric method cannot determine the correct overload to invoke.
The only way I can think of is by creating a huge swift with as many cases as the number of types you want to handle:
func giveGeneric<T>() -> T? {
switch "\(T.self)" {
case "\(obA.self)":
return giveObject() as obA as? T
case "\(obB.self)":
return giveObject() as obB as? T
default:
return .None
}
}
But I don't think I would use such a solution even with a gun pointed at my head - it's really ugly.
If in all your cases you create instances using a parameterless constructor, then you might create a protocol and constraint the T generic type to implement it:
protocol Instantiable {
init()
}
func giveGeneric<T: Instantiable>() -> T {
return T()
}
You can use with built-in as well as new types - for instance:
extension String : Instantiable {
// `String` already implements `init()`, so nothing to add here
}
let s: String = giveGeneric()
Alternatively, if you prefer you can make the protocol declare a static giveObject method rather than a parameterless constructor:
protocol Instantiable {
static func giveObject() -> Self
}
func giveGeneric<T: Instantiable>() -> T {
return T.giveObject()
}
extension String : Instantiable {
static func giveObject() -> String {
return String()
}
}
let s: String = giveGeneric()

Can you override between extensions in Swift or not? (Compiler seems confused!)

I've been working on an iOS application in Swift (much of it being moved from Objective-C). I'm using Core Data and trying to use extensions to add functionality to classes auto-generated from my model. One thing I readily did in Objective-C was to add a method in a category on class A and override that method in a category on class B (which derived from A), and I was hoping to do the same in Swift.
For a while now I've had the following code in my project (and this is just one example), and though I have not used the functionality yet, the compiler has worked just fine compiling this code:
// From CellType.swift -- NOTE: Imports from Foundation and CoreData
#objc(CellType)
class CellType: NSManagedObject {
#NSManaged var maxUses: NSNumber
#NSManaged var useCount: NSNumber
// Other properties removed for brevity
}
// From SwitchCellType.swift -- NOTE: Imports from Foundation and CoreData
#objc(SwitchCellType)
class SwitchCellType: CellType {
#NSManaged var targetCellXIndex: NSNumber
#NSManaged var targetCellYIndex: NSNumber
#NSManaged var targetCellType: CellType
// Other properties removed for brevity
}
// From CellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension CellType
{
var typeLabel : String { get { return "Empty"; } }
func isEqualToType(otherCellType : CellType) -> Bool
{
return (self.typeLabel == otherCellType.typeLabel &&
self.maxUses.isEqualToNumber(otherCellType.maxUses) &&
self.useCount.isEqualToNumber(otherCellType.useCount));
}
// Code removed for brevity
}
// From SwitchCellTypeLogic.swift -- NOTE: Imports from Foundation and CoreData
extension SwitchCellType // YES, this compiles with the overrides!
{
override var typeLabel : String { get { return "Switch"; } }
override func isEqualToType(otherCellType : CellType) -> Bool
{
var answer = false;
if let otherSwitchCellType = otherCellType as? SwitchCellType
{
answer = super.isEqualToType(otherCellType) &&
self.targetCellXIndex.isEqualToNumber(otherSwitchCellType.targetCellXIndex) &&
self.targetCellYIndex.isEqualToNumber(otherSwitchCellType.targetCellYIndex) &&
self.targetCellType.isEqualToType(otherSwitchCellType.targetCellType);
}
return answer;
}
// Code removed for brevity
}
Hopefully some kind Swift expert out there already sees my issue, but here's how I found out about it: Recently I tried to add similar functionality using methods that have parameters and/or return values that are not built in types, but I started getting this error: Declarations in extensions cannot override yet.
To explore this issue I added the following to one of my swift files, thinking it would compile just fine:
class A
{
}
class B : A
{
}
extension A
{
var y : String { get { return "YinA"; } }
}
extension B
{
override var y : String { get { return "YinB"; } } // Compiler error (see below) -- What??
}
To my surprise, I received the same compiler error (Declarations in extensions cannot override yet). What? But I've used that patter several times already without compiler errors.
Questions:
First, are there certain rules about overriding in extensions such that in some cases it is supposed to work but in other cases it is not? Second (and more disconcerting) why does it seem that the Swift compiler is so inconsistent? What am I missing here? Please help me restore my faith in Swift.
UPDATE:
As noted in the correct answer by Martin R, it seems you can override methods in the current version of Swift (1.1 via Xcode 6.1) as long as they (1) involve only classes derived from NSObject and (2) do not use the inout modifier. Here's some examples:
class A : NSObject { }
class B : A { }
class SubNSObject : NSObject {}
class NotSubbed {}
enum SomeEnum { case c1, c2; }
extension A
{
var y : String { get { return "YinA"; } }
func f() -> A { return A(); }
func g(val: SubNSObject, test: Bool = false) { }
func h(val: NotSubbed, test: Bool = false) { }
func j(val: SomeEnum) { }
func k(val: SubNSObject, inout test: Bool) { }
}
extension B
{
// THESE OVERIDES DO COMPILE:
override var y : String { get { return "YinB"; } }
override func f() -> A { return A(); }
override func g(val: SubNSObject, test: Bool) { }
// THESE OVERIDES DO NOT COMPILE:
//override func h(val: NotSubbed, test: Bool = false) { }
//override func j(val: SomeEnum) { }
//override func k(val: SubNSObject, inout test: Bool) { }
}
It seems that overriding methods and properties in an extension works with the
current Swift (Swift 1.1/Xcode 6.1) only for Objective-C compatible
methods and properties.
If a class is derived from NSObject then all its members are automatically available
in Objective-C (if possible, see below). So with
class A : NSObject { }
your example code compiles and works as expected. Your Code Data extension overrides
work because NSManagedObject is a subclass of NSObject.
Alternatively, you can use the #objc attribute for a method or property:
class A { }
class B : A { }
extension A
{
#objc var y : String { get { return "YinA" } }
}
extension B
{
#objc override var y : String { get { return "YinB" } }
}
Methods which are not representable in Objective-C cannot be marked with #objc
and cannot be overridden in a subclass extension. That applies for example to
methods having inout parameters or parameters of an enum type.
I experienced this on Xcode9. Closing and reopening Xcode worked for me. Probably a bug in the compiler.