Skip to content

Commit

Permalink
1.0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcBiosca authored Nov 2, 2022
1 parent 14e065d commit 720348f
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 74 deletions.
59 changes: 14 additions & 45 deletions AsyncImageDemo/App/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,8 @@ import AsyncImage
struct ContentView: View {
@State private var showSlide = false

private let imageCache: ImageCache
private let publisherCache: PublisherCache
private let images: [ImageItem] = (1...100).map { _ in ImageItem() }

init(imageCache: ImageCache) {
self.imageCache = imageCache
self.publisherCache = PublisherCacheFactory.makeTemporaryCache()
}

var body: some View {
NavigationView {
ZStack {
Expand All @@ -28,21 +21,13 @@ struct ContentView: View {

ForEach(self.images) { item in
NavigationLink(
destination: DetailView(
imageCache: self.imageCache,
publisherCache: self.publisherCache,
item: item
)) {
destination: DetailView(item: item)) {
HStack(alignment: .center) {
Spacer(minLength: 0)

AsyncImage(
request: item.image.urlRequest,
imageCache: self.imageCache,
publisherCache: self.publisherCache
)
.frame(width: 200)
.cornerRadius(10)
AsyncImage(item.image.urlRequest)
.frame(width: 200)
.cornerRadius(10)

Spacer(minLength: 0)
}
Expand All @@ -57,21 +42,13 @@ struct ContentView: View {
Spacer(minLength: 90)

HStack {
AsyncImage(
request: self.images[self.images.count - 1].image.urlRequest,
imageCache: self.imageCache,
publisherCache: self.publisherCache
)
.frame(width: 200)
.clipShape(Circle())
AsyncImage(self.images[self.images.count - 1].image.urlRequest)
.frame(width: 200)
.clipShape(Circle())

AsyncImage(
request: self.images[self.images.count - 2].image.urlRequest,
imageCache: self.imageCache,
publisherCache: self.publisherCache
)
.frame(width: 200)
.clipShape(Circle())
AsyncImage(self.images[self.images.count - 2].image.urlRequest)
.frame(width: 200)
.clipShape(Circle())
}
}
}
Expand All @@ -86,23 +63,15 @@ struct ContentView: View {
}

struct DetailView: View {
private let imageCache: ImageCache
private let publisherCache: PublisherCache
private let item: ImageItem

init(imageCache: ImageCache, publisherCache: PublisherCache, item: ImageItem) {
self.imageCache = imageCache
self.publisherCache = publisherCache
init(item: ImageItem) {
self.item = item
}

var body: some View {
AsyncImage(
request: self.item.image.urlRequest,
imageCache: self.imageCache,
publisherCache: self.publisherCache
)
.aspectRatio(contentMode: .fit)
AsyncImage(self.item.image.urlRequest)
.aspectRatio(contentMode: .fit)
}
}

Expand All @@ -118,6 +87,6 @@ struct ImageItem: Identifiable {

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView(imageCache: ImageCacheFactory.makeTemporaryCache())
ContentView()
}
}
8 changes: 2 additions & 6 deletions AsyncImageDemo/SceneDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,9 @@ import AsyncImage
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
let globalImageCache = ImageCacheFactory.makeTemporaryCache()

func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Create the SwiftUI view that provides the window contents.
let contentView = ContentView(
imageCache: globalImageCache
)
let contentView = ContentView()

// Use a UIHostingController as window root view controller.
if let windowScene = scene as? UIWindowScene {
Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@ iOS >= 13 AsyncImage with the following characteristics:
## Getting started
You can clone this repository and run the `AsyncImageDemo` App.

```swift
AsyncImage("https://picsum.photos/200/300".urlRequest)
.frame(width: 200, height: 200)
```

You can optionally use the `configuration` to apply modifiers to the Image rather than directly to the View returned by AsyncImage.
If not specified, default ones applied are: `resizable().renderingMode(.original)`

### Controlling the cache
> Warning: Make sure to create an ImageCache and PublisherCache object somewhere in your app/module and injected to the AsyncImage object. Do not create a new one per each image, otherwise it will not work as intended.
```swift
Expand All @@ -25,9 +34,6 @@ AsyncImage(
.frame(width: 200, height: 200)
```

You can optionally use the `configuration` to apply modifiers to the Image rather than directly to the View returned by AsyncImage.
If not specified, default ones applied are: `resizable().renderingMode(.original)`

## Install
### Swift Package Manager
In Xcode, right click your project and `Add Packages`, then enter the repository URL.
Expand Down
8 changes: 4 additions & 4 deletions SimpleAsyncImage.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Pod::Spec.new do |s|
s.platform = :ios, "13.0"
s.swift_version = '5.0'
s.name = "SimpleAsyncImage"
s.version = "1.0.2"
s.version = "1.0.3"
s.summary = "Simple iOS 13 AsyncImage"
s.description = <<-DESC
iOS >= 13 AsyncImage with the following characteristics:
Expand All @@ -15,9 +15,9 @@ Pod::Spec.new do |s|
- Automatically reload when connectivity is regained
- Automatic retry when URLRequest fails
DESC
s.homepage = "https://github.com/MarcBiosca"
s.homepage = "https://github.com/MarcBiosca/AsyncImage.git"
s.license = { :type => "MIT", :file => "LICENSE" }
s.author = { "MarcBiosca" => "marc@dostresdos.com" }
s.source = { :git => "https://github.com/MarcBiosca/AsyncImage.git", :tag => "#{s.version}" }
s.source_files = 'Sources/AsyncImage/**/*'
end
s.source_files = 'Sources/AsyncImage/**/*'
end
19 changes: 18 additions & 1 deletion Sources/AsyncImage/Feature/AsyncImage/AsyncImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public struct AsyncImage: View {

public init(
request: URLRequest?,
imageCache: ImageCache?,
imageCache: ImageCache,
publisherCache: PublisherCache,
configuration: @escaping (Image) -> Image = {
$0.resizable().renderingMode(.original)
Expand All @@ -27,6 +27,23 @@ public struct AsyncImage: View {
publisherCache: publisherCache
)
)

self.configuration = configuration
}

public init(
_ request: URLRequest?,
configuration: @escaping (Image) -> Image = {
$0.resizable().renderingMode(.original)
}
) {
self._viewModel = ObservedObject(
wrappedValue: AsyncImageVM(
request: request,
imageCache: TemporaryImageCache.shared,
publisherCache: TemporaryPublisherCache.shared
)
)
self.configuration = configuration
}

Expand Down
19 changes: 5 additions & 14 deletions Sources/AsyncImage/Feature/AsyncImage/AsyncImageVM.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ final class AsyncImageVM: ObservableObject {
private let request: URLRequest?
private let networkAgent: NetworkAgentProtocol

private var images: ImageCache?
private var images: ImageCache
private var publishers: PublisherCache
private var imagePublisher: AnyCancellable?
private var networkPublisher: AnyCancellable?
Expand All @@ -26,7 +26,7 @@ final class AsyncImageVM: ObservableObject {
init(
request: URLRequest?,
networkAgent: NetworkAgentProtocol = NetworkAgent(),
imageCache: ImageCache?,
imageCache: ImageCache,
publisherCache: PublisherCache
) {
self.request = request
Expand All @@ -40,7 +40,7 @@ final class AsyncImageVM: ObservableObject {
func load() {
guard !self.isLoading, let url = self.request?.url else { return }

if let image = self.images?[url] {
if let image = self.images[url] {
self.imageState = .success(image)
return
}
Expand Down Expand Up @@ -81,11 +81,6 @@ private extension AsyncImageVM {

let publisher = self.networkAgent.download(from: self.request)
.subscribe(on: self.imageProcessingQueue)
.handleEvents(receiveCompletion: { [weak self] _ in
self?.onCancel(url: url)
}, receiveCancel: { [weak self] in
self?.onCancel(url: url)
})
.share()
.eraseToAnyPublisher()

Expand Down Expand Up @@ -116,15 +111,11 @@ private extension AsyncImageVM {
return Empty().eraseToAnyPublisher()
}

func onCancel(url: URL) {
self.publishers.set(nil, for: url.absoluteString)
self.isLoading = false
}

func onOutput(url: URL, image: UIImage) {
self.images?[url] = image
self.images[url] = image
self.set(.success(image))

self.publishers.set(nil, for: url.absoluteString)
self.networkPublisher = nil
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import Foundation
import UIKit

final class TemporaryImageCache: ImageCache {
// To facilitate integrations
static let shared = TemporaryImageCache()

private let cache = NSCache<NSURL, UIImage>()

init() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,5 @@

public protocol PublisherCache {
func set(_ value: UIImageErrorPublisher?, for key: String)

func get(_ key: String) -> UIImageErrorPublisher?
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import Combine
import UIKit

final class TemporaryPublisherCache: PublisherCache {
// To facilitate integrations
static let shared = TemporaryPublisherCache()

private var cache = [String: UIImageErrorPublisher]()
private let concurrentQueue = DispatchQueue(label: "temporary-publisher", attributes: .concurrent)

Expand Down

0 comments on commit 720348f

Please sign in to comment.