3
\$\begingroup\$

I'm using Swift 4.0 and I'm using UserDefaults in my project for storing Strings like login or something. I have a class:

class Cache: UserDefaultsManager {

    static var currentProfileID: Int? {
        get {
            return value(forKey: Constants.Cache.CurrentProfileID) as? Int
        }
        set {
            set(newValue, forKey: Constants.Cache.CurrentProfileID)
        }
    }

    static var login: String? {
        get {
            return value(forKey: Constants.Cache.Login) as? String
        }
        set {
            set(newValue, forKey: Constants.Cache.Login)
        }
    }

    // ... and there are more of this kind

, where UserDefaultsManager looks like this:

class UserDefaultsManager {
    private static var uds: UserDefaults {
        return UserDefaults.standard
    }

    static func value(forKey: String) -> Any? {
        if let encoded = uds.object(forKey: forKey) as? Data {
            return NSKeyedUnarchiver.unarchiveObject(with: encoded)
        }    
        return nil   
    }

    static func set(_ data: Any?, forKey: String) {
        let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: data as AnyObject)
        uds.set(encodedData, forKey: forKey)

        if (!uds.synchronize()) {
            NSLog("Failed sync UserDefaults?")
        }
    }
}

Constants.Cache is a structure that holds my keys. My question is:

What would be a good approach to make my Cache class lighter with less code?

I imagine the final product should look like:

class Cache {
    static var hash: UDSObject<String>?
    static var currentProfileID: UDSObject<Int>?
    static var login: UDSObject<String>?
    static var selectedWindowID: UDSObject<Int>?
}
\$\endgroup\$
2
\$\begingroup\$

Your Cache class cannot be lighter than it already is. The most you can do is, introduce generics to remove the redundant casting every time you are trying to fetch from user defaults.

However, I see a major problem with the implementation here:

Is Cache also a UserDefaultsManager? Do you see Cache as something that extends the functionalities of the UserDefaultsManager class, or does your Cache use the functionalities of the UserDefaultsManager? I feel that composition is the way to go here than inheritance. I'd refactor this code to something like this:

protocol CacheHandling {
    func value<T>(forKey: String) -> T?
    func set<T>(_ data: T?, forKey: String)
}

extension CacheHandling {
    func value<T>(forKey: String) -> T? {
        let userDefaults = UserDefaults.standard
        if let encoded = userDefaults.object(forKey: forKey) as? Data {
            return NSKeyedUnarchiver.unarchiveObject(with: encoded) as? T
        }
        return nil
    }

    func set<T>(_ data: T?, forKey: String) {
        let userDefaults = UserDefaults.standard
        let encodedData: Data = NSKeyedArchiver.archivedData(withRootObject: data as AnyObject)
        userDefaults.set(encodedData, forKey: forKey)

        if (!userDefaults.synchronize()) {
            NSLog("Failed sync UserDefaults?")
        }
    }
}


struct Cache : CacheHandling {
    var currentProfileID: Int? {
        get {
            return value(forKey: Constants.Cache.CurrentProfileID)
        }
        set {
            set(newValue, forKey: Constants.Cache.CurrentProfileID)
        }
    }

    var login: String? {
        get {
            return value(forKey: Constants.Cache.Login)
        }
        set {
            set(newValue, forKey: Constants.Cache.Login)
        }
    }
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.