hyperoslo / Cache

:package: Nothing but Cache.
Other
2.97k stars 334 forks source link

New transformer parameter #192

Closed vburojevic closed 4 years ago

vburojevic commented 6 years ago
let diskConfig = DiskConfig(name: "Floppy")
let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)

let storage = try? Storage(
  diskConfig: diskConfig, 
  memoryConfig: memoryConfig, 
  transformer: TransformerFactory.forCodable(ofType: User.self) // Storage<User>
)

Docs indicate to have a separate instance of storage for every type in the new version, why? In the previous version, I used one storage to store all Codable entities, not sure how to migrate it now...

onmyway133 commented 6 years ago

@vburojevic Hi, as in https://github.com/hyperoslo/Cache#generic-type-safety-and-transformer, although Storage now have different type constraints, they all share the same thing under the hood, the type is to just for safety and extension. You can use method transformCodable to transform to a new type

vburojevic commented 6 years ago

Hey! Thanks for a quick answer!

Hmm, not sure I get it. transformCodable still requires a type to be defined.

Now I have something like this, and just store everything that's Codable into it:

private let _storage: Storage = {
        let diskConfig = DiskConfig(name: "NetworkCache")
        let memoryConfig = MemoryConfig(expiry: .never)

        let storage = try! Storage(diskConfig: diskConfig, memoryConfig: memoryConfig)

        return storage
    }()

I would preferably have something like this now (but can't pass Codable as type now of course, this is just to illustrate):

let diskConfig = DiskConfig(name: "Floppy")
let memoryConfig = MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)

let storage = try? Storage(
  diskConfig: diskConfig, 
  memoryConfig: memoryConfig, 
  transformer: TransformerFactory.forCodable(ofType: Codable.self) // Storage<Codable>
) 

I guess one way would be to have TransformerFactory return instance of TransformerFactory.data, but I'll have to deserialize every Codable struct myself to data before saving it to storage?

Because I currently have something like this:

func store<T: Codable>(_ data: T, forURL URL: URL) throws {
        try _storage.setObject(data, forKey: URL.absoluteString)
    }

Maybe I am missing something though.

evermeer commented 6 years ago

Initially I also was confused. I was using Cache as a generic mechanism in my network library. So the initialization cannot be done with a specific type. I now initialize the Storage with a dummy transformer and in the code that gets and sets values I initiate the transformer in the correct type.

So my cashing code now looks like this:

The dummy type:

public struct Cashable: Codable {    }

initialization of the cache:

        let transformer = TransformerFactory.forCodable(ofType: Cashable.self)
        BeterDichtbijApi.cache = try? Storage(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: transformer)

Using the cache in my network layer:

    public static func doCashableApiRequest<T: Codable>(_ method: String, cacheSeconds: TimeInterval = 7200, onFailure: @escaping () -> Void, _ onSuccess: @escaping (T?) -> Void) {
        if let object = try? MyApi.cache?.transformCodable(ofType: T.self).object(forKey: method) {
            if let object: T = object {
                onSuccess(object)
                return
            }
        }

        doApiRequest(method, onFailure: onFailure) { (object: T?) in
            if object != nil {
                try? MyApi.cache?.transformCodable(ofType: T.self).setObject(object!, forKey: method, expiry: .date(Date().addingTimeInterval(cacheSeconds)))
            }
            onSuccess(object)
        }
    }
onmyway133 commented 6 years ago

@vburojevic Hi, you can't do this TransformerFactory.forCodable(ofType: Codable.self) as protocol can't conform to itself, a concrete type needs to be specified. @evermeer Thanks for the answer, that's also something we would like to add, to build on top the newly introduced generic Storage

vburojevic commented 6 years ago

@onmyway133 yes I know, I mentioned that this was just an illustration. So Dummy transformer is the only way for now?

onmyway133 commented 6 years ago

@vburojevic yeah, to support multiple types, UIImage, Data, custom types, not just Codable. I could make something called CodableStorage to transform Storage under the hood, now sure if you like that idea ?

vburojevic commented 6 years ago

Seems cool!

Erumaru commented 6 years ago

@onmyway133 Hello, when will u release this feature? :)

ToroLiu commented 5 years ago

I don't like 3rd transformer parameter. Generally, I use cache for a distinct goal, not a distinct codable class. It makes codes worse now. I prefer version 4.2.0.

ysangmok commented 5 years ago

Was there a change to this feature? I'm having the same issue as everyone, having initialization done with specific type makes it less generic for me.

aaisataev commented 5 years ago

I created CacheManager that returns default storage by passing any codable type.

class CacheManager {

    static let dashboardKey = "dashboard"

    private static let diskConfig = DiskConfig(
        name: "Storage",
        expiry: .never,
        maxSize: 10000000,
        directory: directory,
        protectionType: .complete
    )

    private static let memoryConfig = MemoryConfig(
        expiry: .never,
        countLimit: 20,
        totalCostLimit: 10
    )

    private static let directory = FileManager.default.urls(for: FileManager.SearchPathDirectory.applicationSupportDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).first

    public static func defaultStorage<T: Codable>(model: T.Type) -> Storage<T>? {
        do {
            let storage = try Storage(diskConfig: diskConfig, memoryConfig: memoryConfig, transformer: TransformerFactory.forCodable(ofType: model))
            return storage
        } catch {
            return nil
        }
    }
}

So, you can use it this way:

class Dashboard: Codable {
    func cache() {
        guard let storage = CacheManager.defaultStorage(model: Dashboard.self) else { return }
        self.updated = Date()
        try? storage.setObject(self, forKey: CacheManager.dashboardKey)
    }

    static func cached() -> Dashboard? {
        guard let storage = CacheManager.defaultStorage(model: Dashboard.self),
            let dashboard = try? storage.object(forKey: CacheManager.dashboardKey) else { return nil}
        return dashboard
    }
}
trevinwisaksana commented 5 years ago

Removing this library from our app. Next time when you want to make changes this like this, you might as well create a different library for a different purpose.