Best practice for Swift methods that can return or error

If you want to handle errors that can happen during login process than use the power of Swift error handling:

struct User {
}

enum SecurityError: Error {
    case emptyEmail
    case emptyPassword
}

class SecurityService {
    static func loginWith(email: String, password: String) throws -> User {
        if email.isEmpty {
            throw SecurityError.emptyEmail
        }
        if password.isEmpty {
            throw SecurityError.emptyPassword
        }
        return User()
    }    
}

do {
    let user = try SecurityService.loginWith1(email: "", password: "")
} catch SecurityError.emptyEmail {
    // email is empty
} catch SecurityError.emptyPassword {
    // password is empty
} catch {
    print("\(error)")
}

Or convert to optional:

guard let user = try? SecurityService.loginWith(email: "", password: "") else {
    // error during login, handle and return
    return
}

// successful login, do something with `user`

If you just want to get User or nil:

class SecurityService {    
    static func loginWith(email: String, password: String) -> User? {
        if !email.isEmpty && !password.isEmpty {
            return User()
        } else {
            return nil
        }
    }
}

if let user = SecurityService.loginWith(email: "", password: "") {
    // do something with user
} else {
    // error
}

// or

guard let user = SecurityService.loginWith(email: "", password: "") else {
    // error
    return
}

// do something with user

Besides the standard way to throw errors you can use also an enum with associated types as return type

struct User {}

enum LoginResult {
  case success(User)
  case failure(String)
}

class SecurityService {
  static func loginWith(email: String, password: String) -> LoginResult {
    if email.isEmpty { return .failure("Email is empty") }
    if password.isEmpty { return .failure("Password is empty") }
    return .success(User())
  }
}

And call it:

let result = SecurityService.loginWith("Foo", password: "Bar")
switch result {
  case .Success(let user) : 
     print(user) 
     // do something with the user
  case .Failure(let errormessage) : 
     print(errormessage) 
    // handle the error
}

Tags:

Swift