Giter Site home page Giter Site logo

3-layer composition? about shallows HOT 14 CLOSED

dreymonde avatar dreymonde commented on July 22, 2024
3-layer composition?

from shallows.

Comments (14)

dreymonde avatar dreymonde commented on July 22, 2024

Hi! Will answer your question in about twenty minutes 🙂

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

So
I agree that this is not explained well enough, my bad
I guess that the issue here is Keys type on your RemoteStorage, I suppose it doesn’t use the same Filename type as DiskStorage and MemoryStorage<Filename, MyObject>. If that’s the case, the solution would be to ditch Filename and just add .usingStringKeys() to DiskStorage, which will convert its keys from Filename to String.

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Also keep in mind that this kind of 3-layer composition will have its issues, mostly about writes. So if say you’ll want you have a three-layer Storage<String, String>:

  1. Memory
  2. Disk
  3. Firebase

Let’s say that you want to:

composedStorage.set("new-value", forKey: "key-1")

Your memory and disk storages will almost always succeed, but network storage can fail. Now you have inconsistent storage: memory and disk have «new-value», while Firebase still has some let’s call it «old-value».

And when you’ll try to retrieve the data like this:

composedStorage.retrieve(forKey: "key-1") {  }

You will get a data from your memory storage directly (or disk storage if your app is restarted), so you’ll get a «new-value»… but Firebase, which should be the real source of truth, still has «old-value». That’s definitely a bug, and you will need to build a whole system around it.

That’s why I’m not generally recommending to use Shallows in such a manner. Network-based storages are fine as long as they’re read-only. If you want to support writes as well, you’ll be better with building some custom solution.

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

First, thanks for the incredibly speedy response!

I see what you mean about having a kind of hidden implementation of the remote storage, buried inside Shallows—could make things trickier than need be. So I suppose perhaps a better solution would be to have some class/service dedicated to keeping the DiskStorage and Firebase in sync? Just out of curiosity, is there any built-in notification for responding to changes in DiskStorage?

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Yes having this kind of service is a good idea. There is no such notifications built-in, but you can easily add such functionality in extensions on Storage.

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

Perfect, thanks!

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Here’s an example of what I mean by that. It uses Alba as a reactive solution but you can easily adapt it to use anything you want, including NSNotifications.

import Alba
import Shallows

public struct ReportingStorage<Key, Value> : StorageProtocol {
    
    private let underlying: Storage<Key, Value>
    
    public let didRetrieve = Publisher<Result<Value>>(label: "ReportingStorage.didRetrieve")
    public let didSet = Publisher<Result<Void>>(label: "ReportingStorage.didSetWithResult")
    
    init(_ storage: Storage<Key, Value>) {
        self.underlying = storage
    }
    
    public func retrieve(forKey key: Key, completion: @escaping (Result<Value>) -> ()) {
        underlying.retrieve(forKey: key, completion: { result in
            completion(result)
            self.didRetrieve.publish(result)
        })
    }
    
    public func set(_ value: Value, forKey key: Key, completion: @escaping (Result<Void>) -> ()) {
        underlying.set(value, forKey: key) { (result) in
            completion(result)
            self.didSet.publish(result)
        }
    }
    
}

extension StorageProtocol {
    
    public func reporting() -> ReportingStorage<Key, Value> {
        return ReportingStorage(asStorage())
    }
    
}

And then you can just:

let disk = DiskStorage.main.folder("cache", in: .cachesDirectory)
let diskReporting = disk.reporting()
diskReporting.didSet.proxy.listen { (result) in
    print(result)
}

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

This is great, thanks!

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

I'm having trouble getting the reactive solution above working. I think (maybe) it's because I'm using a combined memory+disk storage(??). I've put a break in ReportingStorage .set(), but it never gets hit (though a break in .set() in my memoryCache does get hit). My basic setup uses storageResponder = memoryCache.reporting() to set up the listener. Any thoughts as to what might be going on?

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Hi! I’ll be able to inspect your question more in-depth tomorrow. Meanwhile, could you please provide some more code? Ideally your composition setup code (creating memory+disk reporting storage) and usage code (the place where you use the mentioned storage).

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

Great, thanks.
My "ReactiveStorage" is basically identical to your ReportingStorage above.

public class LocalStorageManager {

    public let storageResponder: ReactiveStorage<Filename, MyObj>

    // the memoryCache is a property of the service class "LocalStorageManager"
    let memoryCache = MemoryStorage<Filename, MyObj>()
        .combined(with: DiskStorage.main.folder("myobjs", in: .documentDirectory).mapJSONObject(MyObj.self))

    // ...other stuff
    // ...

    init() {
        // creating LocalStorageManager sets up the storageResponder (reporting storage)
        storageResponder = memoryCache.reporting()
        storageResponder.didSet.proxy.listen { (result) in
            print("MyObj set with result: \(result)")
        }
    }

}

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Hm, I can’t see the reason .set() on the reactive storage is not being hit.
To be clear — when retrieving/setting data on a storage, you need to use your storageResponder object instead of memoryCache. I suppose this is the problem.

from shallows.

jbmaxwell avatar jbmaxwell commented on July 22, 2024

Ah, of course... yes, my previous code was calling set on memoryCache, not storageResponder! Will update that now. Thanks.

from shallows.

dreymonde avatar dreymonde commented on July 22, 2024

Great! Let me know if you have any problems.

from shallows.

Related Issues (15)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.