Swift结构体中调用present方法报错的解决方案
问题原因
报错的核心是:FirebaseServices是嵌套结构体,它的静态方法createUser里直接调用present方法,但present是UIViewController的实例方法,只有视图控制器的实例才能调用,而结构体FirebaseServices并不是SignUpViewController的实例,因此无法直接使用。
解决方案
下面提供两种可行的解决思路:
方案一:通过回调将错误抛回视图控制器处理(推荐)
把弹窗逻辑从Firebase服务层剥离,让服务层只负责业务逻辑,UI展示交给视图控制器处理。修改createUser的completion闭包,传递错误信息,在SignUpViewController调用方法时处理弹窗。
修改后的完整代码:
import UIKit import FirebaseAuth import FirebaseFirestore import FirebaseStorage extension SignUpViewController { struct SignUpModelFirebase { let emailText : String let passwordText : String let nameText : String let usernameText : String let profileImage : UIImage } struct FirebaseServices { // 保留completion传递错误,移除内部弹窗逻辑 static func createUser(user : SignUpModelFirebase, completion: @escaping(Error?) -> Void) { let storage = Storage.storage() let storageReference = storage.reference() let mediaFolder = storageReference.child("Profile Image") if let data = user.profileImage.jpegData(compressionQuality: 0.5) { let uuidImg = UUID().uuidString let imageReference = mediaFolder.child("\(uuidImg)") imageReference.putData(data) { storageMetaData, error in if let error = error { completion(error) print(error.localizedDescription) }else { imageReference.downloadURL { url, error in if let error = error { completion(error) }else { guard let imageURL = url?.absoluteString else { completion(NSError(domain: "YourApp", code: -1, userInfo: [NSLocalizedDescriptionKey: "获取图片链接失败"])) return } print(imageURL) Auth.auth().createUser(withEmail: user.emailText, password: user.passwordText) { data, error in if let error = error { completion(error) return } guard let uid = data?.user.uid else { completion(NSError(domain: "YourApp", code: -2, userInfo: [NSLocalizedDescriptionKey: "获取用户ID失败"])) return } let data = [ "email" : user.emailText, "username" : user.usernameText, "name" : user.nameText, "profileImageUrl" : imageURL, "uid" : uid ] as [String : Any] let firestoreDatabase = Firestore.firestore() firestoreDatabase.collection("Users").document(uid).setData(data,completion: completion) } } } } } } else { completion(NSError(domain: "YourApp", code: -3, userInfo: [NSLocalizedDescriptionKey: "图片转换失败"])) } } } }
在SignUpViewController中调用并处理弹窗:
// 示例:注册按钮点击事件 @IBAction func signUpButtonTapped(_ sender: UIButton) { guard let email = emailTextField.text, let password = passwordTextField.text, let name = nameTextField.text, let username = usernameTextField.text, let profileImage = profileImageView.image else { return } let userModel = SignUpModelFirebase(emailText: email, passwordText: password, nameText: name, usernameText: username, profileImage: profileImage) FirebaseServices.createUser(user: userModel) { [weak self] error in guard let self = self else { return } DispatchQueue.main.async { if let error = error { let alert = UIAlertController(title: "错误", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "确定", style: .default)) self.present(alert, animated: true) } else { // 注册成功逻辑,如跳转首页 } } } }
方案二:传递视图控制器实例到服务层(不推荐)
如果一定要在服务层处理弹窗,可以给createUser添加参数传入SignUpViewController实例,通过该实例调用present。但这种方式会让业务层和UI层耦合,不利于后期维护。
修改后的方法片段:
static func createUser(user : SignUpModelFirebase, viewController: SignUpViewController, completion: @escaping(Error?) -> Void) { // ...其他代码不变 if let error = error { DispatchQueue.main.async { let alert = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK!", style: .default)) viewController.present(alert, animated: true) } completion(error) print(error.localizedDescription) } // ...其他代码不变 }
调用时传入当前视图控制器:
FirebaseServices.createUser(user: userModel, viewController: self) { error in // 后续逻辑处理 }
最佳实践建议
优先选择方案一,它遵循单一职责原则:业务逻辑层专注处理Firebase相关操作,UI展示由视图控制器负责,代码结构更清晰,便于后续维护和测试。同时注意:所有UI操作必须在主线程执行,因此弹窗代码要包裹在DispatchQueue.main.async中,避免线程问题。
内容的提问来源于stack exchange,提问作者7mach
相关产品推荐
相关产品推荐

