Skip to content

Commit d45f014

Browse files
committed
Restore platform requirements and bump dependencies
1 parent d7b018e commit d45f014

13 files changed

+732
-757
lines changed

Package.resolved

Lines changed: 6 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Package.swift

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,21 @@ import PackageDescription
66
let package = Package(
77
name: "NetworkImage",
88
platforms: [
9-
.macOS(.v10_12),
10-
.iOS(.v11),
11-
.tvOS(.v11),
12-
.watchOS(.v3),
9+
.macOS(.v10_15),
10+
.iOS(.v13),
11+
.tvOS(.v13),
12+
.watchOS(.v6),
1313
],
1414
products: [
1515
.library(name: "NetworkImage", targets: ["NetworkImage"]),
1616
],
1717
dependencies: [
18-
.package(url: "https://github.com/pointfreeco/combine-schedulers", from: "0.5.0"),
19-
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "0.1.0"),
18+
.package(url: "https://github.com/pointfreeco/combine-schedulers", from: "0.5.2"),
19+
.package(url: "https://github.com/pointfreeco/xctest-dynamic-overlay", from: "0.2.1"),
2020
.package(
2121
name: "SnapshotTesting",
2222
url: "https://github.com/pointfreeco/swift-snapshot-testing",
23-
from: "1.8.2"
23+
from: "1.9.0"
2424
),
2525
],
2626
targets: [
Lines changed: 95 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,116 @@
1-
#if canImport(Combine)
2-
import Combine
3-
import Foundation
4-
import XCTestDynamicOverlay
1+
import Combine
2+
import Foundation
3+
import XCTestDynamicOverlay
54

6-
/// Loads and caches images.
7-
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
8-
public struct NetworkImageLoader {
9-
private let _image: (URL) -> AnyPublisher<OSImage, Error>
10-
private let _cachedImage: (URL) -> OSImage?
5+
/// Loads and caches images.
6+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
7+
public struct NetworkImageLoader {
8+
private let _image: (URL) -> AnyPublisher<OSImage, Error>
9+
private let _cachedImage: (URL) -> OSImage?
1110

12-
/// Creates an image loader.
13-
/// - Parameters:
14-
/// - urlSession: The `URLSession` that will load the images.
15-
/// - imageCache: An immediate cache to store the images in memory.
16-
public init(urlSession: URLSession, imageCache: NetworkImageCache) {
17-
self.init(urlLoader: URLLoader(urlSession: urlSession), imageCache: imageCache)
18-
}
11+
/// Creates an image loader.
12+
/// - Parameters:
13+
/// - urlSession: The `URLSession` that will load the images.
14+
/// - imageCache: An immediate cache to store the images in memory.
15+
public init(urlSession: URLSession, imageCache: NetworkImageCache) {
16+
self.init(urlLoader: URLLoader(urlSession: urlSession), imageCache: imageCache)
17+
}
1918

20-
init(urlLoader: URLLoader, imageCache: NetworkImageCache) {
21-
self.init(
22-
image: { url in
23-
if let image = imageCache.image(for: url) {
24-
return Just(image)
25-
.setFailureType(to: Error.self)
26-
.eraseToAnyPublisher()
27-
} else {
28-
return urlLoader.dataTaskPublisher(for: url)
29-
.tryMap { data, response in
30-
if let httpResponse = response as? HTTPURLResponse {
31-
guard 200 ..< 300 ~= httpResponse.statusCode else {
32-
throw NetworkImageError.badStatus(httpResponse.statusCode)
33-
}
19+
init(urlLoader: URLLoader, imageCache: NetworkImageCache) {
20+
self.init(
21+
image: { url in
22+
if let image = imageCache.image(for: url) {
23+
return Just(image)
24+
.setFailureType(to: Error.self)
25+
.eraseToAnyPublisher()
26+
} else {
27+
return urlLoader.dataTaskPublisher(for: url)
28+
.tryMap { data, response in
29+
if let httpResponse = response as? HTTPURLResponse {
30+
guard 200 ..< 300 ~= httpResponse.statusCode else {
31+
throw NetworkImageError.badStatus(httpResponse.statusCode)
3432
}
35-
36-
return try decodeImage(from: data)
3733
}
38-
.handleEvents(receiveOutput: { image in
39-
imageCache.setImage(image, for: url)
40-
})
41-
.eraseToAnyPublisher()
42-
}
43-
},
44-
cachedImage: { url in
45-
imageCache.image(for: url)
46-
}
47-
)
48-
}
4934

50-
init(
51-
image: @escaping (URL) -> AnyPublisher<OSImage, Error>,
52-
cachedImage: @escaping (URL) -> OSImage?
53-
) {
54-
_image = image
55-
_cachedImage = cachedImage
56-
}
35+
return try decodeImage(from: data)
36+
}
37+
.handleEvents(receiveOutput: { image in
38+
imageCache.setImage(image, for: url)
39+
})
40+
.eraseToAnyPublisher()
41+
}
42+
},
43+
cachedImage: { url in
44+
imageCache.image(for: url)
45+
}
46+
)
47+
}
5748

58-
/// Returns a publisher that loads an image for a given URL.
59-
public func image(for url: URL) -> AnyPublisher<OSImage, Error> {
60-
_image(url)
61-
}
49+
init(
50+
image: @escaping (URL) -> AnyPublisher<OSImage, Error>,
51+
cachedImage: @escaping (URL) -> OSImage?
52+
) {
53+
_image = image
54+
_cachedImage = cachedImage
55+
}
6256

63-
/// Returns the cached image for a given URL if there is any.
64-
public func cachedImage(for url: URL) -> OSImage? {
65-
_cachedImage(url)
66-
}
57+
/// Returns a publisher that loads an image for a given URL.
58+
public func image(for url: URL) -> AnyPublisher<OSImage, Error> {
59+
_image(url)
6760
}
6861

69-
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
70-
public extension NetworkImageLoader {
71-
/// The shared singleton image loader.
72-
///
73-
/// The shared image loader uses the shared `URLCache` and provides
74-
/// reasonable defaults for disk and memory caches.
75-
static let shared = Self(urlSession: .imageLoading, imageCache: NetworkImageCache())
62+
/// Returns the cached image for a given URL if there is any.
63+
public func cachedImage(for url: URL) -> OSImage? {
64+
_cachedImage(url)
7665
}
66+
}
7767

78-
#if DEBUG
79-
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
80-
public extension NetworkImageLoader {
81-
static func mock<P>(
82-
url matchingURL: URL,
83-
withResponse response: P
84-
) -> Self where P: Publisher, P.Output == OSImage, P.Failure == Error {
85-
Self { url in
86-
if url != matchingURL {
87-
XCTFail("\(Self.self).image recevied an unexpected URL: \(url)")
88-
}
68+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
69+
public extension NetworkImageLoader {
70+
/// The shared singleton image loader.
71+
///
72+
/// The shared image loader uses the shared `URLCache` and provides
73+
/// reasonable defaults for disk and memory caches.
74+
static let shared = Self(urlSession: .imageLoading, imageCache: NetworkImageCache())
75+
}
8976

90-
return response.eraseToAnyPublisher()
91-
} cachedImage: { _ in
92-
nil
77+
#if DEBUG
78+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
79+
public extension NetworkImageLoader {
80+
static func mock<P>(
81+
url matchingURL: URL,
82+
withResponse response: P
83+
) -> Self where P: Publisher, P.Output == OSImage, P.Failure == Error {
84+
Self { url in
85+
if url != matchingURL {
86+
XCTFail("\(Self.self).image recevied an unexpected URL: \(url)")
9387
}
88+
89+
return response.eraseToAnyPublisher()
90+
} cachedImage: { _ in
91+
nil
9492
}
93+
}
9594

96-
static func mock<P>(
97-
response: P
98-
) -> Self where P: Publisher, P.Output == OSImage, P.Failure == Error {
99-
Self { _ in
100-
response.eraseToAnyPublisher()
101-
} cachedImage: { _ in
102-
nil
103-
}
95+
static func mock<P>(
96+
response: P
97+
) -> Self where P: Publisher, P.Output == OSImage, P.Failure == Error {
98+
Self { _ in
99+
response.eraseToAnyPublisher()
100+
} cachedImage: { _ in
101+
nil
104102
}
103+
}
105104

106-
static var failing: Self {
107-
Self { _ in
108-
XCTFail("\(Self.self).image is unimplemented")
109-
return Just(OSImage())
110-
.setFailureType(to: Error.self)
111-
.eraseToAnyPublisher()
112-
} cachedImage: { _ in
113-
nil
114-
}
105+
static var failing: Self {
106+
Self { _ in
107+
XCTFail("\(Self.self).image is unimplemented")
108+
return Just(OSImage())
109+
.setFailureType(to: Error.self)
110+
.eraseToAnyPublisher()
111+
} cachedImage: { _ in
112+
nil
115113
}
116114
}
117-
#endif
115+
}
118116
#endif
Lines changed: 51 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,62 @@
1-
#if canImport(Combine)
2-
import Combine
3-
import CombineSchedulers
4-
import Foundation
1+
import Combine
2+
import CombineSchedulers
3+
import Foundation
54

6-
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
7-
internal struct NetworkImageEnvironment {
8-
var imageLoader: NetworkImageLoader
9-
var mainQueue: AnySchedulerOf<DispatchQueue>
10-
}
5+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
6+
internal struct NetworkImageEnvironment {
7+
var imageLoader: NetworkImageLoader
8+
var mainQueue: AnySchedulerOf<DispatchQueue>
9+
}
1110

12-
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
13-
internal final class NetworkImageStore: ObservableObject {
14-
enum Action {
15-
case onAppear(environment: NetworkImageEnvironment)
16-
case didLoadImage(OSImage)
17-
case didFail
18-
}
11+
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
12+
internal final class NetworkImageStore: ObservableObject {
13+
enum Action {
14+
case onAppear(environment: NetworkImageEnvironment)
15+
case didLoadImage(OSImage)
16+
case didFail
17+
}
1918

20-
enum State: Equatable {
21-
case notRequested(URL)
22-
case placeholder
23-
case image(OSImage)
24-
case fallback
25-
}
19+
enum State: Equatable {
20+
case notRequested(URL)
21+
case placeholder
22+
case image(OSImage)
23+
case fallback
24+
}
2625

27-
@Published private(set) var state: State
28-
private var cancellables: Set<AnyCancellable> = []
26+
@Published private(set) var state: State
27+
private var cancellables: Set<AnyCancellable> = []
2928

30-
init(url: URL?) {
31-
if let url = url {
32-
state = .notRequested(url)
33-
} else {
34-
state = .fallback
35-
}
29+
init(url: URL?) {
30+
if let url = url {
31+
state = .notRequested(url)
32+
} else {
33+
state = .fallback
3634
}
35+
}
3736

38-
func send(_ action: Action) {
39-
switch action {
40-
case let .onAppear(environment):
41-
guard case let .notRequested(url) = state else {
42-
return
43-
}
44-
if let image = environment.imageLoader.cachedImage(for: url) {
45-
state = .image(image)
46-
} else {
47-
state = .placeholder
48-
environment.imageLoader.image(for: url)
49-
.map { .didLoadImage($0) }
50-
.replaceError(with: .didFail)
51-
.receive(on: environment.mainQueue)
52-
.sink { [weak self] action in
53-
self?.send(action)
54-
}
55-
.store(in: &cancellables)
56-
}
57-
case let .didLoadImage(image):
37+
func send(_ action: Action) {
38+
switch action {
39+
case let .onAppear(environment):
40+
guard case let .notRequested(url) = state else {
41+
return
42+
}
43+
if let image = environment.imageLoader.cachedImage(for: url) {
5844
state = .image(image)
59-
case .didFail:
60-
state = .fallback
45+
} else {
46+
state = .placeholder
47+
environment.imageLoader.image(for: url)
48+
.map { .didLoadImage($0) }
49+
.replaceError(with: .didFail)
50+
.receive(on: environment.mainQueue)
51+
.sink { [weak self] action in
52+
self?.send(action)
53+
}
54+
.store(in: &cancellables)
6155
}
56+
case let .didLoadImage(image):
57+
state = .image(image)
58+
case .didFail:
59+
state = .fallback
6260
}
6361
}
64-
#endif
62+
}

0 commit comments

Comments
 (0)