Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New transformer parameter #192

Closed
vburojevic opened this issue Jun 18, 2018 · 12 comments
Closed

New transformer parameter #192

vburojevic opened this issue Jun 18, 2018 · 12 comments

Comments

@vburojevic
Copy link

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
Copy link
Contributor

@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
Copy link
Author

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
Copy link

evermeer commented Jun 19, 2018

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
Copy link
Contributor

@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
Copy link
Author

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

@onmyway133
Copy link
Contributor

onmyway133 commented Jun 19, 2018

@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
Copy link
Author

Seems cool!

@Erumaru
Copy link

Erumaru commented Jul 10, 2018

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

@ToroLiu
Copy link

ToroLiu commented Nov 1, 2018

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
Copy link

ysangmok commented Nov 1, 2018

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
Copy link

aaisataev commented Jan 23, 2019

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
Copy link

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.

@3lvis 3lvis closed this as completed Jun 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

9 participants