Droste 0.2.3

Droste 0.2.3

测试已测试
语言语言 SwiftSwift
许可证 MIT
发布最后发布2020年3月
SPM支持 SPM

George Tsifrikas 维护。



Droste 0.2.3

Droste

Build status Platform iOS Swift 4 compatible Carthage compatible CocoaPods compatible License: MIT

介绍

Droste 是一个轻量级的可组合缓存库,它利用 RxSwift 的 Observable 作为其 API。

用法示例

使用内置缓存

import Droste

let aDisposeBag = DisposeBag()
let url = URL(string: "https://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=pizza&format=json")!

Caches
    .sharedJSONCache
    .get(url)
    .subscribe(onNext: { jsonObject in
        //use jsonObject
    }).disposed(by: aDisposeBag)

或自定义缓存

import Droste

let aDisposeBag = DisposeBag()
let url = URL(string: "https://en.wikipedia.org/w/api.php?action=query&prop=revisions&rvprop=content&rvsection=0&titles=pizza&format=json")!

let networkFetcher = NetworkFetcher()
    .mapKeys { (url: URL) -> URLRequest in //map url to urlrequest
        return URLRequest(url: url)
    }

let diskCache = DiskCache<URL, NSData>()
let ramCache = RamCache<URL, NSData>()

let dataCache = ramCache + (diskCache + networkFetcher).reuseInFlight() 
let jsonCache = dataCache
            .mapValues(f: { (data) -> AnyObject in
                //convert NSData to json object
                return try JSONSerialization.jsonObject(with: data as Data, options: [.allowFragments]) as AnyObject
            }, fInv: { (object) -> NSData in
                //convert json object to NSData
                return try JSONSerialization.data(withJSONObject: object, options: []) as NSData
            })

jsonCache
    .get(url)
    .subscribe(onNext: { jsonObject in
        //use jsonObject
    }).disposed(by: aDisposeBag)

特性

开箱即用

我们提供了一些开箱即用的缓存单例,可供使用。

  • Caches.sharedJSONCache 接受 URL 并返回 AnyObject,根据端点响应可以是 Array 或 Dictionary
  • Caches.sharedDataCache 接受 URL 并返回 NSData
  • Caches.sharedImageCache 接受 URL 并返回 UIImage

组合缓存

您可以组合不同类型的缓存,例如将 ram 缓存与由网络获取器支持的磁盘缓存组合。要组合两个或多个缓存,可以使用 .compose+ 操作符。

示例

let ramCache = RamCache<URL, NSData>()
let diskCache = DiskCache<URL, NSData>()

//First hit ram cache and in case of failure, hit the disk cache
let ramDiskCache = ramCache.compose(other: diskCache)

//Same composition example using the the overloaded `+` operator
let ramDiskCache = ramCache + diskCache

可过期缓存

您可以为每个支持过期的缓存设置过期时间。DiskCache 和 RamCache 都默认支持!您可以按以下方式设置过期时间:

// seconds from now
let ramCache = RamCache<URL, NSData>().expires(at: .seconds(30))

// or by explicitly setting a date 
let diskCache = DiskCache<URL, NSData>().expires(at: .date(Date(timeIntervalSince1970: 1516045540)))

在 ram 和 disk 缓存中刷新资源 5 分钟后。

let ramCache = RamCache<URL, NSData>().expires(at: .seconds(600))
let diskCache = DiskCache<URL, NSData>().expires(at: .seconds(600))

let dataCache = ramCache + diskCache + networkFetcher

取消请求

Droste 利用 RxSwift 的功能,为我们提供了一些额外的免费功能,例如取消正在进行的请求。

带有 DisposeBag 的示例

var aDisposeBag = DisposeBag()

let diskCache = DiskCache<URL, NSData>()
let networkFetcher = NetworkFetcher<URL, NSData>()

let diskNetworkCache = diskCache + networkFetcher
diskNetworkCache
    .get("a url")
    .subscribe(onNext: { response in

    }).disposed(by: aDisposeBag)

//...later
aDisposeBag = DisposeBag() // this line will cancel the on-going request

带有 flatMapLatest 的示例

var aDisposeBag = DisposeBag()

let diskCache = DiskCache<URL, NSData>()
let networkFetcher = NetworkFetcher<URL, NSData>()

let diskNetworkCache = diskCache + networkFetcher

//Each time the user input emits a new value, it cancels the previous request in cache
someUserInput
    .flatMapLatest{ userInput in
        let key = exampleKey(from: userInput)
        return diskNetworkCache.get(key)
    }
    .subscribe(onNext: { response in

    }).disposed(by: aDisposeBag)

映射值和键

不同的缓存可能知道不同类型的值和键。例如,您可能有一个使用 String 类型键并返回 UIImage 类型的 RAM 缓存,以及一个使用 URL 作为键并返回 NSData 的网络获取器(它是一种缓存类型)。如果您想合并这两个缓存,您不能直接操作,因为它们的类型不匹配。您可以使用 mapValuesmapKeys 来映射缓存中的值和键。

示例

let networkFetcher = NetworkFetcher()      //Value = NSData, Key = URLRequest
let ramCache = RamCache<String, UIImage>() //Value = UIImage, Key = String

let imageNetworkFetcher = networkFetcher  //Value = UIImage, Key = String
                            .mapKeys { (urlString: String) -> URLRequest in
                                let url = URL(string: urlString)!
                                return URLRequest(url: url)
                            }
                            .mapValues(
                                f: { (data) -> UIImage in
                                    return UIImage(data: data as Data)!
                                }) { (image) -> NSData in
                                    return UIImagePNGRepresentation(image) as! NSData
                                }

let imageRamNetworkCache = ramCache + imageNetworkFetcher //Value = UIImage, Key = String

imageRamNetworkCache.get("http://an.image.url.png")
    .subscribe(onNext: { image in
        //in a view controller context
        self.imageView.image = image
    }).disposed(by: disposeBag)

更多示例 见此处

发送请求

在你将两个缓存组合在一起,并希望右边的缓存始终从网络获取数据,而不管第一个缓存是否成功的情况下,你可以使用发送请求操作符。通过这种方式,你可以从第一个缓存(如果存在)中获取值,并跟随第二个缓存的值(如果存在)。请注意,.forwardRequest 操作符不提供任何保证,以确保发射的顺序与组合的顺序一致。

示例

假设你有一个屏幕需要立即加载并显示缓存的结果,但会在网络请求成功时始终更新。

let screenCache = diskCache.forwardRequest() + networkFetcher

screenCache
    .get("withKey")
    .subscribe(onNext: {  screenViewModel
        self.viewModel = screenViewModel //if disk cache has a value this will be called twice
    })

切换缓存

根据使用的键在运行时切换缓存。

示例

let cacheA = ...
let cacheB = ...
let cache = switchCache(cacheA: cacheA, cacheB: cacheB) { (key) -> CacheSwitchResult in
    if key == "Hello" {
        return .cacheA
    }
    if key == "World" {
        return .cacheB
    }
}

cache.get("Hello") //this will use cacheA
cache.get("World") //this will use cacheB

重用执行中的资源

当你想从一个缓存/检索器(例如 NetworkFetcher)获取一个昂贵的资源时,你可以使用 .reuseInFlight 操作符,当有一个相同键在执行的请求时,合并所有具有相同键的请求。这种场景的一个例子是,一个聊天应用在屏幕上显示同一个头像多次,每次头像都会从缓存中发出自己的请求。使用这个操作符意味着只有一个请求将被发出,任何后续的请求都将共享第一个请求的响应。

示例

let cache = memoryCache + (diskCache + networkFetcher).reuseInFlight()

cache.get("profileImage")
    .subscribe(onNext: { image in
        avatar1.image = image
    })

//a short while after, before the first request finished
cache.get("profileImage")
    .subscribe(onNext: { image in
        avatar2.image = image //this will get the same response as the above without making a second request
    }

备注

  • 键必须是可哈希的。
  • 此操作符是线程安全的,这意味着您可以从多个队列中分派相同的请求,而它将正常工作且不会出现任何意外的行为。

跳过-while

使用 .skipWhile 操作符可以在运行时根据给定条件跳过缓存。

示例

let updateFromNetwork = true
let conditionedDiskCache = diskCache.skipWhile { key in
        let shouldSkip = (key == "a" && updateFromNetwork)
        return Observable(shouldSkip)
    }

let cache = conditionedDiskCache + networkFetcher

cache.get("a")
    .subscribe(onNext: { response in
        
    })

突出获取

我们有两种 .get 操作符,它们的行为略有不同。

  • func get(_ key: Key) -> Observable<Value? 可选返回值

使用此操作符意味着,如果缓存没有值且内部没有抛出错误,则它将返回一个值 nil。所有抛出的错误都将如预期传播。

  • func get(_ key: Key) -> Observable<Value> 非可选返回值

使用此操作符意味着,如果缓存没有值,则将抛出错误 CacheFetchError.valueNotFound。此操作符 不会改变 其他抛出错误的其他预期行为。

要求

  • iOS 9.0+
  • Xcode 8.0+

参与其中

  • 如果您希望贡献某种内容,请随时提交PRs
  • 如果您有功能请求,请开启一个issue
  • 如果您发现了一个bug或需要帮助,请在提交issue前,先检查旧issue,常见问题解答StackOverflow(标签'Droste')上的线程。

在贡献之前,请查阅CONTRIBUTING文件以获取更多信息。

如果您在应用中使用强 fuzzy,我们非常想了解您的使用情况!请在Twitter联系我们。

示例

按照以下3个步骤运行示例项目

  • 克隆Droste仓库
  • 打开Example/Example.xcworkspace工作空间
  • 运行Example项目

您也可以通过在Droste项目下的Example/Example.workspace子目录中的Droste Playground进行实验和学习。

安装

CocoaPods

CocoaPods

要安装Droste,只需要在Podfile中添加以下行:

pod 'Droste', '~> 0.2.0'

Carthage

Carthage

要安装Droste,只需要在Cartfile中添加以下行:

github "gtsifrikas/Droste" ~> 0.2.0

版本1.0的任务

  • 100%覆盖率
  • 支持macOS
  • 支持linux
  • 更新README以包含自定义Cache创建。
  • 提出一个API以支持TTL,并在DiskCache和RamCache中实现它。

作者

更改记录

查看更改记录