3

I have a method which does exactly the same thing for two types of data in Swift.

To keep things simple (and without duplicating a method) I pass AnyObject as an argument to my method which can be either of these two types. How to I unwrap it with an || (OR) statement so I can proceed? Or maybe this done otherwise?

func myFunc(data:AnyObject) {

    if let data = data as? TypeOne {
        // This works fine. But I need it to look something like unwrapping below
    }

    if let data = data as? TypeOne || let data = data as? TypeTwo { // <-- I need something like this
        // Do my stuff here, but this doesn't work
    }

}

I'm sure this is trivial in Swift, I just can't figure out how to make it work.

kernelpanic
  • 2,876
  • 3
  • 34
  • 58
  • No it's not trivial. Assume that your second cast works. Now how do you know if `data` is `TypeOne` or `TypeTwo`? Better to use `if TypeOne { } else if TypeTwo { }` – Code Different Jul 28 '15 at 19:05
  • That's why I asked if there is an "OR" ``||``. Either one of two type may pass, and that should suffice to continue.. But from some answers below I see that there may not be an "OR" :) – kernelpanic Jul 28 '15 at 19:09
  • 2
    Maybe you should reconsider your design. In OO, type checking and casts are considered code smell at should be avoided. For example, you could introduce a common protocol for both types. – Clashsoft Jul 28 '15 at 19:09

4 Answers4

2

You can't unify two different casts of the same thing. You have to keep them separate because they are two different casts to two different types which the compiler needs to treat in two different ways.

var x = "howdy" as AnyObject
// x = 1 as AnyObject

// so x could have an underlying String or Int
switch x {
case let x as String:
    print(x)
case let x as Int:
    print(x)
default: break
}

You can call the same method from within those two different cases, if you have a way of passing a String or an Int to it; but that's the best you can do.

func printAnything(what:Any) {
    print(what)
}
switch x {
case let x as String:
    printAnything(x)
case let x as Int:
    printAnything(x)
default: break
}

Of course you can ask

if (x is String || x is Int) {

but the problem is that you are no closer to performing an actual cast. The casts will still have to be performed separately.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • Yeah, switch works better in this case so I can reuse the unwrapped variable. Thank you! – kernelpanic Jul 28 '15 at 19:11
  • `var x = "howdy" as AnyObject` this is so horrible from my point of view, because you're bridging your type to an Objc type and trying to match it with standard swift types. Sure it does it's job, but I still hope we can get rid of Objc stuff as soon as possible and use pure Swift. – DevAndArtist Jul 28 '15 at 20:19
1

Building on Clashsoft's comment, I think a protocol is the way to go here. Rather than pass in AnyObject and unwrap, you can represent the needed functionality in a protocol to which both types conform.

This ought to make the code easier to maintain, since you're coding toward specific behaviors rather then specific classes.

I mocked up some code in a playground that shows how this would work.

Hopefully it will be of some help!

protocol ObjectBehavior {
    var nickname: String { get set }
}

class TypeOne: ObjectBehavior {
    var nickname = "Type One"
}

class TypeTwo: ObjectBehavior {
    var nickname = "Type Two"
}

func myFunc(data: ObjectBehavior) -> String {
    return data.nickname
}

let object1 = TypeOne()
let object2 = TypeTwo()

println(myFunc(object1))
println(myFunc(object2))
Joshua Kaden
  • 1,210
  • 11
  • 16
0

Find if that shared code is exactly the same for both types. If yes:

protocol TypeOneOrTypeTwo {}

extension TypeOneOrTypeTwo {

    func thatSharedCode() {

        print("Hello, I am instance of \(self.dynamicType).")
    }
}

extension TypeOne: TypeOneOrTypeTwo {}
extension TypeTwo: TypeOneOrTypeTwo {}

If not:

protocol TypeOneOrTypeTwo {

    func thatSharedMethod()
}

extension TypeOne: TypeOneOrTypeTwo {

    func thatSharedMethod() {

        // code here:
    } 
}

extension TypeTwo: TypeOneOrTypeTwo {

    func thatSharedMethod() {

        // code here:
    } 
}

And here you go:

func myFunc(data: AnyObject) {

    if let data = data as? TypeOneOrTypeTwo {

        data.thatSharedCode() // Or `thatSharedMethod()` if your implementation differs for types.
    }
}
Stanislav Smida
  • 1,565
  • 17
  • 23
0

You mean like this?

enum IntOrString {
    case int(value: Int)
    case string(value: String)
}

func parseInt(_ str: String) -> IntOrString {
    if let intValue = Int(str) {
        return IntOrString.int(value: intValue)
    }
    return IntOrString.string(value: str)
}

switch parseInt("123") {
case .int(let value):
    print("int value \(value)")
case .string(let value):
    print("string value \(value)")
}

switch parseInt("abc") {
case .int(let value):
    print("int value \(value)")
case .string(let value):
    print("string value \(value)")
}

output:

int value 123
string value abc
Doug Coburn
  • 2,485
  • 27
  • 24