3

I want to implement a function like this one:

protocol Base {
    var value: Int { get set }
}
class ObjectTypeA: Base {
    var value: Int = 0
}
class ObjectTypeB: Base {
    var value: Int = 1
}

var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
    objects.filter({ $0 is type }).forEach { // <<< ERROR IS HERE
        var object = $0
        object.value = value
    }
}
updatePropertyForType(ObjectTypeB.self, value: 10)

But there is an error:

'type' is not a type

Please, help me fix it.

adnako
  • 1,287
  • 2
  • 20
  • 30
  • Possible duplicate of [How to check two instances are the same class/type in swift](http://stackoverflow.com/questions/24161622/how-to-check-two-instances-are-the-same-class-type-in-swift) – Daniel Jun 27 '16 at 08:51
  • As there already mentioned - "This isn't actually pure Swift", I'd like to find out a right kosher way to solve this example. – adnako Jun 27 '16 at 08:54
  • @simpleBob, Could you, please, just fix my code to show me what do you mean - "already answered"? I can't find an answer there. – adnako Jun 27 '16 at 08:56

3 Answers3

2

See this answer:

protocol Base: AnyObject {
    var value: Int { get set }
}
class ObjectTypeA: Base {
    var value: Int = 0
}
class ObjectTypeB: Base {
    var value: Int = 1
}

var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
    objects.filter({let item = $0; return type === item.dynamicType }).forEach {
        $0.value = value
    }
}

call it with:

updatePropertyForType(ObjectTypeA.self, value: 3)
Community
  • 1
  • 1
Daniel
  • 20,420
  • 10
  • 92
  • 149
  • Thanks, but now I have to create a dumb object to use this function instead sending a type. Is there a better solution? – adnako Jun 27 '16 at 09:03
  • Yes, I've found a same solution, thanks. Just one little remark - **$0.value = value** is wrong because a **value** property is immutable. – adnako Jun 27 '16 at 09:35
  • @adnako It works if `Base` inherits from `AnyObject` – Daniel Jun 27 '16 at 09:59
1

But this code works just fine.

protocol Base {
    var value: Int { get set }
}
class ObjectTypeA: Base {
    var value: Int = 0
}
class ObjectTypeB: Base {
    var value: Int = 1
}

var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]
func updatePropertyForType(type: Base.Type, value: Int) {
    objects.filter({ object in
        let result = object.dynamicType == type
        return result
    }).forEach {
        var object = $0
        object.value = value
    }
}
updatePropertyForType(ObjectTypeB.self, value: 10)
print(objects)
adnako
  • 1,287
  • 2
  • 20
  • 30
1

As the other answers have eluded to, you cannot use meta-types with is. However, a nicer solution would be to simply use generics. This will allow Swift to infer the type you pass into the function, allowing you to write it as:

protocol Base : class {
    var value: Int { get set }
}

class ObjectTypeA: Base {
    var value: Int = 0
}
class ObjectTypeB: Base {
    var value: Int = 1
}

var objects: [Base] = [ObjectTypeA(), ObjectTypeB()]

func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
    objects.filter{ $0 is T }.forEach{ $0.value = value }
}

updateElements(ofType: ObjectTypeB.self, withValue: 10)

You'll also want to make your Base protocol class bound (: class) in order to allow Swift to treat Base typed instances as reference types (allowing you to do $0.value = value).

Your previous code of:

var object = $0
object.value = value

would have worked for reference types, but not for value types – as object here is simply a copy of $0, so any mutations won't be reflected in the array. Therefore you should annotate your protocol to make it clear that your protocol isn't designed to be used with value types.

If you do want to be able to handle value types, you may want to consider using map instead:

func updateElements<T:Base>(ofType type: T.Type, withValue value: Int) {
    objects = objects.map {
        var object = $0
        if object is T {
            object.value = value
        }
        return object
    }
}
Hamish
  • 78,605
  • 19
  • 187
  • 280
  • Thanks, I did not thought about generics! Unfortunately I can't modify **Base** protocol, it's a third party framework. I know about value and reference types, but thank you for clarifying it on public. – adnako Jun 27 '16 at 11:33
  • @adnako Ah okay, I suppose you could create an intermediate protocol that is both class bound and conforms to `Base` – and then conform your classes to it and use it as your array type, although that may or may not be worth the extra effort. Happy to help :) – Hamish Jun 27 '16 at 12:10