Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Backport AsyncImage

Backport AsyncImage

集まれSwift好き!Swift愛好会 vol.61 @ オンライン https://love-swift.connpass.com/event/218651/

D36d6bab8f1e0ff4bb3377571e5f7dcd?s=128

Yutaro Muta

July 21, 2021
Tweet

Transcript

  1. Backport AsyncImage 2021/07/21 @ू·ΕSwift޷͖ʂSwiftѪ޷ձ vol.61 Yutaro Muta @yutailang0119

  2. • Yutaro Muta @yutailang0119 • Hatena Co., Ltd. @Kyoto •

    Conference Sta f Who am I ?
  3. Goal • SwiftUI.AsyncImage Λ஌Δ • ࠓ೔ (Xcode 12.5.1) ͔Β࢖͑Δ SBPAsyncImage

    • SBPAsyncImage ͔ΒֶͿ SwiftUI Trick
  4. Environment • Xcode Version 13.0 beta 3 (13A5192i) • Xcode

    Version 12.5.1 (12E507)
  5. SwiftUI.AsyncImage

  6. SwiftUI.AsyncImage • > A view that asynchronously loads and displays

    an image. • Available on Xcode 13 struct AsyncImage<Content> where Content : View https://developer.apple.com/documentation/swiftui/asyncimage
  7. SwiftUI.AsyncImage AsyncImage(url: URL(string: “https://example.com/icon.png")) .frame(width: 200, height: 200) AsyncImage(url: URL(string:

    "https://example.com/icon.png")) { image in image.resizable() } placeholder: { ProgressView() } .frame(width: 50, height: 50)
  8. SwiftUI.AsyncImage Xcode 13 beta 3ݱࡏ • ⚠ URLRequestʹະରԠ • ϦΫΤετຖʹΩϟογϡϙϦγʔ΍λΠϜΞ΢τͳͲΛίϯτϩʔϧ

    Ͱ͖ͳ͍ • ⚠ ௚઀తͳը૾ͷΩϟογϡػೳ͸ͳ͍
  9. • ⚠ Availability • iOS 15.0+ Beta • macOS 12.0+

    Beta • Mac Catalyst 15.0+ Beta • tvOS 15.0+ Beta • watchOS 8.0+ Beta SwiftUI.AsyncImage
  10. ࠓ͙͢ SwiftUI.AsyncImage Λ࢖͍͍ͨ

  11. yutailang0119/SBPAsyncImage

  12. yutailang0119/SBPAsyncImage • > Backport of SwiftUI.AsyncImage to iOS 14, macOS

    11, tvOS 14 and watchOS 7 and earlier. > SBPAsyncImage provides interface and behavior of AsyncImage to earlier OS. • Available on Xcode 12.5.1 struct BackportAsyncImage<Content> where Content : View typealias AsyncImage = BackportAsyncImage https://github.com/yutailang0119/SBPAsyncImage
  13. yutailang0119/SBPAsyncImage BackportAsyncImage(url: URL(string: “https://example.com/icon.png")) .frame(width: 200, height: 200) SBPAsyncImage.AsyncImage(url: URL(string:

    "https://example.com/icon.png")) { image in image.resizable() } placeholder: { ProgressView() } .frame(width: 50, height: 50)
  14. Availability • iOS 13.0+ • macOS 10.5+ • Mac Catalyst

    13.0+ • tvOS 13.0+ • watchOS 6.0+ yutailang0119/SBPAsyncImage
  15. Availability • iOS 13.0+ • macOS 10.5+ • Mac Catalyst

    13.0+ • tvOS 13.0+ • watchOS 6.0+ yutailang0119/SBPAsyncImage ✅ Cover all SwiftUI platforms
  16. yutailang0119/SBPAsyncImage • ⚠ SwiftUI.AsyncImage Λ׬શʹτϨʔεͰ͖ͯ͸͍ͳ͍ • ྫ͑͹ɺ௨৴ͷΩϟϯηϧ • SwiftUI.AsyncImage ͸

    task(_:) Ͱ௨৴Λ։͍࢝ͯͦ͠͏ • ⚠ macOS ͰScaleʹରԠͰ͖͍ͯͳ͍ • https://github.com/yutailang0119/SBPAsyncImage/issues/8 • Welcome Pull Request 🥴
  17. SBPAsyncImage Tricks

  18. Case1: @ObservableObject ͸ ඳըຖʹinit͞ΕΔ

  19. Case1: @ObservableObject ͸ඳըຖʹinit͞ΕΔ • @ObservableObject Ͱఆٛ͢ΔϓϩύςΟ͸ɺRootͷView͕ඳը͞ΕΔ ຖʹinit͞ΕΔ • ObservedObject ͕࣋ͭ஋͸อ࣋͞Εͳ͍

    https://developer.apple.com/documentation/combine/observableobject
  20. • https://github.com/yutailang0119/SBPAsyncImage/pull/5 Ҏલ͸ɺҎԼͷ Α͏ͳ࢖͍ํΛ͢ΔͱɺϘλϯΛԡ͢౓ʹ௨৴͞Ε͍ͯͨ • ϘλϯΛԡ͢౓ʹɺҰ౓ը૾͕ۭʹͳΔ struct ContentView: View {

    @State var count: Int = 0 var body: some View { VStack { SBPAsyncImage.AsyncImage(url: URL(string: "https://example.com/icon.png")) Button { count += 1 } label: { Text("Count: \(count)") } } } } Case1: @ObservableObject ͸ඳըຖʹinit͞ΕΔ
  21. • @StateObject Λ࢖ͬͯղܾ • ⚠ Availability • iOS 14.0+ •

    macOS 11.0+ • Mac Catalyst 14.0+ • tvOS 14.0+ • watchOS 7.0+ https://developer.apple.com/documentation/swiftui/stateobject Case1: @ObservableObject ͸ඳըຖʹinit͞ΕΔ
  22. • @StateObject Λ࢖͑ͳ͍ҎલͷOSͰ͸ @ObservableObject ͱ @State Λ૊Έ߹ΘͤͯɺৼΔ෣͍Λ໛฿͢Δ • ͘Θ͘͠͸ `iOS

    13Ͱ΋StateObject͕࢖͍͍ͨʂ` • https://kouki.hatenadiary.com/entry/2021/06/22/130000 Case1: @ObservableObject ͸ඳըຖʹinit͞ΕΔ
  23. Case2: StateObject Λ ௚઀initͯ͠͸͍͚ͳ͍

  24. • StateObject.init(wrappedValue:) • > You don’t call this initializer directly.

    Instead, declare a property with the @StateObject attribute in a View, App, or Scene, and provide an initial value: • initΛ௚઀࣮ߦ͢ΔͱɺSwiftUIͷϝϞϦ؅ཧ΍ @autoclosure ͷ࠷దԽ͕ ޮ͔ͳ͘ͳΔ https://developer.apple.com/documentation/swiftui/stateobject/init(wrappedvalue:) Case2: StateObject Λ௚઀initͯ͠͸͍͚ͳ͍ init(wrappedValue thunk: @autoclosure @escaping () -> ObjectType)
  25. • https://github.com/yutailang0119/SBPAsyncImage/pull/14 Ҏલ͸ɺҎԼ ͷΑ͏ʹStateObjectΛ௚઀init͍ͯͨ͠ @available(iOS 14.0, macOS 11.0, tvOS 14.0,

    watchOS 7.0, *) private struct ContentBody<Content: View>: View { @StateObject private var viewModel: ViewModel private let content: (AsyncImagePhase) -> Content init(viewModel: ViewModel, @ViewBuilder content: @escaping (AsyncImagePhase) -> Content) { self._viewModel = .init(wrappedValue: viewModel) self.content = content } } Case2: StateObject Λ௚઀initͯ͠͸͍͚ͳ͍
  26. • @StateObject ͷinit͸Viewʹ೚ͤͯɺStateObject͕ߦ͍͍ͨॲཧ͸ɺ onAppear(perform:) ʹҠಈ • ͘Θ͘͠͸ `Explanation behind the

    error? “Accessing StateObject’s object without being installed on a View. This will create a new instance each time.”` • https://forums.swift.org/t/explanation-behind-the-error-accessing- stateobjects-object-without-being-installed-on-a-view-this-will- create-a-new-instance-each-time/40111 Case2: StateObject Λ௚઀initͯ͠͸͍͚ͳ͍
  27. @available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) private

    struct ContentBody<Content: View>: View { @StateObject private var viewModel = ViewModel() ... var body: some View { content(viewModel.phase) .onAppear { viewModel.download(url: url, scale: scale, transaction: transaction) } } } Case2: StateObject Λ௚઀initͯ͠͸͍͚ͳ͍
  28. ·ͱΊ

  29. ·ͱΊ • SwiftUI.AsyncImage ศརͰ͢Ͷ • ૈ͍Օॴ΋͋Δ • yutailang0119/SBPAsyncImage ΛΑΖ͘͠ •

    SwiftUI׆༻ʹ͸Trick͕ຬࡌ
  30. Reference • https://developer.apple.com/documentation/swiftui/asyncimage • https://github.com/yutailang0119/SBPAsyncImage • https://developer.apple.com/documentation/combine/observableobject • https://developer.apple.com/documentation/swiftui/stateobject •

    https://kouki.hatenadiary.com/entry/2021/06/22/130000 • https://developer.apple.com/documentation/swiftui/stateobject/init(wrappedvalue:) • https://forums.swift.org/t/explanation-behind-the-error-accessing-stateobjects-object-without- being-installed-on-a-view-this-will-create-a-new-instance-each-time/40111
  31. &OKPZ"TZOD*NBHF 5IBOLT w NVUBZVUBSP!HNBJMDPN w IUUQTUXJUUFSDPNZVUBJMBOH w IUUQTHJUIVCDPNZVUBJMBOH