You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -36,33 +36,65 @@ with out-of-box implementations and great customization possibilities. `Cache` u
36
36
## Key features
37
37
38
38
-[x] Work with Swift 4 `Codable`. Anything conforming to `Codable` will be saved and loaded easily by `Storage`.
39
-
-[X] Disk storage by default. Optionally using `memory storage` to enable hybrid.
39
+
-[x] Hybrid with memory and disk storage.
40
40
-[X] Many options via `DiskConfig` and `MemoryConfig`.
41
41
-[x] Support `expiry` and clean up of expired objects.
42
42
-[x] Thread safe. Operations can be accessed from any queue.
43
43
-[x] Sync by default. Also support Async APIs.
44
-
-[X] Store images via `ImageWrapper`.
45
44
-[x] Extensive unit test coverage and great documentation.
46
45
-[x] iOS, tvOS and macOS support.
47
46
48
47
## Usage
49
48
50
49
### Storage
51
50
52
-
`Cache` is built based on [Chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern), in which there are many processing objects, each knows how to do 1 task and delegates to the next one. But that's just implementation detail. All you need to know is `Storage`, it saves and loads `Codable` objects.
51
+
`Cache` is built based on [Chain-of-responsibility pattern](https://en.wikipedia.org/wiki/Chain-of-responsibility_pattern), in which there are many processing objects, each knows how to do 1 task and delegates to the next one, so can you compose Storages the way you like.
52
+
53
+
For now the following Storage are supported
54
+
55
+
-`MemoryStorage`: save object to memory.
56
+
-`DiskStorage`: save object to disk.
57
+
-`HybridStorage`: save object to memory and disk, so you get persistented object on disk, while fast access with in memory objects.
58
+
-`SyncStorage`: blocking APIs, all read and write operations are scheduled in a serial queue, all sync manner.
59
+
-`AsyncStorage`: non-blocking APIs, operations are scheduled in an internal queue for serial processing. No read and write should happen at the same time.
53
60
54
-
`Storage` has disk storage and an optional memory storage. Memory storage should be less time and memory consuming, while disk storage is used for content that outlives the application life-cycle, see it more like a convenient way to store user information that should persist across application launches.
61
+
Although you can use those Storage at your discretion, you don't have to. Because we also provide a convenient `Storage` which uses `HybridStorage` under the hood, while exposes sync and async APIs through `SyncStorage` and `AsyncStorage`.
55
62
56
-
`DiskConfig` is required to set up disk storage. You can optionally pass `MemoryConfig`to use memory as front storage.
63
+
All you need to do is to specify the configuration you want with `DiskConfig` and `MemoryConfig`. The default configurations are good to go, but you can customise a lot.
57
64
58
65
59
66
```swift
60
67
let diskConfig =DiskConfig(name: "Floppy")
61
68
let memoryConfig =MemoryConfig(expiry: .never, countLimit: 10, totalCostLimit: 10)
62
69
63
-
let storage =try?Storage(diskConfig: diskConfig, memoryConfig: memoryConfig)
All `Storage` now are generic by default, so you can get a type safety experience. Once you create a Storage, it has a type constraint that you don't need to specify type for each operation afterwards.
80
+
81
+
If you want to change the type, `Cache` offers `transform` functions, look for `Transformer` and `TransformerFactory` for built-in transformers.
82
+
83
+
```swift
84
+
let storage: Storage<User> =...
85
+
storage.setObject(superman, forKey: "user")
86
+
87
+
let imageStorage = storage.transformImage() // Storage<UIImage>
88
+
imageStorage.setObject(image, forKey: "image")
89
+
90
+
let stringStorage = storage.transformCodable(ofType: String.self) // Storage<String>
Each transformation allows you to work with a specific type, however the underlying caching mechanism remains the same, you're working with the same `Storage`, just with different type annotation. You can also create different `Storage` for each type if you want.
95
+
96
+
`Transformer` is necessary because the need of serialising and deserialising objects to and from `Data` for disk persistency. `Cache` provides default `Transformer ` for `Data`, `Codable` and `UIImage/NSImage`
97
+
66
98
#### Codable types
67
99
68
100
`Storage` supports any objects that conform to [Codable](https://developer.apple.com/documentation/swift/codable) protocol. You can [make your own things conform to Codable](https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types) so that can be saved and loaded from `Storage`.
@@ -95,6 +127,8 @@ public enum StorageError: Error {
95
127
caseencodingFailed
96
128
/// The storage has been deallocated
97
129
casedeallocated
130
+
/// Fail to perform transformation to or from Data
131
+
casetransformerFail
98
132
}
99
133
```
100
134
@@ -156,11 +190,11 @@ try? storage.setObject(data, forKey: "a bunch of bytes")
There is time you want to get object together with its expiry information and meta data. You can use `Entry`
178
212
179
213
```swift
180
-
let entry =try? storage.entry(ofType: String.self, forKey: "my favorite city")
214
+
let entry =try? storage.entry(forKey: "my favorite city")
181
215
print(entry?.object)
182
216
print(entry?.expiry)
183
217
print(entry?.meta)
@@ -215,7 +249,7 @@ storage.async.setObject("Oslo", forKey: "my favorite city") { result in
215
249
}
216
250
}
217
251
218
-
storage.async.object(ofType: String.self, forKey: "my favorite city") { result in
252
+
storage.async.object(forKey: "my favorite city") { result in
219
253
switch result {
220
254
case .value(let city):
221
255
print("my favorite city is \(city)")
@@ -224,7 +258,7 @@ storage.async.object(ofType: String.self, forKey: "my favorite city") { result i
224
258
}
225
259
}
226
260
227
-
storage.async.existsObject(ofType: String.self, forKey: "my favorite city") { result in
261
+
storage.async.existsObject(forKey: "my favorite city") { result in
228
262
ifcase .value(let exists) = result, exists {
229
263
print("I have a favorite city")
230
264
}
@@ -268,19 +302,6 @@ try? storage.setObject(
268
302
storage.removeExpiredObjects()
269
303
```
270
304
271
-
## What about images?
272
-
273
-
As you may know, `NSImage` and `UIImage` don't conform to `Codable` by default. To make it play well with `Codable`, we introduce `ImageWrapper`, so you can save and load images like
274
-
275
-
```swift
276
-
let wrapper =ImageWrapper(image: starIconImage)
277
-
try? storage.setObject(wrapper, forKey: "star")
278
-
279
-
let icon =try? storage.object(ofType: ImageWrapper.self, forKey: "star").image
280
-
```
281
-
282
-
If you want to load image into `UIImageView` or `NSImageView`, then we also have a nice gift for you. It's called [Imaginary](https://github.com/hyperoslo/Imaginary) and uses `Cache` under the hood to make you life easier when it comes to working with remote images.
283
-
284
305
## Handling JSON response
285
306
286
307
Most of the time, our use case is to fetch some json from backend, display it while saving the json to storage for future uses. If you're using libraries like [Alamofire](https://github.com/Alamofire/Alamofire) or [Malibu](https://github.com/hyperoslo/Malibu), you mostly get json in the form of dictionary, string, or data.
If you want to load image into `UIImageView` or `NSImageView`, then we also have a nice gift for you. It's called [Imaginary](https://github.com/hyperoslo/Imaginary) and uses `Cache` under the hood to make you life easier when it comes to working with remote images.
0 commit comments