【问题标题】:Throw Error in function taking escaping closure在转义闭包的函数中抛出错误
【发布时间】:2018-06-10 19:43:50
【问题描述】:

我正在尝试编写一个在 Firebase 中注册新用户的自定义函数。我在一个名为 DatabaseManager 的类中导入了 Firebase。在那里,我管理所有数据库交互。在这个类中,我想要一个自定义函数来添加用户,这会引发所有 Firebase 错误。这样我就可以在 ViewController 类中使用该函数,我可以在其中捕获错误并显示警报。 但是,我似乎无法使该功能正常工作,而且我不确定自己做错了什么。

这是我的功能:

enum createAccountError : Error{
        case emailInUse, weakPassword, networkError, unknownError
    }

    //Mark: create a user profile
    ///create account with email, password, username, phoneNumber, birthDate, name
    func createAccount(_ userModel: UserModel, _ password: String?, completion: @escaping (_ inner: ()throws -> Bool)->())  {
        Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in
            if let error = error {
                if let errCode = AuthErrorCode(rawValue: error._code) {
                    switch errCode {
                    case .emailAlreadyInUse:
                        completion({throw createAccountError.emailInUse})
                    case .weakPassword:
                        completion({throw createAccountError.weakPassword})
                    case .networkError:
                        completion({throw createAccountError.networkError})
                    default:
                        completion({throw createAccountError.unknownError})
                    }
                }
                return
            } else {
                completion({return true})
            }
        })
    }

这是我尝试使用它的方法:

DatabaseManager.system.createAccount(user, password) { (( inner: ()throws -> Bool)->()) in
            do{
                let result = try inner()
            } catch .emailInUse{
                //show alert
            }
            }

【问题讨论】:

  • 请更新您的问题(不要发布 cmets),以清楚地解释您发布的代码存在哪些问题。
  • maddy,不确定您的意思,我只想问如何使用函数。我使用它的方式不起作用。
  • “不起作用”不是一个有用的陈述。它在哪些方面不完全有效?

标签: swift escaping closures try-catch throw


【解决方案1】:

我建议重构你的函数如下:

func createAccount(_ userModel: UserModel, _ password: String?, completion: @escaping(Error?) -> ())  {
    Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in
        if let error = error {
            if let errCode = AuthErrorCode(rawValue: error._code) {
                switch errCode {
                case .emailAlreadyInUse:
                    completion(createAccountError.emailInUse)
                case .weakPassword:
                    completion(createAccountError.weakPassword)
                case .networkError:
                    completion(createAccountError.networkError)
                default:
                    completion(createAccountError.unknownError)
                }
            }

        } else {
            completion(nil)
        }
    })
}

然后当你调用该函数时,你可以检查是否有这样的错误:

DatabaseManager.system.createAccount(user, password) { (error) in
   guard error == nil else {
      //Handle error
      return
   }

   //There was no error

【讨论】:

    【解决方案2】:

    我已经为演示创建了测试功能,一切正常

    // error type
    enum TestError: Error { case notFound, empty }
    // define typealias for eiser usage of complex function type
    typealias ThrowableCallback = () throws -> Bool
    
    func createAccount(_ shouldThrow: Bool, completion: @escaping (_ inner: ThrowableCallback) -> Void) {
      // call completion callback asynchronously
      DispatchQueue.main.async {
        if shouldThrow {
          // throw error
          completion({ throw TestError.notFound })
        }
        // return value
        completion({ return true })
      }
    }
    

    用法:

    createAccount(true) { (inner: ThrowableCallback) -> Void in
            do {
              let success = try inner()
              print(success)
            } catch {
              print(error)
            }
          }
    

    UPD:我不建议使用这种技术来处理异步函数中的错误。使用单独的成功和失败回调或 Promises 来优雅地处理异步代码(查看this 了解更多信息)

    UPD 2:实际解决方案

    typealias ThrowableCallback = () throws -> User 
    func createAccount(_ userModel: UserModel,
                       _ password: String,
                       completion: @escaping (_ inner: ThrowableCallback) -> Void) {
      Auth.auth().createUser(withEmail: userModel.email!, password: password!, completion: {(user, error) in 
        if let error = error { completion({ throw error }) } 
        else { completions({ return user! }) } 
      }) 
    }
    
    // usage
    createAccount(userModel, somePassword) { (inner: ThrowableCallback) -> Void in
            do {
              let createdUser = try inner()
            } catch {
              ler errCode = (error as NSError)._code
              switch errCode {
                 case .emailAlreadyInUse:
                   showAlert("Email is already in use")
                 case .weakPassword:
                   showAlert("please enter stronger password ")
                 case .networkError:
                   showAlert("it seams that there is no internet connection")
                 default:
                   showAlert("Error creating user. Please try again later")
                 }
            }
          }
    

    【讨论】:

    • 我喜欢你的解决方案。关于你不使用它的建议,看看我这样做的全部原因是因为我想在另一个类中处理 Firebase 错误,我将使用这个函数 createAccount()。老实说,重新审视我的代码,我认为从 switch 案例中生成更多枚举可能不是最好的方法,但我不确定如何转移 AuthErrorCode 错误。这样我就可以在其他课程中处理它。你知道我该怎么做吗?
    • 不错,看起来很棒。我会试一试。谢谢
    • Andy C,所以我的代码实际上有问题。使用时,line let errCode = (error as NSError)._code,错误类型其实是AuthErrorCode。所以问题是我不能在我打算使用该功能的课程中继续使用它。有什么办法可以解决这个问题?
    • @dre_84w934 对不起,我忘记了。您应该使用此代码let errCode = AuthErrorCode(rawValue: (error as NSError)._code) 将其转换为 AuthErrorCode 类型。然后一切都应该正常工作。
    • 是的,你是对的,这是最简单的部分 :) 让我知道这个解决方案是否对你有好处,我会更新我的答案
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-14
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多