Skip to content

Instantly share code, notes, and snippets.

@shayanbo
Last active July 6, 2023 09:42
Show Gist options
  • Save shayanbo/46e08a08aafe2bddcba2d7c48020e33e to your computer and use it in GitHub Desktop.
Save shayanbo/46e08a08aafe2bddcba2d7c48020e33e to your computer and use it in GitHub Desktop.
Enhanced AsyncImage Sample
import SwiftUI
import CommonCrypto
struct AsyncImage<Content> : View where Content: View {
enum Error : Swift.Error {
case invalidData
}
private let url: URL
@State
private var phase: AsyncImagePhase = .empty
@ViewBuilder
private let content: (AsyncImagePhase) -> Content
init(url: URL) where Content == Image {
self.url = url
self.content = { phase in
phase.image ?? Image("")
}
}
init<I, P>(url: URL, content: @escaping (Image)->I, placeholder: @escaping () -> P) where Content == Group<_ConditionalContent<I, P>>, I : View, P : View {
self.url = url
self.content = { phase in
Group {
if let image = phase.image {
content(image)
} else {
placeholder()
}
}
}
}
init(url: URL, @ViewBuilder content: @escaping (AsyncImagePhase) -> Content) {
self.url = url
self.content = content
}
var body: some View {
content(phase).task {
/// try cache
let cachedName = url.absoluteString.md5
let cacheURL = URL.cachesDirectory.appending(path: cachedName)
var cachePath = cacheURL.absoluteString
cachePath.replace("file://", with: "")
if FileManager.default.fileExists(atPath: cachePath) {
if let image = try? UIImage(data: Data(contentsOf: cacheURL)) {
self.phase = .success(Image(uiImage: image))
return
}
}
/// load from remote
do {
let result = try await URLSession.shared.data(from: url)
if let image = UIImage(data: result.0) {
/// disk cache
let cachedName = url.absoluteString.md5
let cachePath = URL.cachesDirectory.appending(path: cachedName)
try? result.0.write(to: cachePath)
self.phase = .success(Image(uiImage: image))
} else {
self.phase = .failure(Error.invalidData)
}
} catch (let error) {
self.phase = .failure(error)
}
}
}
}
extension String {
var md5: String {
guard let data = data(using: .utf8) else {
return self
}
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
_ = data.withUnsafeBytes { (bytes: UnsafeRawBufferPointer) in
return CC_MD5(bytes.baseAddress, CC_LONG(data.count), &digest)
}
return digest.map { String(format: "%02x", $0) }.joined()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment