美文网首页
基于Swift5.1对MMKV的封装

基于Swift5.1对MMKV的封装

作者: 十二生肖都背不全的家伙 | 来源:发表于2021-02-24 12:07 被阅读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中的,可以自己替换或者写死。

    相关文章

      网友评论

          本文标题:基于Swift5.1对MMKV的封装

          本文链接:https://www.haomeiwen.com/subject/qlfsfltx.html