Skip to content

Commit c6991c2

Browse files
author
Chris
authored
Merge pull request #3 from crelies/dev
Custom cache + custom cache key, Unit Tests
2 parents 064488d + fb8e9c7 commit c6991c2

29 files changed

+826
-78
lines changed

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,24 @@ RemoteImage(type: .phAsset(localIdentifier: "541D4013-D51C-463C-AD85-0A1E4EA838F
5353
})
5454
```
5555

56+
## Custom cache
57+
58+
The `RemoteImageService` uses a default cache. To use a custom one just conform to the protocol `RemoteImageCache` and set it on the type `RemoteImageService`.
59+
60+
```swift
61+
RemoteImageService.cache = yourCache
62+
```
63+
64+
## Custom cache key
65+
66+
The default cache uses the associated value of the related `RemoteImageType` as the key. You can customize this by setting a cache key provider through
67+
68+
```swift
69+
RemoteImageService.cacheKeyProvider = { remoteImageType -> AnyObject in
70+
// return a key here
71+
}
72+
```
73+
5674
## Migration from 0.1.0 -> 1.0.0
5775

5876
The `url parameter` was refactored to a `type parameter` which makes it easy to fetch images at a URL or from the iCloud.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//
2+
// URLSession+RemoteImageURLDataPublisher.swift
3+
// RemoteImage
4+
//
5+
// Created by Christian Elies on 15.12.19.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
11+
extension URLSession: RemoteImageURLDataPublisher {
12+
func dataPublisher(for request: URLRequest) -> AnyPublisher<(data: Data, response: URLResponse), URLError> {
13+
dataTaskPublisher(for: request).eraseToAnyPublisher()
14+
}
15+
}

Sources/RemoteImage/private/Models/RemoteImageState.swift

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,23 +8,8 @@
88

99
import Foundation
1010

11-
enum RemoteImageState {
12-
case error(_ error: Error)
11+
enum RemoteImageState: Hashable {
12+
case error(_ error: NSError)
1313
case image(_ image: PlatformSpecificImageType)
1414
case loading
1515
}
16-
17-
extension RemoteImageState: Equatable {
18-
static func == (lhs: RemoteImageState, rhs: RemoteImageState) -> Bool {
19-
switch (lhs, rhs) {
20-
case (.error(let lhsError), .error(let rhsError)):
21-
return (lhsError as NSError) == (rhsError as NSError)
22-
case (.image(let lhsImage), .image(let rhsImage)):
23-
return lhsImage == rhsImage
24-
case (.loading, .loading):
25-
return true
26-
default:
27-
return false
28-
}
29-
}
30-
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
//
2+
// RemoteImageServiceProtocol.swift
3+
// RemoteImage
4+
//
5+
// Created by Christian Elies on 15.12.19.
6+
//
7+
8+
import Combine
9+
10+
protocol RemoteImageServiceProtocol where Self: ObservableObject {
11+
static var cache: RemoteImageCache { get set }
12+
static var cacheKeyProvider: RemoteImageCacheKeyProvider { get set }
13+
14+
var state: RemoteImageState { get set }
15+
func fetchImage(ofType type: RemoteImageType)
16+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
//
2+
// RemoteImageURLDataPublisher.swift
3+
// RemoteImage
4+
//
5+
// Created by Christian Elies on 15.12.19.
6+
//
7+
8+
import Combine
9+
import Foundation
10+
11+
protocol RemoteImageURLDataPublisher {
12+
func dataPublisher(for request: URLRequest) -> AnyPublisher<(data: Data, response: URLResponse), URLError>
13+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// RemoteImageURLDataPublisherProvider.swift
3+
// RemoteImage
4+
//
5+
// Created by Christian Elies on 15.12.19.
6+
//
7+
8+
protocol RemoteImageURLDataPublisherProvider {
9+
var remoteImageURLDataPublisher: RemoteImageURLDataPublisher { get }
10+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//
2+
// DefaultRemoteImageCache.swift
3+
//
4+
//
5+
// Created by Christian Elies on 14.12.19.
6+
//
7+
8+
import Foundation
9+
10+
struct DefaultRemoteImageCache {
11+
let cache = NSCache<AnyObject, PlatformSpecificImageType>()
12+
}
13+
14+
extension DefaultRemoteImageCache: RemoteImageCache {
15+
func object(forKey key: AnyObject) -> PlatformSpecificImageType? { cache.object(forKey: key) }
16+
17+
func setObject(_ object: PlatformSpecificImageType, forKey key: AnyObject) { cache.setObject(object, forKey: key) }
18+
19+
func removeObject(forKey key: AnyObject) { cache.removeObject(forKey: key) }
20+
21+
func removeAllObjects() { cache.removeAllObjects() }
22+
}

Sources/RemoteImage/private/Services/PhotoKitService.swift

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,35 +6,34 @@ protocol PhotoKitServiceProvider {
66

77
protocol PhotoKitServiceProtocol {
88
func getPhotoData(localIdentifier: String,
9-
success: @escaping (Data) -> Void,
10-
failure: @escaping (Error) -> Void)
9+
_ completion: @escaping (Result<Data, Error>) -> Void)
1110
}
1211

1312
final class PhotoKitService {
14-
13+
static var asset: PHAsset.Type = PHAsset.self
14+
static var imageManager: PHImageManager = PHImageManager.default()
1515
}
1616

1717
extension PhotoKitService: PhotoKitServiceProtocol {
1818
func getPhotoData(localIdentifier: String,
19-
success: @escaping (Data) -> Void,
20-
failure: @escaping (Error) -> Void) {
21-
let fetchAssetsResult = PHAsset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
19+
_ completion: @escaping (Result<Data, Error>) -> Void) {
20+
let fetchAssetsResult = Self.asset.fetchAssets(withLocalIdentifiers: [localIdentifier], options: nil)
2221
guard let phAsset = fetchAssetsResult.firstObject else {
23-
failure(PhotoKitServiceError.phAssetNotFound(localIdentifier: localIdentifier))
22+
completion(.failure(PhotoKitServiceError.phAssetNotFound(localIdentifier: localIdentifier)))
2423
return
2524
}
26-
25+
2726
let options = PHImageRequestOptions()
2827
options.isNetworkAccessAllowed = true
29-
PHImageManager.default().requestImageDataAndOrientation(for: phAsset,
30-
options: options,
31-
resultHandler: { data, _, _, info in
28+
Self.imageManager.requestImageDataAndOrientation(for: phAsset,
29+
options: options,
30+
resultHandler: { data, _, _, info in
3231
if let error = info?[PHImageErrorKey] as? Error {
33-
failure(error)
32+
completion(.failure(error))
3433
} else if let data = data {
35-
success(data)
34+
completion(.success(data))
3635
} else {
37-
failure(PhotoKitServiceError.missingData)
36+
completion(.failure(PhotoKitServiceError.missingData))
3837
}
3938
})
4039
}

Sources/RemoteImage/private/Services/RemoteImageServiceDependencies.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,16 @@
77

88
import Foundation
99

10-
protocol RemoteImageServiceDependenciesProtocol: PhotoKitServiceProvider {
10+
protocol RemoteImageServiceDependenciesProtocol: PhotoKitServiceProvider, RemoteImageURLDataPublisherProvider {
1111

1212
}
1313

1414
struct RemoteImageServiceDependencies: RemoteImageServiceDependenciesProtocol {
1515
let photoKitService: PhotoKitServiceProtocol
16+
let remoteImageURLDataPublisher: RemoteImageURLDataPublisher
1617

1718
init() {
1819
photoKitService = PhotoKitService()
20+
remoteImageURLDataPublisher = URLSession.shared
1921
}
2022
}

Sources/RemoteImage/public/Models/PhotoKitServiceError.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ public enum PhotoKitServiceError: Error {
55
case phAssetNotFound(localIdentifier: String)
66
}
77

8+
extension PhotoKitServiceError: Equatable {}
9+
810
extension PhotoKitServiceError: LocalizedError {
911
public var errorDescription: String? {
1012
switch self {

0 commit comments

Comments
 (0)