Skip to content

Commit c4c899d

Browse files
authored
Performance improvement (#25)
1 parent e1f0797 commit c4c899d

File tree

10 files changed

+511
-364
lines changed

10 files changed

+511
-364
lines changed

Development/AsyncMultiplexImage-Demo.xcodeproj/xcshareddata/xcschemes/AsyncMultiplexImage-Demo.xcscheme

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
<EnvironmentVariable
5454
key = "ASYNC_MULTIPLEX_IMAGE_LOG_ENABLED"
5555
value = "1"
56-
isEnabled = "YES">
56+
isEnabled = "NO">
5757
</EnvironmentVariable>
5858
</EnvironmentVariables>
5959
</LaunchAction>

Development/AsyncMultiplexImage-Demo/ContentView.swift

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -45,67 +45,64 @@ actor _SlowDownloader: AsyncMultiplexImageDownloader {
4545

4646
struct ContentView: View {
4747

48-
@State private var basePhotoURLString: String =
49-
"https://images.unsplash.com/photo-1492446845049-9c50cc313f00"
50-
5148
var body: some View {
5249
NavigationView {
5350
Form {
5451
Section {
5552
NavigationLink("SwiftUI") {
56-
VStack {
57-
AsyncMultiplexImage(
58-
multiplexImage: .init(
59-
identifier: basePhotoURLString,
60-
urls: buildURLs(basePhotoURLString)
61-
),
62-
downloader: _SlowDownloader(pipeline: .shared)
63-
) { phase in
64-
switch phase {
65-
case .empty:
66-
Text("Loading")
67-
case .progress(let image):
68-
image
69-
.resizable()
70-
.scaledToFill()
71-
case .success(let image):
72-
image
73-
.resizable()
74-
.scaledToFill()
75-
case .failure(let error):
76-
Text("Error")
77-
}
78-
}
79-
80-
HStack {
81-
Button("1") {
82-
basePhotoURLString =
83-
"https://images.unsplash.com/photo-1660668377331-da480e5339a0"
84-
}
85-
Button("2") {
86-
basePhotoURLString =
87-
"https://images.unsplash.com/photo-1658214764191-b002b517e9e5"
88-
}
89-
Button("3") {
90-
basePhotoURLString =
91-
"https://images.unsplash.com/photo-1587126396803-be14d33e49cf"
92-
}
93-
}
94-
}
95-
.padding()
96-
.navigationTitle("SwiftUI")
53+
SwitchingDemo()
54+
.navigationTitle("SwiftUI")
9755
}
9856
NavigationLink("UIKit") {
9957
UIKitContentViewRepresentable()
10058
}
101-
NavigationLink("SwiftUI List", destination: { UsingList() })
59+
60+
NavigationLink("Stress 1", destination: { StressGrid<Cell_1>() })
61+
62+
NavigationLink("Stress 2", destination: { StressGrid<Cell_2>() })
10263
}
10364
.navigationTitle("Multiplex Image")
10465
}
10566
}
10667
}
10768
}
10869

70+
private struct SwitchingDemo: View {
71+
72+
@State private var basePhotoURLString: String =
73+
"https://images.unsplash.com/photo-1492446845049-9c50cc313f00"
74+
75+
var body: some View {
76+
VStack {
77+
AsyncMultiplexImage(
78+
multiplexImage: .init(
79+
identifier: basePhotoURLString,
80+
urls: buildURLs(basePhotoURLString)
81+
),
82+
downloader: _SlowDownloader(pipeline: .shared),
83+
content: AsyncMultiplexImageBasicContent()
84+
)
85+
86+
HStack {
87+
Button("1") {
88+
basePhotoURLString =
89+
"https://images.unsplash.com/photo-1660668377331-da480e5339a0"
90+
}
91+
Button("2") {
92+
basePhotoURLString =
93+
"https://images.unsplash.com/photo-1658214764191-b002b517e9e5"
94+
}
95+
Button("3") {
96+
basePhotoURLString =
97+
"https://images.unsplash.com/photo-1587126396803-be14d33e49cf"
98+
}
99+
}
100+
}
101+
.padding()
102+
}
103+
104+
}
105+
109106
struct UIKitContentViewRepresentable: UIViewRepresentable {
110107

111108
func makeUIView(context: Context) -> UIKitContentView {

Development/AsyncMultiplexImage-Demo/List.swift

Lines changed: 85 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,116 @@
11
import AsyncMultiplexImage
22
import AsyncMultiplexImage_Nuke
3-
//
4-
// List.swift
5-
// AsyncMultiplexImage-Demo
6-
//
7-
// Created by Muukii on 2024/06/13.
8-
//
93
import SwiftUI
104

11-
struct UsingList: View {
5+
struct StressGrid<Cell: CellType>: View {
126

137
@State var items: [Entity] = Entity.batch()
148

159
var body: some View {
16-
ScrollView {
17-
LazyVGrid(
18-
columns: [
19-
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 16),
20-
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 16),
21-
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 16),
22-
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 16),
23-
], spacing: 16
24-
) {
25-
ForEach(items) { entity in
26-
if entity.id == items.last?.id {
27-
Cell(entity: entity)
28-
.onAppear {
29-
Task {
30-
let newItems = await Entity.delayBatch()
31-
items.append(contentsOf: newItems)
32-
}
33-
}
34-
} else {
35-
Cell(entity: entity)
10+
GeometryReader { proxy in
11+
ScrollView {
12+
LazyVGrid(
13+
columns: [
14+
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 2),
15+
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 2),
16+
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 2),
17+
.init(.flexible(minimum: 0, maximum: .infinity), spacing: 2),
18+
], spacing: 2
19+
) {
20+
ForEach(items) { entity in
21+
Cell(entity: entity)
3622
}
3723
}
3824
}
25+
.onPreferenceChange(AnchorPreferenceKey.self, perform: { v in
26+
guard let v = v else {
27+
return
28+
}
29+
let bounds = proxy[v]
30+
print(bounds)
31+
})
3932
}
4033
}
4134

4235
}
4336

4437
#Preview {
45-
UsingList()
38+
StressGrid<Cell_1>()
39+
}
40+
41+
#Preview {
42+
StressGrid<Cell_3>()
4643
}
4744

4845
let imageURLString =
4946
"https://images.unsplash.com/photo-1567095761054-7a02e69e5c43?q=80&w=800&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
5047

51-
struct Cell: View {
48+
protocol CellType: View {
49+
init(entity: Entity)
50+
}
51+
52+
struct Cell_1: View, CellType {
53+
54+
let entity: Entity
55+
56+
var body: some View {
57+
AsyncMultiplexImageNuke(imageRepresentation: .remote(entity.image))
58+
.frame(height: 100)
59+
}
60+
}
61+
62+
63+
struct Cell_2: View, CellType {
5264

5365
let entity: Entity
5466

5567
var body: some View {
5668
VStack {
5769
AsyncMultiplexImageNuke(imageRepresentation: .remote(entity.image))
5870
.frame(height: 100)
59-
HStack {
60-
Image(systemName: "globe")
61-
.imageScale(.large)
62-
.foregroundStyle(.tint)
63-
Text(entity.name)
64-
}
71+
.clipShape(
72+
RoundedRectangle(
73+
cornerRadius: 20,
74+
style: .continuous
75+
)
76+
)
77+
.frame(maxWidth: .infinity)
78+
.aspectRatio(1, contentMode: .fit)
79+
}
80+
.padding()
81+
}
82+
}
83+
84+
struct Cell_3: View, CellType {
85+
86+
final class Object: ObservableObject {
87+
88+
@Published var value: Int = 0
89+
90+
init() {
91+
print("Object.init")
92+
}
93+
94+
deinit {
95+
// print("Object.deinit")
96+
97+
}
98+
}
99+
100+
let entity: Entity
101+
102+
@State private var value: Int = 0
103+
@StateObject private var object = Object()
104+
105+
var body: some View {
106+
let _ = Self._printChanges()
107+
VStack {
108+
Button("Up \(value)") {
109+
value += 1
110+
}
111+
Button("Up \(object.value)") {
112+
object.value += 1
113+
}
65114
}
66115
.padding()
67116
}
@@ -84,7 +133,7 @@ struct Entity: Identifiable {
84133
}
85134

86135
static func batch() -> [Self] {
87-
(0..<100).map { _ in
136+
(0..<100000).map { _ in
88137
.make()
89138
}
90139
}

Sources/AsyncMultiplexImage-Nuke/AsyncMultiplexImageNuke.swift

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -12,27 +12,9 @@ public struct AsyncMultiplexImageNuke: View {
1212
public var body: some View {
1313
AsyncMultiplexImage(
1414
imageRepresentation: imageRepresentation,
15-
downloader: AsyncMultiplexImageNukeDownloader.shared
16-
) { phase in
17-
Group {
18-
switch phase {
19-
case .empty:
20-
EmptyView()
21-
case .progress(let image):
22-
image
23-
.resizable()
24-
.scaledToFill()
25-
.transition(.opacity.animation(.bouncy))
26-
case .success(let image):
27-
image
28-
.resizable()
29-
.scaledToFill()
30-
.transition(.opacity.animation(.bouncy))
31-
case .failure:
32-
EmptyView()
33-
}
34-
}
35-
}
15+
downloader: AsyncMultiplexImageNukeDownloader.shared,
16+
content: AsyncMultiplexImageBasicContent()
17+
)
3618
}
3719

3820
}
@@ -52,11 +34,11 @@ public struct AsyncMultiplexImageNuke: View {
5234

5335
#Preview("Rotating") {
5436
HStack {
55-
37+
5638
Rectangle()
5739
.frame(width: 100, height: 100)
5840
.rotationEffect(.degrees(10))
59-
41+
6042
AsyncMultiplexImageNuke(
6143
imageRepresentation: .remote(
6244
.init(
@@ -70,7 +52,7 @@ public struct AsyncMultiplexImageNuke: View {
7052
.frame(width: 100, height: 100)
7153
.rotationEffect(.degrees(10))
7254
.clipped(antialiased: true)
73-
55+
7456
AsyncMultiplexImageNuke(
7557
imageRepresentation: .remote(
7658
.init(
@@ -83,7 +65,7 @@ public struct AsyncMultiplexImageNuke: View {
8365
)
8466
.frame(width: 100, height: 100)
8567
.rotationEffect(.degrees(20))
86-
68+
8769
AsyncMultiplexImageNuke(
8870
imageRepresentation: .remote(
8971
.init(

Sources/AsyncMultiplexImage-Nuke/AsyncMultiplexImageNukeDownloader.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Nuke
44
import SwiftUI
55

66
public actor AsyncMultiplexImageNukeDownloader: AsyncMultiplexImageDownloader {
7-
7+
88
public static let `shared` = AsyncMultiplexImageNukeDownloader(pipeline: .shared, debugDelay: 0)
99

1010
public let pipeline: ImagePipeline

0 commit comments

Comments
 (0)