Share
Embed
Transcript
39 slides
-
७ਖ਼όοΫϙʔτ / privateͩͬͨγϯϘϧΛpublicʹมΘΔ • ϑϨʔϜϫʔΫͷϔομʔɾΠϯλϑΣʔεʹ͋Δͷެ։γϯϘϧ͚ͩ • ݹ͍OSͰඇެ։ͷΫϥε͕ؔଘࡏ͍ͯͨ͠Βɺ৽͍͠ϦϦʔεʹ߹Θͤͯ ެ։ʹ͢Δ͜ͱͰόοΫϙʔτ͕Մೳ class MyViewController:
UIViewController { override func viewIsAppearing(_ animated: Bool) { super.viewIsAppearing(animated) // ... } } -
७ਖ਼όοΫϙʔτ / σϑΥϧτҾ͕มΘΔ • 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 ) { // ... } -
७ਖ਼όοΫϙʔτ / ࣮͕·Δ͝ͱ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 ) } } -
७ਖ਼όοΫϙʔτ / ࣮͕·Δ͝ͱswiftinterfaceʹల։͞ΕΔ .cornerRadius(16) // deprecated .clipShape(RoundedRectangle(cornerRadius: 16)) .clipShape(.rect(cornerRadius: 16))
.clipShape(.rect) .clipShape(.circle) .clipShape(.capsule) -
७ਖ਼όοΫϙʔτ / 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 -
७ਖ਼όοΫϙʔτ / ݹ͍OS͚ͩswiftinterfaceʹ࣮͕ల։͞ΕΔ • ݹ͍OSͰγϯϘϧ͕ଘࡏ͍ͯ͜͠ͱʹ͢ΔͨΊͷम০: @backDeployed • ৽OSϥΠϒϥϦͷόΠφϦΛ͍ͭͭɺݹ͍OSswiftinterfaceͷ࣮Λ͏͜ͱͱ͔ • 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) }) } ) } } } } -
ϚΫϩ / 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" } -
ϚΫϩ / 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) } -
OSSͷόοΫϙʔτ / SwiftUI৭ʑ • ͋Ε͜Εͷ SwiftUI ػೳͷόοΫϙʔτ • StateObject •
AppStorage • AsyncImage • .task • ͳͲͳͲ shaps80/SwiftUIBackports -
OSSͷόοΫϙʔτ / Observation • iOS 17Ͱग़ͨObservationϑϨʔϜϫʔΫΛ ·Δ͝ͱόοΫϙʔτ • ຐ๏ 🪄✨
pointfreeco/swift-perception -
OSSͷόοΫϙʔτ / Group(subviews:transform:) • iOS 18Ͱ৽͘͠Ճ͞Εͨ Group ͷػೳ • GroupͷதͷࢠϏϡʔͰԿΒ͔ͷॲཧΛ͢Δ
• iOS 17ҎԼͰඇެ։ͷVariadicViewΛ͏ඞ ཁ͕͋ͬͨ Lumisilk/SwiftUI-AnySubviews -
ͦͦόοΫϙʔτͰ͖Δ͔ʁ • όοΫϙʔτͰ͖ͳ͍ͷ͕͋Δ • OSͱີʹؔΘΔػೳ • WidgetsɺApp IntentsɺAppϥΠϑαΠΫϧ • ݴޠ༻
• RegexɺConcurrency • είʔϓ͕σΧ͗ͯ͢ݱ࣮తʹࣗલ࣮Ͱ͖ͳ͍ • SwiftUIશମ -
ʮόοΫϙʔτʯ͔ʮϑΥʔϧόοΫʯ͔ • ྫ͑ɺiOS 17 Ͱొͨ͠ ContentUnavailableView Λ͍͍ͨ߹ ContentUnavailableView { Label("ݕࡧ݁Ռ͕͋Γ·ͤΜ",
systemImage: "magnifyingglass") } description: { Text("ผͷΩʔϫʔυΛ͓ࢼ͍ͩ͘͠͞") } -
ʮόοΫϙʔτʯ͔ʮϑΥʔϧόοΫʯ͔ @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) } } } } -
όοΫϙʔτΛ࣮ / దͳωʔϛϯά • ޙ΄Ͳআ͞ΕΔલఏͷίʔυ • Ͳ͜ͰΘΕ͍ͯΔ͔ɺͲ͏ͬͯണ͕͢ͷ͔Λ࣮͔࣌ΒܾΊΔ • enum Backport
ͰωʔϜεϖʔεΛ࡞͓ͬͯ͘ͱศར • ݩωλ Dave DeLong ࢯͷهࣄ • https://davedelong.com/blog/2021/10/09/simplifying-backwards-compatibility- in-swift/ -
όοΫϙʔτΛ࣮ / దͳωʔϛϯά 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() -
όοΫϙʔτΛ࣮ / OSͷόʔδϣϯͰ͍͚Δ • ৽OSͰ৽ػೳΛ७ਖ਼ͷ··ɺݹ͍OSͰόοΫϙʔτͱ͍͏͍͚ extension Backport where Content: View
{ func myNewFunction() -> some View { if #available(iOS 18, *) { // ... } else { ... } } } -
όοΫϙʔτΛ࣮ / 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() } } } -
όοΫϙʔτΛ࣮ / 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() } } } -
όοΫϙʔτΛ࣮ / geometryGroup • geometryGroupiOS 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) // ...