You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:55:28