Skip to content

Commit

Permalink
Add Swift 6 support
Browse files Browse the repository at this point in the history
  • Loading branch information
gonzalezreal committed Oct 5, 2024
1 parent 7aff8d1 commit ae2dcbd
Show file tree
Hide file tree
Showing 6 changed files with 74 additions and 40 deletions.
1 change: 0 additions & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
// swift-tools-version:5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

Expand Down
24 changes: 24 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// swift-tools-version:6.0

import PackageDescription

let package = Package(
name: "NetworkImage",
platforms: [
.macOS(.v11),
.iOS(.v14),
.tvOS(.v14),
.watchOS(.v7),
],
products: [
.library(name: "NetworkImage", targets: ["NetworkImage"])
],
dependencies: [],
targets: [
.target(name: "NetworkImage"),
.testTarget(
name: "NetworkImageTests",
dependencies: ["NetworkImage"]
),
]
)
2 changes: 1 addition & 1 deletion Sources/NetworkImage/ImageSource.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

struct ImageSource: Hashable {
struct ImageSource: Hashable, Sendable {
let url: URL
let scale: CGFloat
}
20 changes: 10 additions & 10 deletions Sources/NetworkImage/NetworkImage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,6 @@ public struct NetworkImage<Content>: View where Content: View {
private let transaction: Transaction
private let content: (NetworkImageState) -> Content

private var environment: NetworkImageModel.Environment {
.init(transaction: self.transaction, imageLoader: self.imageLoader)
}

/// Loads and displays an image from the specified URL using
/// a default placeholder until the image loads.
///
Expand Down Expand Up @@ -168,15 +164,19 @@ public struct NetworkImage<Content>: View where Content: View {

public var body: some View {
if #available(macOS 12.0, iOS 15.0, tvOS 15.0, watchOS 8.0, *) {
self.content(self.model.state.image)
.task(id: self.source) {
await self.model.onAppear(source: self.source, environment: self.environment)
self.content(model.image)
.task(id: source) {
model.imageLoaderChanged(imageLoader)
model.transactionChanged(transaction)
await model.sourceChanged(source)
}
} else {
self.content(self.model.state.image)
self.content(model.image)
.modifier(
TaskModifier(id: self.source) {
await self.model.onAppear(source: self.source, environment: self.environment)
TaskModifier(id: source) { @MainActor in
model.imageLoaderChanged(imageLoader)
model.transactionChanged(transaction)
await model.sourceChanged(source)
}
)
}
Expand Down
2 changes: 1 addition & 1 deletion Sources/NetworkImage/NetworkImageCache.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import CoreGraphics
import Foundation

/// A type that temporarily stores images in memory, keyed by the URL from which they were loaded.
public protocol NetworkImageCache: AnyObject, Sendable {
public protocol NetworkImageCache: AnyObject {
/// Returns the image associated with a given URL.
func image(for url: URL) -> CGImage?

Expand Down
65 changes: 38 additions & 27 deletions Sources/NetworkImage/NetworkImageModel.swift
Original file line number Diff line number Diff line change
@@ -1,45 +1,56 @@
import SwiftUI

final class NetworkImageModel: ObservableObject {
struct Environment {
let transaction: Transaction
let imageLoader: NetworkImageLoader
}
@MainActor final class NetworkImageModel: ObservableObject {
@Published private(set) var source: ImageSource?
@Published private(set) var image: NetworkImageState = .empty

struct State: Equatable {
var source: ImageSource?
var image: NetworkImageState = .empty
}
private var transaction = Transaction()
private var imageLoader: NetworkImageLoader = DefaultNetworkImageLoader.shared

@Published private(set) var state: State = .init()
// MARK: Actions

@MainActor func onAppear(source: ImageSource?, environment: Environment) async {
guard source != self.state.source else { return }
func sourceChanged(_ source: ImageSource?) async {
guard source != self.source else { return }

guard let source else {
self.state = .init()
return
self.source = source

if let source {
await loadImage(source: source)
}
}

self.state.source = source
func transactionChanged(_ transaction: Transaction) {
self.transaction = transaction
}

let image: NetworkImageState
func imageLoaderChanged(_ imageLoader: NetworkImageLoader) {
self.imageLoader = imageLoader
}

do {
let cgImage = try await environment.imageLoader.image(from: source.url)
image = .success(
image: .init(decorative: cgImage, scale: source.scale),
private func loadImageFinished(_ cgImage: CGImage, scale: CGFloat) {
withTransaction(transaction) {
self.image = .success(
image: Image(decorative: cgImage, scale: scale),
idealSize: CGSize(
width: CGFloat(cgImage.width) / source.scale,
height: CGFloat(cgImage.height) / source.scale
width: CGFloat(cgImage.width) / scale,
height: CGFloat(cgImage.height) / scale
)
)
} catch {
image = .failure
}
}

private func loadImageFailed(_: Error) {
self.image = .failure
}

withTransaction(environment.transaction) {
self.state.image = image
// MARK: Effects

private func loadImage(source: ImageSource) async {
do {
let cgImage = try await imageLoader.image(from: source.url)
loadImageFinished(cgImage, scale: source.scale)
} catch {
loadImageFailed(error)
}
}
}

0 comments on commit ae2dcbd

Please sign in to comment.