基于Swift5.1对MMKV的封装

2021-02-24  本文已影响0人  十二生肖都背不全的家伙

基于Swift 5.1的 Keypath dynamicMemberLookup 新特性进行了MMKV的封装,使用起来会很方便。
使用方式:

extension KeyStore {
    var name: Key<String> { .init(key: "name", defaultValue: "Tom") }
}

KVM.name = "Tom"
print(KVM.name) // "Tom"

主要代码:

//
//  Default.swift
//  Utils
//
//  Created by Vincent on 2021/2/23.
//

import Foundation
import MMKV

public var KVM: KeyValueManager { KeyValueManager.default }
public typealias KeyStore = KeyValueManager.KeyStore

@dynamicMemberLookup
public class KeyValueManager: NSObject {
    public typealias Key = KeyStore.Key
    
    public struct KeyStore {
        public struct Key<T> {
            public let valueType: T.Type = T.self
            public let key: String
            public let defaultValue: T?
            public init(key: String, defaultValue: T?) {
                self.key = key
                self.defaultValue = defaultValue
            }
        }
    }
    
    let store: KeyStore = .init()
    
    /// mmapID 也是文件夹名称
    let mmapID: String = {
        let key = KeychainAccessUtil.MMKVPasswordKey
        if let randomPath = try? KeychainAccessUtil.default.appKeychain?.getString(key) {
            return randomPath
        }else {
            let randomPath = String.random(ofLength: 32)
            try? KeychainAccessUtil.default.appKeychain?.set(randomPath, key: key)
            return randomPath
        }
    }()
    
    /// cryptKey 加密用密钥
    let cryptKey: Data? = {
        let key = KeychainAccessUtil.DBPasswordKey
        if let password = try? KeychainAccessUtil.default.appKeychain?.getData(key) {
            return password
        }else {
            let password = String.random(ofLength: 32).data(using: .utf8) ?? Data()
            try? KeychainAccessUtil.default.appKeychain?.set(password, key: key)
            return password
        }
    }()
    
    public static let `default` = KeyValueManager()
    private override init(){
        super.init()
        if var url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first {
            url.appendPathComponent("." + mmapID)
            MMKV.initialize(rootDir: url.path)
        }else {
            assertionFailure("获取文件路径失败")
        }
        MMKV.register(self)
        MMKV.enableAutoCleanUp(maxIdleMinutes: 2)
    }
    
    var defaultKV: MMKV? { MMKV.defaultMMKV(withCryptKey: cryptKey) }
}

// MARK: - Subscripts
extension KeyValueManager {
//    String
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<String>>) -> String? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.string(forKey: key.key, defaultValue: key.defaultValue)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Int64
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Int64>>) -> Int64? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.int64(forKey: key.key, defaultValue: key.defaultValue ?? Int64.min)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    UInt64
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<UInt64>>) -> UInt64? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.uint64(forKey: key.key, defaultValue: key.defaultValue ?? UInt64.min)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Bool
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Bool>>) -> Bool? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.bool(forKey: key.key, defaultValue: key.defaultValue ?? false)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Float
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Float>>) -> Float? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.float(forKey: key.key, defaultValue: key.defaultValue ?? Float.nan)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Double
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Double>>) -> Double? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.double(forKey: key.key, defaultValue: key.defaultValue ?? Double.nan)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Date
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Date>>) -> Date? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.date(forKey: key.key, defaultValue: key.defaultValue)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Data
    public subscript(dynamicMember keyPath: KeyPath<KeyStore, Key<Data>>) -> Data? {
        get {
            let key = store[keyPath: keyPath]
            return self.defaultKV?.data(forKey: key.key, defaultValue: key.defaultValue)
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
    
//    Object
    public subscript<T: NSCoding & NSObjectProtocol>(dynamicMember keyPath: KeyPath<KeyStore, Key<T>>) -> T? {
        get {
            let key = store[keyPath: keyPath]
            self.defaultKV?.object(of: T.self, forKey: key.key)
            return self.defaultKV?.object(of: T.self, forKey: key.key) as? T
        }
        set {
            let key = store[keyPath: keyPath]
            if let value = newValue {
                self.defaultKV?.set(value, forKey: key.key)
            }else {
                self.defaultKV?.removeValue(forKey: key.key)
            }
        }
    }
}

// MARK: - MMKVHandler
extension KeyValueManager: MMKVHandler {
    public func onMMKVCRCCheckFail(_ mmapID: String!) -> MMKVRecoverStrategic {
        return .onErrorRecover
    }
    public func onMMKVFileLengthError(_ mmapID: String!) -> MMKVRecoverStrategic {
        return .onErrorRecover
    }
    public func mmkvLog(with level: MMKVLogLevel, file: UnsafePointer<Int8>!, line: Int32, func funcname: UnsafePointer<Int8>!, message: String!) {
        print(level.logType, tag: "MMKV", message, "", filePath: String.init(cString: file), funcName: String.init(cString: funcname), lineNum: Int(line))
    }
}

// MARK: MMKVLogLevel
extension MMKVLogLevel {
//    Cover to XloggerType
    var logType: XloggerType {
        switch self {
        case .debug:
            return .debug
        case .info:
            return .info
        case .warning:
            return .warning
        case .error:
            return .error
        case .none:
            return .none
        @unknown default:
            return .verbose
        }
    }
}

// MARK: - KeychainAccessUtil
extension KeychainAccessUtil {
    public static var MMKVKey: String { "com.mmkv.www" }
    public static var MMKVPasswordKey: String { "com.mmkv.password.www" }
}


代码中mmkvLog回调方法的内容可以自定义实现,我使用了封装后的xlog,需要自己替换
MMKV的加密密钥以及文件存储位置和mmapID都是存在Keychain中的,可以自己替换或者写死。

上一篇下一篇

猜你喜欢

热点阅读