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

iOS Swift应用模拟器可显示Firestore数据,真机无法显示求助

iOS Swift应用Firestore真机数据加载异常排查

我开发的iOS Swift应用在模拟器中能正常读取Firestore数据并展示,但在iPhone真机上数据为空,无法显示。直接在FirstScreenViewController中读取数据可正常显示,排查代码未找到问题,寻求帮助。


DataManager.swift

//
//  DataManager.swift
//  Aqra3
//
//  Created by Mahmoud Abdelaziz on 14/01/2023.
//


import UIKit
import FirebaseFirestore

class DataManager {
    static let shared = DataManager()
    var delegate: DataManagerDelegate?

    //var dataCache = [String: Any]() // for screen 1 [0] is the title
    let currentDateTime = Date()
    var db: Firestore!
    var db2: Firestore!
    var db3: Firestore!

    //var db4: Firestore!
    //var dailyAdvice = String()
    var screenOneBody = String()
    var screenOneTitle = String()
    var screenTwoBody = String()
    var screenTwoTitle = String()
    var screenThreeBody = String()
    var screenThreeTitle = String()
    
    //private init() {}

    func fetchDataFromFirestore(date: String) {
        let settings = FirestoreSettings()
        Firestore.firestore().settings = settings
        db = Firestore.firestore()
        db2 = Firestore.firestore()
        db3 = Firestore.firestore()
        //db4 = Firestore.firestore()
        
        //let topicDay = String(self.currentDateTime.formatted(date: .abbreviated, time: .omitted))// exp Jan 3, 2023

        let screenOne = db.collection("Screen1").document(date)
        screenOne.getDocument{ (document, error) in
            if let document = document, document.exists {
                self.screenOneBody = document.get("Body")! as! String
                self.screenOneTitle = document.get("Title")! as! String
                print("Screen One Body: \(self.screenOneBody) \n")
                print("Screen One Title: \(self.screenOneTitle) \n")
                print("Data: \(date) \n")
               // DispatchQueue.main.async {
               //     self.delegate?.dataDidUpdate()
               // }
                let screenTwo = self.db2.collection("Screen2").document(date)
                screenTwo.getDocument{ (document, error) in
                    if let document = document, document.exists {
                        self.screenTwoBody = document.get("Body")! as! String
                        self.screenTwoTitle = document.get("Title")! as! String
                        print("Screen Two Body: \(self.screenTwoBody) \n")
                        print("Screen Two Title: \(self.screenTwoTitle) \n")
                        print("Data: \(date) \n")
                        
                        let screenThree = self.db3.collection("Screen3").document(date)
                        screenThree.getDocument { (document, error) in
                            if let document = document, document.exists {
                                self.screenThreeBody = document.get("Body")! as! String
                                self.screenThreeTitle = document.get("Title")! as! String
                                print("Screen Three Body: \(self.screenThreeBody) \n")
                                print("Screen Three Title: \(self.screenThreeTitle) \n")
                                print("Data: \(date) \n")
                            } else {
                                        print("Document does not exist")
                                        print("Data: \(date)")
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }

protocol DataManagerDelegate {
    func fetchDataFromFirestore(date: String)
    func dataDidUpdate()

}

FirstScreenViewController.swift

//
//  FirstScreenViewController.swift
//  Aqra3
//
//  Created by Mahmoud Abdelaziz on 01/01/2023.
//

import UIKit
import SwiftUI
import FirebaseFirestore

class FirstScreenViewController: UIViewController, DataManagerDelegate {
    func fetchDataFromFirestore(date: String) {
        DataManager.shared.fetchDataFromFirestore(date: date)
        changeTheBody()
        DataManager.shared.delegate?.dataDidUpdate()
        
    }
    
    //var newDateFromMenu = SideMenuViewController()
    var db: Firestore!
    let currentDateTime = Date()
    let refreshControl = UIRefreshControl()
    var refreshTimer = Timer()
    
    @IBOutlet weak var FirstScreenTextView: UITextView!
    @IBOutlet weak var firstScreenTitle: UITextView!
    

    override func viewDidLoad() {
        super.viewDidLoad()
        navigationItem.hidesBackButton = true // hide the back button
        let settings = FirestoreSettings()
        Firestore.firestore().settings = settings
        db = Firestore.firestore()
        //self.navigationController!.navigationBar.tintColor = UIColor.red // this if you want to cahnge the navigationBar color without change the global tint color

        //DataManager.shared.fetchDataFromFirestore()
        let swipeLeft = UISwipeGestureRecognizer(target: self, action: #selector(handleGesture))

        swipeLeft.direction = .right
        self.view.addGestureRecognizer(swipeLeft)
        DataManager.shared.delegate = self

        //let topicDay = String(self.currentDateTime.formatted(date: .abbreviated, time: .omitted))// exp Jan 3, 2023
        //DataManager.shared.fetchDataFromFirestore(date: topicDay)
        changeTheBody()
        
        refreshTimer = Timer.scheduledTimer(timeInterval: 0.0, target: self, selector: #selector(changeTheBody), userInfo: nil, repeats: true)
    

    }
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        refreshTimer.invalidate()
    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        refreshTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(changeTheBody), userInfo: nil, repeats: true)
    }

    
    @objc func changeTheBody() {
        FirstScreenTextView.text =  "\(DataManager.shared.screenOneBody)"
        //FirstScreenTextView.semanticContentAttribute = .forceRightToLeft
        let style = NSMutableParagraphStyle()
        style.lineSpacing = 10  // adjust line spacing as needed
        style.baseWritingDirection = .rightToLeft
        style.alignment = .justified
        let attributes = [    NSAttributedString.Key.paragraphStyle: style]
        FirstScreenTextView.attributedText = NSAttributedString(string: FirstScreenTextView.text, attributes: attributes)
        FirstScreenTextView.font = UIFont.systemFont(ofSize: 20)
        FirstScreenTextView.textColor = UIColor.white
        firstScreenTitle.textColor = UIColor.white
        firstScreenTitle.text =  "\(DataManager.shared.screenOneTitle)"

        refreshControl.endRefreshing()

    }
    @objc func handleGesture(gesture: UISwipeGestureRecognizer) -> Void {
        if gesture.direction == UISwipeGestureRecognizer.Direction.right {
            performSegue(withIdentifier: "fromFirstScreenToMainScreen", sender: self)
        }
    }
    
   /* override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "goToResult" {
            let destinationVC = segue.destination as! SideMenuViewController
 
        }
    }*/
    func dataDidUpdate() {
        changeTheBody()
    }
}

AppDelegate.swift

//
//  AppDelegate.swift
//  AQRA3
//
//  Created by Mahmoud Abdelaziz on 01/01/2023.
//

import UIKit
import FirebaseCore
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    let currentDateTime = Date()



    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let topicDay = String(self.currentDateTime.formatted(date: .abbreviated, time: .omitted))// exp Jan 3, 2023
        FirebaseApp.configure()
        DataManager.shared.fetchDataFromFirestore(date: topicDay)
        return true
    }

    // MARK: UISceneSession Lifecycle

    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        // Called when a new scene session is being created.
        // Use this method to select a configuration to create the new scene with.
        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        // Called when the user discards a scene session.
        // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
        // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
    }


}

排查方向及修复建议

1. 日期格式一致性问题

模拟器和真机的区域设置可能不同,导致生成的date字符串与Firestore文档ID不匹配:

  • 原代码用currentDateTime.formatted(date: .abbreviated, time: .omitted)生成日期,不同区域格式差异大(比如英文是"Jan 3, 2023",阿拉伯语会是完全不同的格式)。
  • 修复:改用固定格式的日期字符串,确保全平台一致:
    let formatter = DateFormatter()
    formatter.dateFormat = "yyyy-MM-dd"
    let topicDay = formatter.string(from: currentDateTime)
    

2. 网络权限与访问限制

真机可能存在网络访问限制:

  • 检查Info.plist,添加网络权限配置,确保能访问Firebase:
    <key>NSAppTransportSecurity</key>
    <dict>
        <key>NSAllowsArbitraryLoads</key>
        <true/>
    </dict>
    
  • 确认真机当前网络能正常访问Firebase服务。

3. Firestore初始化与异步回调问题

  • 原DataManager重复初始化多个Firestore实例,完全没必要,只需要初始化一次。
  • 异步加载完成后未调用delegate?.dataDidUpdate(),导致UI无法感知数据更新,需在所有数据加载完成后添加:
    DispatchQueue.main.async {
        self.delegate?.dataDidUpdate()
    }
    

4. 代理设置时机问题

AppDelegate中调用fetchDataFromFirestore时,FirstScreenViewController还未初始化,代理为nil,数据加载完成后无法通知UI。

  • 修复:将数据请求移到FirstScreenViewController的viewDidLoad中,确保代理已设置完成再发起请求。

5. 强制解包的安全问题

原代码用!强制解包Firestore字段,若字段缺失或类型错误会导致崩溃,真机上可能因此加载失败:

  • 修复:用可选绑定安全获取字段:
    if let body = document.get("Body") as? String {
        self.screenOneBody = body
    } else {
        print("Screen1 Body字段缺失或类型错误")
    }
    

内容的提问来源于stack exchange,提问作者Mahmoud

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 04:15:39