Created
May 8, 2024 21:37
-
-
Save malhal/7a4e687962f89392c16c388635227aec to your computer and use it in GitHub Desktop.
ResourceCacheTest.swift
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// ResourceCacheTest.swift | |
// Test (iOS) | |
// | |
// Created by Malcolm Hall on 08/05/2024. | |
// | |
import SwiftUI | |
import AsyncAlgorithms | |
struct ResourceCacheTest { | |
struct RuntimeError: LocalizedError { | |
let description: String | |
init(_ description: String) { | |
self.description = description | |
} | |
var errorDescription: String? { | |
description | |
} | |
} | |
@MainActor | |
class ResourceCache { | |
enum Request { | |
case inprogress | |
case success(UIImage) | |
case failed(Error) | |
} | |
let urls: AsyncChannel<URL> | |
var requests: [AsyncChannel<(URL, Request)>] = [] | |
init() { | |
let urls = AsyncChannel<URL>() | |
self.urls = urls | |
Task { | |
var requestsByURL: [URL : Request] = [:] | |
for await url in urls { | |
print("loop") | |
try? await Task.sleep(for: .seconds(1)) // testing | |
print("url \(url)") | |
if let request = requestsByURL[url] { | |
switch request { | |
case .inprogress: | |
continue | |
default: | |
for channel in requests { | |
await channel.send((url, request)) | |
} | |
} | |
} | |
var request: Request = .inprogress | |
requestsByURL[url] = request | |
let urlRequest = URLRequest(url: url) | |
do { | |
print("requesting...") | |
let (data, _) = try await URLSession.shared.data(for: urlRequest) | |
let image = UIImage(data: data)! | |
request = .success(image) | |
} catch { | |
print("Failed to fetch resource:", error) | |
request = .failed(error) | |
} | |
requestsByURL[url] = request | |
for channel in requests { | |
await channel.send((url, request)) | |
} | |
} | |
} | |
} | |
} | |
struct MyView: View { | |
let resourceCache: ResourceCache | |
@State var image: UIImage? | |
let text: String | |
let url: URL | |
var body: some View { | |
Group { | |
if let image { | |
Image(uiImage: image) | |
} | |
else { | |
Text("Loading") | |
.task { | |
await withDiscardingTaskGroup { group in | |
group.addTask { @MainActor in | |
let requests = AsyncChannel<(URL, ResourceCache.Request)>() | |
resourceCache.requests.append(requests) | |
for await (_, request) in requests.filter({ url, request in | |
url == self.url | |
}) { | |
switch request { | |
case .success(let image): | |
self.image = image | |
default: | |
break | |
} | |
} | |
resourceCache.requests.removeAll { requests === $0 } | |
} | |
print("sending \(text)") | |
await resourceCache.urls.send(url) | |
print("sent \(text)") | |
} | |
} | |
} | |
} | |
} | |
} | |
struct ContentView: View { | |
@State var resourceCache: ResourceCache? | |
var body: some View { | |
if let resourceCache { | |
VStack { | |
MyView(resourceCache: resourceCache, text: "1", url: URL(string: "https://avatars.githubusercontent.com/u/61242156?s=100&v=4")!) | |
MyView(resourceCache: resourceCache, text: "2", url: URL(string: "https://avatars.githubusercontent.com/u/31996?s=100&v=4")!) | |
MyView(resourceCache: resourceCache, text: "3", url: URL(string: "https://avatars.githubusercontent.com/u/61242156?s=100&v=4")!) | |
} | |
}else { | |
Text("Initialising...") | |
.onAppear { | |
resourceCache = ResourceCache() | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment