Search code examples
swiftpattern-matchingoptional-binding

Swift Pattern Matching - Switch, downcasting, and optional binding in a single statement


Is there a way to consolidate the switch statement below to include the optional binding?

A little background context...

First here's my result type:

enum Result<Value> {
    case success(Value)
    case failure(Swift.Error)
}

An extension on Facebook's SDK:

func start(_ completionHandler: @escaping (Result<Any>) -> Void) -> FBSDKGraphRequestConnection
{
    return start() { (_, response, error) in
        switch (response, error) {
        case (.some(let result), .none):
            completionHandler(Result(value: result))

        case (.none, .some(let error)):
            completionHandler(Result(error: error))

        case (.none, .none), (.some, .some):
            preconditionFailure("Unexpected State")
        }
    }
}

And, finally, the bit of code I'm trying to refactor. My goal is to avoid the duplicate self.present(error:) code:

_ = FBSDKGraphRequest.emailRequest().start() { (result: Result<Any>) in

    switch result {
    case .success (let value as [ String : Any ]):
        ...
        if let email = value["email"] as? String {
            ...
        }
        else {
            self.present(error: Error(.facebookOther))
        }

    default:
        self.present(error: Error(.facebookOther))
    }
}

Solution

  • I'm not sure you can eliminate the duplication with a switch and optionally bind email, but you can with an if:

    if case .success(let value as [String : Any]) = result, let email = value["email"] as? String {
        print("email: \(email)")
    } else {
        self.present(error: Error(.facebookOther))
    }
    

    The best you can do with a switch is to avoid the duplication by adding a where clause, but you miss out on the optional binding of email:

    switch result {
    case .success (let value as [ String : Any ]) where value["email"] is String:
        let email = value["email"] as! String
        print("email: \(email)")
    default:
        self.present(error: Error(.facebookOther))
    }