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
相关产品推荐
相关产品推荐

