Swift中如何在URL请求返回前阻止App启动至初始视图控制器?
问题分析
你这段代码的核心问题有两个:
fetchUser是异步网络请求,didFinishLaunchingWithOptions会直接返回true,APP随即启动初始视图控制器,此时请求大概率还没完成,fetchBaseData自然是nil。- 你每次调用
APICaller()都是创建新实例,回调里给新实例的fetchBaseData赋值,初始视图控制器里的APICaller实例根本拿不到这个值。
解决方案
第一步:把APICaller改成单例(必做)
只有用单例才能保证赋值和读取的是同一个实例的属性:
class APICaller { static let shared = APICaller() // 全局单例 var fetchBaseData: BaseData? // 替换成你的实际数据类型 func fetchUser(completionHandler: @escaping (BaseData?, Error?) -> Void) { // 你的网络请求逻辑 } }
方案一:在AppDelegate中等待请求完成(适合iOS12及以下)
用DispatchGroup等待请求结束,注意要设置超时时间,避免APP长时间卡顿:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { let group = DispatchGroup() var fetchedData: BaseData? group.enter() APICaller.shared.fetchUser { baseData, error in fetchedData = baseData group.leave() } // 等待请求完成,超时时间设为10秒(可按需调整) let waitResult = group.wait(timeout: .now() + 10) if waitResult == .success, let data = fetchedData { APICaller.shared.fetchBaseData = data } return true }
⚠️ 注意:这种方式会阻塞主线程,APP启动屏会停留到请求完成,建议配合启动屏优化用户体验。
方案二:iOS13+用SceneDelegate延迟加载根控制器(推荐)
这种方式不会阻塞主线程,启动屏会正常显示,请求完成后再加载初始视图控制器:
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let windowScene = (scene as? UIWindowScene) else { return } let window = UIWindow(windowScene: windowScene) self.window = window APICaller.shared.fetchUser { baseData, error in if let data = baseData { APICaller.shared.fetchBaseData = data } // 请求完成后再初始化并显示根控制器 let rootVC = YourInitialViewController() // 替换成你的初始VC类名 window.rootViewController = UINavigationController(rootViewController: rootVC) window.makeKeyAndVisible() } }
方案三:在初始视图控制器中处理数据加载
如果允许初始视图控制器先显示,再等待数据更新UI,可以这么做:
class YourInitialViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // 显示加载指示器 let loadingIndicator = UIActivityIndicatorView(style: .large) loadingIndicator.center = view.center loadingIndicator.startAnimating() view.addSubview(loadingIndicator) // 检查数据是否已加载 if let data = APICaller.shared.fetchBaseData { updateUI(with: data) loadingIndicator.stopAnimating() } else { // 重新请求数据 APICaller.shared.fetchUser { [weak self] baseData, error in DispatchQueue.main.async { loadingIndicator.stopAnimating() guard let data = baseData else { // 这里处理请求失败的逻辑,比如显示错误提示 return } APICaller.shared.fetchBaseData = data self?.updateUI(with: data) } } } } private func updateUI(with data: BaseData) { // 根据数据更新你的UI } }
内容的提问来源于stack exchange,提问作者Andikatama
相关产品推荐
相关产品推荐

