我们经常会在开发时遇到这样的业务场景:
1.用户登录一次之后,再次登录app时,用户名作为默认登录账号。
2.当用户从某个页面退出时,假如这个页面有类似于轮播的组件,或者多视图选择器组件,我们希望用户登录回到APP后,仍然可以直接导航到该页面特定的轮播图或视图。
针对以上业务场景,我们需要存储用户的登录名,退出APP时当前视图的index到userDefaults中,然后在打开APP后,读取存储的key值,实现需求。
下面,我们来完整地构建一个,可扩展的、成熟的、开箱即用的userDefaults工具类。
Step1,我们来定义一个代表用户行为的类UserAction.swift
import Foundation
public enum UserAction: String {
case firstLogin
case indicatorIndex
public var name: Stirng {
rawValue
}
}
这里,我们构建了一个枚举,它是String类型,并其中定义了一个name作为之后我们UserDefault的key值。
Step2,我们来定义一个DataStore协议,遵循这个协议必须实现协议中的,读,写,删除方法。
import Foundation
import Combine
public protocol DataStore {
func read<T: Decodable>(_ key: String) -> AnyPublisher<T?, Error>
func write<T: Encodable>(_ key: String, value: T) -> AnyPublisher<Void, Error>
func delete(_ key: String) -> AnyPublisher<Void, Error>
}
在这个DataStore.swift中,我们定义了三个方法,最终它们都会返回一个新的publisher
Step3,我们需要有一个UserDefaultsStore.swift的类,去遵循DataStore协议,并对协议中的方法进行具体的实现。
import Foundation
import Combine
public class UserDefaultsStore: DataStore {
private let userDefaults: UserDefaults
private let decoder: JSONDecoder
private let encoder: JSONEncoder
init(
userDefaults: UserDefaults,
decoder: JSONDecoder,
encoder: JSONEncoder
) {
self.userDefaults = userDefaults
self.decoder = decoder
self.encoder = encoder
}
public func read<T: Decodable>(_ key: String) -> AnyPublisher<T?, Error> {
Just(key)
.tryMap { key -> T? in
guard let data = userDefaults.data(forKey: key) else { return nil }
return try decoder.decode(T.self, from: data)
}.eraseToAnyPublisher()
}
public func write<T: Encodable>(_ key: String, value: T) -> AnyPublisher<Void, Error> {
Just(())
.tryMap { emptyValue -> Void in
let data = try encoder.encode(value)
userDefaults.set(data, forKey: key)
return emptyValue
}.eraseToAnyPublisher()
}
public func delete(_ key: String) -> AnyPublisher<Void, Error> {
userDefaults.removeObject(forKey: key)
return Just(()).setFailureType(to: Error.self).eraseToAnyPublisher()
}
}
Step4,我们构建一个标准用户行为管理类,在这个类中,我们将对本工具类进行最后的封装。
首先,我需要定义一个用户行为管理协议UserActionsManager
public protocol UserActionsManager {
func writeToDefaults<T: Encodable>(_ userAction: UserAction, value: T) -> AnyPublisher<Void, Error>
func readFromDefaults<T: Decodable>(_ userAction: UserAction) -> AnyPublisher<T?, Error>
func removeFromDefaults(_ userAction: UserAction) -> AnyPublisher<Void, Error>
}
其次,我们来对UserDefaultStore进行扩展,我们用两个convenience init来分别实例化这个类,其中userId可以传进来用户的id,或者我们不区分用户的,用系统提供的.standard参数.
public extension UserDefaultsStore {
convenience init?(
userId: String,
decoder: JSONDecoder = JSONDecoder(),
encoder: JSONEncoder = JSONEncoder()
) {
guard let customUserDefaults = UserDefaults(suiteName: userId) else { return nil }
self.init(
userDefaults: customUserDefaults,
decoder: decoder,
encoder: encoder
)
}
convenience init(
decoder: JSONDecoder = JSONDecoder(),
encoder: JSONEncoder = JSONEncoder()
) {
self.init(
userDefaults: .standard,
decoder: decoder,
encoder: encoder
)
}
}
然后,我们的标准用户行为管理类StandardUserActionsManager遵循该协议.
import Foundation
import Combine
public protocol UserActionsManager {
func writeToDefaults<T: Encodable>(_ userAction: UserAction, value: T) -> AnyPublisher<Void, Error>
func readFromDefaults<T: Decodable>(_ userAction: UserAction) -> AnyPublisher<T?, Error>
func removeFromDefaults(_ userAction: UserAction) -> AnyPublisher<Void, Error>
}
public class StandardUserActionsManager: UserActionsManager {
private let userDefaultsStore: UserDefaultsStore
private var cancellabes = Set<AnyCancellable>()
init(
userDefaultsStore: UserDefaultsStore
) {
self.userDefaultsStore = userDefaultsStore
}
public func writeToDefaults<T>(_ userAction: UserAction, value: T) -> AnyPublisher<Void, Error> where T : Encodable {
userDefaultsStore
.write(userAction.name, value: value)
.eraseToAnyPublisher()
}
public func readFromDefaults<T>(_ userAction: UserAction) -> AnyPublisher<T?, Error> where T : Decodable {
userDefaultsStore
.read(userAction.name)
.eraseToAnyPublisher()
}
public func removeFromDefaults(_ userAction: UserAction) -> AnyPublisher<Void, Error> {
userDefaultsStore
.delete(userAction.name)
.eraseToAnyPublisher()
}
}
public extension UserDefaultsStore {
convenience init?(
userId: String,
decoder: JSONDecoder = JSONDecoder(),
encoder: JSONEncoder = JSONEncoder()
) {
guard let customUserDefaults = UserDefaults(suiteName: userId) else { return nil }
self.init(
userDefaults: customUserDefaults,
decoder: decoder,
encoder: encoder
)
}
convenience init(
decoder: JSONDecoder = JSONDecoder(),
encoder: JSONEncoder = JSONEncoder()
) {
self.init(
userDefaults: .standard,
decoder: decoder,
encoder: encoder
)
}
}
这样,我们的标准用户行为管理工具类就完成了,当我们在业务中引入存储读取的功能时,直接实例化该类去实现需求。
最后,让我们写个小demo测试一下:
这里点击=号后,会显示一个计算结果,当我们不存储读取defaults时,当我们退出app,结果显然会清空,当我们完成存储读取后,打开app后仍然显示计算结果。