如何在 Swift 中的某些视图控制器之间正确共享变量?



我是编程和iOS开发的初学者,我正在尝试制作一个使用标签栏控制器的应用程序,它有5个标签栏。

我创建了一个User类,其中包含电子邮件,uid,姓名和其他用户数据(例如其住所等(作为属性。这个类在标签栏控制器(index:0(中第一个标签栏的视图控制器中初始化,假设我们称之为HomeVC。User类的实例使用来自服务器的数据进行初始化。

这个User类的实例将在我的应用中多次使用。 假设当用户在位于第三个选项卡栏导航堆栈(索引:2(的底部/最后一个VC的PostVC中创建帖子时。 以及当用户在配置文件VC中看到自己的配置文件时。

我认为反复请求在我的应用程序中的某个地方获取用户数据不是一个好的做法,因为目前我使用的是 Firestore 数据库,读取数据库越多意味着它需要更多的成本。 所以我只想向服务器发出一个请求来获取用户数据,然后将该用户数据用于我的整个应用程序。

此时我创建了一个超级全局变量(位于类外部的变量(,因此我可以在我的应用程序中的任何位置访问它。 但我刚刚读到使用超级全局变量是一种糟糕的编程实践。

那么我该怎么办呢? 我必须使用prepareForSegue传递用户数据吗? 有没有更好的选择来解决这个问题?

由于您已经在使用Cloud Firestore因此您可以使用Firebase身份验证仪表板(FirebaseAuth(中的各种方法将用户登录。 用户登录后,你可以从应用中的任何位置获取当前用户,如下所示:

import Firebase        
if let user = Auth.auth().currentUser {
let userID = user.uid
let email = user.email
let displayName = user.displayName
}

一旦应用程序具有有效的用户令牌,就不会继续回调服务器,除非令牌需要刷新(例如在更改用户帐户设置期间(。

https://firebase.google.com/docs/auth/ios/start

我建议在这里也避免单例。主要动机是使事情可测试。这是一种可能的方法。

您有这样的User

struct User {
let userid: String
let email: String
}

声明用于管理用户的协议。使用协议使测试更容易,因为您可以创建符合它的模拟。

protocol UserManagerProtocol {
func getSignedInUser() -> User?
func signIn(userid: String, password: String) -> User?
}

声明实现协议的实际类:

class UserManager: UserManagerProtocol {
fileprivate var signedInUser: User?
func getSignedInUser() -> User? {
return signedInUser
}
func signIn(userid: String, password: String) -> User? {
//call your server to sign in user
// if sign in fails, return nil
// if sign in works, create a User object
signedInUser = User(userid: userid, email: "email retrieved from server")
return signedInUser
}
}

在视图控制器中添加引用,但使用协议而不是实际类声明它。同样,这允许嘲笑。

class HomeVC: UIViewController {
var userManager: UserManagerProtocol?
}
class PostVC: UIViewController {
var userManager: UserManagerProtocol?
}

最后,在 AppDelegate 中,当您启动应用程序时,创建UserManager的单个实例,将其存储在 AppDelegate 中,并将其传递给所有视图控制器。在这个例子中,我显然遗漏了很多东西,因为我不知道你的AppDelegate是什么样子的。

class AppDelegate: NSObject, UIApplicationDelegate {
var userManager: UserManagerProtocol?
func applicationDidFinishLaunching(_ application: UIApplication) {
userManager = UserManager()
//when setting up your tab bar controllers, pass the userManager instance to each of them
}
}

我认为你应该避免单例。我学到的最好的解决方案之一是为您的数据管理器创建协议(如会话、购物车......之后,您应该尽可能远离类推送依赖项。在本例中为AppDelegate。因此,您可以为这些类创建属性以保存在那里。如果你有很多这样的人,把它们放在一个班级或其他东西上。

小例子:

@objc protocol SessionManagerProtocol {
func persistUser()
}

然后在您的应用程序委托中:(使其线程安全(

var sessionManager: SessionManagerProtocol!
func getSessionManager() -> SessionManagerProtocol {
if let manager = sessionManager {
return manager
}
sessionManager = SessionManager()
return sessionManager
}

使用此解决方案,如果要使其可测试,还可以注入依赖项。

如果您的应用程序进入后台模式,则将会话保存到 userdefault 或您自己的数据库。

如果您只想在视图控制器之间传递,请查看此链接:在视图控制器之间传递数据

人们讨厌单身人士,尽管

URLSession.shared
UIApplication.shared
NotificationCenter.default

还有更多,几乎每个Apple框架都有一个单例类,只要它记录得很好,以便其他开发人员可以正确阅读您的代码,这不是一个坏主意

我认为对于您的情况,您可以将序列化格式的数据存储在某个文件或 CoreData DB 中.txt。您可以随时从应用的任何部分从这些位置获取数据。

最新更新