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

iOSDC 2024

Sponsored · Ship Features Fearlessly Turn features on and off without deploys. Used by thousands of Ruby developers.

iOSDC 2024

Avatar for Mike Apurin

Mike Apurin

August 22, 2024
Tweet

More Decks by Mike Apurin

Other Decks in Programming

Transcript

  1. ७ਖ਼όοΫϙʔτ / σϑΥϧτҾ਺͕มΘΔ • SwiftͰ͸ɺσϑΥϧτҾ਺͕ϑϨʔϜϫʔΫͷόΠφϦʹ૊Έࠐ·Εͣ swiftinterfaceʹଘࡏ͍ͯ͠Δ @frozen public struct RoundedRectangle

    : SwiftUI.Shape { @inlinable public init( cornerRadius: CoreFoundation.CGFloat, style: SwiftUI.RoundedCornerStyle = .circular ) { // ... } } @frozen public struct RoundedRectangle : SwiftUICore.Shape { @inlinable nonisolated public init( cornerSize: CoreFoundation.CGSize, style: SwiftUICore.RoundedCornerStyle = .continuous ) { // ... }
  2. ७ਖ਼όοΫϙʔτ / ࣮૷͕·Δ͝ͱswiftinterfaceʹల։͞ΕΔ • ؔ਺Λ @inlinable ΍ @_alwaysEmitIntoClient Ͱम০͢Δͱɺ࣮૷͕ϑϨʔϜϫʔ ΫͷόΠφϦʹ૊Έࠐ·Εͣ

    swiftinterface ʹιʔείʔυͷ··Ͱίϐʔ͞ΕΔ • όοΫϙʔτʹݶΒͣ SwiftUI Ͱͦͷύλʔϯ͕ଟ͘࢖ΘΕ͍ͯΔ extension SwiftUICore.Shape where Self == SwiftUICore.RoundedRectangle { @_alwaysEmitIntoClient public static func rect( cornerSize: CoreFoundation.CGSize, style: SwiftUICore.RoundedCornerStyle = .continuous ) -> Self { .init( cornerSize: cornerSize, style: style ) } }
  3. ७ਖ਼όοΫϙʔτ / iOS 18 nonisolated public func onGeometryChange<T>( for type:

    T.Type, of transform: @escaping (GeometryProxy) -> T, action: @escaping (_ newValue: T) -> Void ) -> some View where T : Equatable nonisolated public func gesture<T>( _ gesture: T, isEnabled: Bool ) -> some View where T : Gesture nonisolated public func tag<V>( _ tag: V, includeOptional: Bool = true ) -> some View where V : Hashable
  4. ७ਖ਼όοΫϙʔτ / ݹ͍OS͚ͩswiftinterfaceʹ࣮૷͕ల։͞ΕΔ • ݹ͍OSͰγϯϘϧ͕ଘࡏ͍ͯ͜͠ͱʹ͢ΔͨΊͷम০: @backDeployed • ৽OS͸ϥΠϒϥϦͷόΠφϦΛ࢖͍ͭͭɺݹ͍OS͸swiftinterfaceͷ࣮૷Λ࢖͏͜ͱͱ͔ • SwiftUIʹ͸΄ͱΜͲݟͳ͍͕ɺStoreKitͰ͸ಛʹΑ͘࢖ΘΕ͍ͯΔ

    struct Transaction { @backDeployed(before: iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1) public var currency: Foundation.Locale.Currency? { get { if #available(iOS 17.2, macOS 14.2, tvOS 17.2, watchOS 10.2, visionOS 1.1, *) { return currencyStorage } else { return backing.value( atKeyPath: "currency", sentinel: nil, transform: { String($0).map({ Locale.Currency($0) }) } ) } } } }
  5. ϚΫϩ / Xcode 16 struct MyCustomValueKey: EnvironmentKey { static var

    defaultValue: String { "Default value" } } extension EnvironmentValues { var myCustomValue: String { get { self[MyCustomValueKey.self] } set { self[MyCustomValueKey.self] = newValue} } } extension EnvironmentValues { @Entry var myCustomValue: String = "Default value" }
  6. ϚΫϩ / Xcode 16 private struct PreviewContent: View { @State

    var toggled = true var body: some View { Toggle("Loud Noises", isOn: $toggled) } } #Preview("toggle") { PreviewContent() } #Preview("toggle") { @Previewable @State var toggled = true return Toggle("Loud Noises", isOn: $toggled) }
  7. OSSͷόοΫϙʔτ / SwiftUI৭ʑ • ͋Ε΍͜Εͷ SwiftUI ػೳͷόοΫϙʔτ • StateObject •

    AppStorage • AsyncImage • .task • ͳͲͳͲ shaps80/SwiftUIBackports
  8. OSSͷόοΫϙʔτ / Group(subviews:transform:) • iOS 18Ͱ৽͘͠௥Ճ͞Εͨ Group ͷػೳ • GroupͷதͷࢠϏϡʔͰԿΒ͔ͷॲཧΛ͢Δ

    • iOS 17ҎԼͰ͸ඇެ։ͷVariadicViewΛ࢖͏ඞ ཁ͕͋ͬͨ Lumisilk/SwiftUI-AnySubviews
  9. ʮόοΫϙʔτʯ͔ʮϑΥʔϧόοΫʯ͔ @ViewBuilder var unavailableView: some View { if #available(iOS 17,

    *) { // ... } else { VStack(spacing: 16) { Image(systemName: "magnifyingglass") .font(.system(.largeTitle).weight(.medium)) .imageScale(.large) .foregroundColor(.secondary) VStack { Text("ݕࡧ݁Ռ͕͋Γ·ͤΜ") .font(.title2.bold()) Text("ผͷΩʔϫʔυΛ͓ࢼ͍ͩ͘͠͞") .font(.callout) .foregroundColor(.secondary) } } } }
  10. όοΫϙʔτΛ࣮૷ / ద੾ͳωʔϛϯά • ޙ΄Ͳ࡟আ͞ΕΔલఏͷίʔυ • Ͳ͜Ͱ࢖ΘΕ͍ͯΔ͔ɺͲ͏΍ͬͯണ͕͢ͷ͔Λ࣮૷౰͔࣌ΒܾΊΔ • enum Backport

    ͰωʔϜεϖʔεΛ࡞͓ͬͯ͘ͱศར • ݩωλ͸ Dave DeLong ࢯͷهࣄ • https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility- in-swift/
  11. όοΫϙʔτΛ࣮૷ / ద੾ͳωʔϛϯά public struct Backport<Content> { public let content:

    Content public init(_ content: Content) { self.content = content } } extension View { var backport: Backport<Self> { Backport(self) } } extension Backport where Content: View { func myNewFunction() -> some View { // ... } } // ... Text("iOSDC 2024") .backport.myNewFunction()
  12. όοΫϙʔτΛ࣮૷ / geometryGroup struct ContentView: View { @State var flag

    = false @State var flag2 = false var body: some View { Color.cyan .frame(width: 128, height: 128) .overlay { Group { if flag2 { Text("iOSDC") } else { Text("લ໷ࡇ") } } .task(id: flag2) { try? await Task.sleep(for: .seconds(1.5)) flag2.toggle() } } .offset(y: flag ? 100 : -100) .animation(.linear(duration: 1).repeatForever(), value: flag) .onAppear() { flag.toggle() } } }
  13. όοΫϙʔτΛ࣮૷ / geometryGroup struct ContentView: View { @State var flag

    = false @State var flag2 = false var body: some View { Color.cyan .frame(width: 128, height: 128) .overlay { Group { if flag2 { Text("iOSDC") } else { Text("લ໷ࡇ") } } .task(id: flag2) { try? await Task.sleep(for: .seconds(1.5)) flag2.toggle() } } .geometryGroup() .offset(y: flag ? 100 : -100) .animation(.linear(duration: 1).repeatForever(), value: flag) .onAppear() { flag.toggle() } } }
  14. όοΫϙʔτΛ࣮૷ / geometryGroup • geometryGroup͸iOS 17Ҏ߱ 😇 • ͨͩ͠ɺ࣮͸ผͷॻ͖ํͰಉ༷ͳޮՌ͕ಘΒΕΔ extension

    Backport where Content: View { @ViewBuilder func geometryGroup() -> some View { if #available(iOS 17, *) { content.geometryGroup() } else { content.transformEffect(.identity) } } } // ... } .backport.geometryGroup() .offset(y: flag ? 100 : -100) // ...