Skip to content

Commit

Permalink
Update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalezreal committed Dec 20, 2021
1 parent 8570ba0 commit f8b8ed0
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 134 deletions.
120 changes: 40 additions & 80 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@
[![](https://img.shields.io/endpoint?url=https%3A%2F%2Fswiftpackageindex.com%2Fapi%2Fpackages%2Fgonzalezreal%2FNetworkImage%2Fbadge%3Ftype%3Dplatforms)](https://swiftpackageindex.com/gonzalezreal/NetworkImage)
[![contact: @gonzalezreal](https://img.shields.io/badge/contact-@gonzalezreal-blue.svg?style=flat)](https://twitter.com/gonzalezreal)

NetworkImage is a Swift package that provides image downloading, caching, and displaying for your SwiftUI apps. It leverages the foundation URLCache, providing persistent and in-memory caches.
NetworkImage is a Swift package that provides image downloading, caching, and displaying for your
SwiftUI apps. It leverages the foundation URLCache, providing persistent and in-memory caches.

You can explore all the capabilities of this package in the [companion demo project](Examples/NetworkImageDemo).
You can explore all the capabilities of this package in the
[companion demo project](Examples/NetworkImageDemo).

## Supported Platforms

Expand All @@ -18,73 +20,59 @@ You can use `NetworkImage` in the following platforms:
* watchOS 6.0+

## Usage
A network image downloads and displays an image from a given URL; the download is asynchronous, and the result is cached both in disk and memory.
A network image downloads and displays an image from a given URL; the download is asynchronous,
and the result is cached both in disk and memory.

You create a network image, in its simplest form, by providing the image URL.

```swift
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
.scaledToFit()
.frame(width: 300, height: 200)
```

You can also provide the name of a placeholder image that the view will display while the image is loading or, as a fallback, if an error occurs or the URL is `nil`.
To manipulate the loaded image, use the `content` parameter.

```swift
NetworkImage(
url: URL(string: "https://picsum.photos/id/237/300/200"),
placeholderSystemImage: "photo.fill"
)
.scaledToFit()
```

If you want, you can only provide a fallback image. A network image view only displays this image if an error occurs or when the URL is `nil`.

```swift
NetworkImage(
url: URL(string: "https://picsum.photos/id/237/300/200"),
fallbackSystemImage: "photo.fill"
)
.scaledToFit()
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
image.resizable().scaledToFill()
}
.frame(width: 150, height: 150)
.clipped()
```

It is also possible to create network images using views to compose the network image's placeholders programmatically.
The view displays a standard placeholder that fills the available space until the image loads. You
can specify a custom placeholder by using the `placeholder` parameter.

```swift
NetworkImage(url: movie.posterURL) {
ProgressView()
} fallback: {
Text(movie.title)
.padding()
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
image.resizable().scaledToFill()
} placeholder: {
Color.yellow // Shown while the image is loaded or an error occurs
}
.scaledToFit()
.frame(width: 150, height: 150)
.clipped()
```

### Styling Network Images
You can customize the appearance of network images by creating styles that conform to the `NetworkImageStyle` protocol. To set a specific style for all network images within a view, use the `networkImageStyle(_:)` modifier. In the following example, a custom style adds a grayscale effect to all the network image views within the enclosing `VStack`:
It is also possible to specify a custom fallback placeholder that the view will display if there is
an error or the URL is `nil`.

```swift
struct ContentView: View {
var body: some View {
VStack {
NetworkImage(url: URL(string: "https://picsum.photos/id/1025/300/200"))
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
}
.networkImageStyle(GrayscaleNetworkImageStyle())
}
}

struct GrayscaleNetworkImageStyle: NetworkImageStyle {
func makeBody(configuration: Configuration) -> some View {
configuration.image
.resizable()
.scaledToFit()
.grayscale(0.99)
}
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
image.resizable().scaledToFill()
} placeholder: {
ProgressView() // Shown while the image is loaded
} fallback: {
Image(systemName: "photo") // Shown when an error occurs or the URL is nil
}
.frame(width: 150, height: 150)
.clipped()
.background(Color.yellow)
```

### Using NetworkImageLoader
For other use cases outside the scope of SwiftUI, you can download images directly using the shared `NetworkImageLoader`. In the following example, a view controller downloads an image and applies a transformation to it:
For other use cases outside the scope of SwiftUI, you can download images directly using the
shared `NetworkImageLoader`. In the following example, a view controller downloads an image
and applies a transformation to it.

```swift
class MyViewController: UIViewController {
Expand Down Expand Up @@ -132,7 +120,10 @@ For other use cases outside the scope of SwiftUI, you can download images direct
```

### NetworkImage and Testing
NetworkImage is implemented with testing in mind and provides view modifiers to stub image responses and replace the scheduler that drives the view state changes. This allows you to write synchronous tests, avoiding the use of expectations or waits. The following example shows how to use this feature with Point-Free's [SnapshotTesting](https://github.com/pointfreeco/swift-snapshot-testing) library.
NetworkImage is implemented with testing in mind and provides view modifiers to stub image
responses. This allows you to write synchronous tests, avoiding the use of expectations or waits.
The following example shows how to use this feature with Point-Free's
[SnapshotTesting](https://github.com/pointfreeco/swift-snapshot-testing) library.

```swift
final class MyTests: XCTestCase {
Expand All @@ -145,43 +136,12 @@ final class MyTests: XCTestCase {
.networkImageLoader(
.mock(response: Fail(error: URLError(.badServerResponse) as Error))
)
// Disable animations
.networkImageScheduler(UIScheduler.shared)

assertSnapshot(matching: view, as: .image(layout: .device(config: .iPhoneSe)))
}
}
```

### Animations
NetworkImage performs an explicit `.default` animation when its state changes, using a [Combine Scheduler](https://developer.apple.com/documentation/combine/scheduler). You can override this scheduler and adjust the animation with the `networkImageScheduler(_:)` view modifier.

```swift
struct ContentView: View {
var body: some View {
VStack {
NetworkImage(url: URL(string: "https://picsum.photos/id/1025/300/200"))
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
}
.networkImageScheduler(UIScheduler.shared.animation(.easeIn(duration: 0.25)))
assertSnapshot(matching: view, as: .image(layout: .device(config: .iPhoneSe)))
}
}
```

If you want to disable animations in NetworkImage, simply pass `UIScheduler.shared` to the `.networkImageScheduler` view modifier.

```swift
struct ContentView: View {
var body: some View {
VStack {
NetworkImage(url: URL(string: "https://picsum.photos/id/1025/300/200"))
NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
}
.networkImageScheduler(UIScheduler.shared)
}
}
```

## Installation
You can add NetworkImage to an Xcode project by adding it as a package dependency.
1. From the **File** menu, select **Swift Packages › Add Package Dependency…**
Expand Down
83 changes: 29 additions & 54 deletions Sources/NetworkImage/SwiftUI/NetworkImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,67 +9,42 @@ import SwiftUI
///
/// You create a network image, in its simplest form, by providing the image URL.
///
/// ```swift
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
/// ```
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
/// .frame(width: 300, height: 200)
///
/// You can also provide the name of a placeholder image that the view will display while the image is loading or, as
/// a fallback, if an error occurs or the URL is `nil`.
/// To manipulate the loaded image, use the `content` parameter.
///
/// ```swift
/// NetworkImage(
/// url: URL(string: "https://picsum.photos/id/237/300/200"),
/// placeholderSystemImage: "photo.fill"
/// )
/// ```
///
/// If you want, you can only provide a fallback image. A network image view only displays this image if an error occurs
/// or when the URL is `nil`.
///
/// ```swift
/// NetworkImage(
/// url: URL(string: "https://picsum.photos/id/237/300/200"),
/// fallbackSystemImage: "photo.fill"
/// )
/// ```
///
/// It is also possible to create network images using views to compose the network image's placeholders
/// programmatically.
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
/// image.resizable().scaledToFill()
/// }
/// .frame(width: 150, height: 150)
/// .clipped()
///
/// ```swift
/// NetworkImage(url: movie.posterURL) {
/// ProgressView()
/// } fallback: {
/// Text(movie.title)
/// .padding()
/// }
/// ```
/// The view displays a standard placeholder that fills the available space until the image loads. You can
/// specify a custom placeholder by using the `placeholder` parameter.
///
/// ### Styling Network Images
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
/// image.resizable().scaledToFill()
/// } placeholder: {
/// Color.yellow // Shown while the image is loaded or an error occurs
/// }
/// .frame(width: 150, height: 150)
/// .clipped()
///
/// You can customize the appearance of network images by creating styles that conform to the
/// `NetworkImageStyle` protocol. To set a specific style for all network images within a view, use
/// the `networkImageStyle(_:)` modifier. In the following example, a custom style adds a grayscale
/// effect to all the network image views within the enclosing `VStack`:
/// It is also possible to specify a custom fallback placeholder that the view will display if there is an
/// error or the URL is `nil`.
///
/// ```swift
/// struct ContentView: View {
/// var body: some View {
/// VStack {
/// NetworkImage(url: URL(string: "https://picsum.photos/id/1025/300/200"))
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200"))
/// NetworkImage(url: URL(string: "https://picsum.photos/id/237/300/200")) { image in
/// image.resizable().scaledToFill()
/// } placeholder: {
/// ProgressView() // Shown while the image is loaded
/// } fallback: {
/// Image(systemName: "photo") // Shown when an error occurs or the URL is nil
/// }
/// .networkImageStyle(GrayscaleNetworkImageStyle())
/// }
/// }
/// struct GrayscaleNetworkImageStyle: NetworkImageStyle {
/// func makeBody(configuration: Configuration) -> some View {
/// configuration.image
/// .resizable()
/// .grayscale(0.99)
/// }
/// }
/// ```
/// .frame(width: 150, height: 150)
/// .clipped()
/// .background(Color.yellow)
///
public struct NetworkImage<Content>: View where Content: View {
private enum ViewState: Equatable {
case empty
Expand Down

0 comments on commit f8b8ed0

Please sign in to comment.