Help us understand the problem. What is going on with this article?

[Deprecated] SwiftUI - Xcode 11 beta 4 での変更点

More than 1 year has passed since last update.

Xcode 11 beta 5がリリースされたので、新しい記事「SwiftUI - Xcode 11 beta 5 での変更点」を書きました。
このため、本記事はお役御免となりました。

よかったら「SwiftUI - Xcode 11 beta 5 での変更点」の方も読んでいただけると幸いです。

以下は元記事です。


はじめに

Xcode 11 beta 4が公開されたので、SwiftUI Tutorialsで確認できたSwiftUIのbeta 3beta 4での変更点について、以下にまとめていきます。
(文末にbeta 2beta 3での変更点も記載しています。)

beta 3 → beta 4 での変更点

本記事内で触れていない変更点などもありますので、詳細を知りたい場合は公式のリリースノートを参照してみてください。
- iOS & iPadOS 13 Beta 4 Release Notes

BindableObjectの仕様変更

The BindableObject protocol’s requirement is now willChange instead of didChange, and should now be sent before the object changes rather than after it changes. This change allows for improved coalescing of change notifications. (51580731)

didChangewillChangeに変更されました。
仕様自体もオブジェクトが変更された後ではなく、変更される前に送信されるようになり、変更通知の統合が改善されたようです。

Landmarks/Models/UserData.swift

 final class UserData: BindableObject {
-    let didChange = PassthroughSubject<UserData, Never>()
+    let willChange = PassthroughSubject<Void, Never>()

     var showFavoritesOnly = false {
-        didSet {
-            didChange.send(self)
+        willSet {
+            willChange.send()
         }
     }

     var landmarks = landmarkData {
-        didSet {
-            didChange.send(self)
+        willSet {
+            willChange.send()
         }
     }
 }

Textの仕様変更

The color(:) modifier for Text is renamed foregroundColor(:) for consistency with the more general foregroundColor(_:) view modifier. (50391847)

colorforegroundColorに変名されました。

Landmarks/CategoryRow.swift

  Text(landmark.name)
-     .color(.primary)
+     .foregroundColor(.primary)
      .font(.caption)

Collectionの仕様変更

The identified(by:) method on the Collection protocol is deprecated in favor of dedicated init(:id:selection:rowContent:) and init(:id:content:) initializers. (52976883)

identified(by:)メソッドがdeprecatedに変更され、ForEach(_:id:)またはList(_:id:)の利用が推奨となりました。

Landmarks/Home.swift

- ForEach(categories.keys.sorted().identified(by: \.self)) { key in
+ ForEach(categories.keys.sorted(), id: \.self) { key in
      CategoryRow(categoryName: key, items: self.categories[key]!)
  }
  .listRowInsets(EdgeInsets())

DatePickerの仕様変更

'init(_:minimumDate:maximumDate:displayedComponents:)' is deprecated: DatePicker labels are now required

labelを引数に取らない初期化メソッドがdeprecatedになりました。

Landmarks/Profiles/ProfileEditor.swift

  DatePicker(
-     $profile.goalDate,
+     "Goal Date",
+     selection: $profile.goalDate,
      minimumDate: Calendar.current.date(byAdding: .year, value: -1, to: profile.goalDate),
      maximumDate: Calendar.current.date(byAdding: .year, value: 1, to: profile.goalDate),
      displayedComponents: .date)

なお、beta 4のSwiftUI Tutorialでは、仕様変更の対応以外に次のリファクタリングも行われていました。

+ var dateRange: ClosedRange<Date> {
+     let min = Calendar.current.date(byAdding: .year, value: -1, to: profile.goalDate)!
+     let max = Calendar.current.date(byAdding: .year, value: 1, to: profile.goalDate)!
+     return min...max
+ }

  DatePicker(
-     $profile.goalDate,
-     minimumDate: Calendar.current.date(byAdding: .year, value: -1, to: profile.goalDate),
-     maximumDate: Calendar.current.date(byAdding: .year, value: 1, to: profile.goalDate),
+     "Goal Date",
+     selection: $profile.goalDate,
+     in: dateRange,
      displayedComponents: .date)

AnyTransitionの仕様変更

Cannot invoke 'scale' with no arguments

scaleがメソッドからプロパティに変更されました。

Landmarks/HikeView.swift

-        let removal = AnyTransition.scale()
+        let removal = AnyTransition.scale

presentation(_:), Sheet, Modal, PresentationLink

Added improved presentation modifiers: sheet(isPresented:onDismiss:content:), actionSheet(isPresented:content:), and alert(isPresented:content:) — along with isPresented in the environment — replace the existing presentation(_:), Sheet, Modal, and PresentationLink types. (52075730)

presentation(_:), Sheet, Modal, PresentationLinkがdeprecatedになりました。
併せて、これらの機能に相当する機能がViewのメソッドとして追加されています。

  • sheet(isPresented:onDismiss:content:)
  • actionSheet(isPresented:content:)
  • alert(isPresented:content:)

SwiftUI TutorialsではPresentationLinkが利用されていましたが、sheet(isPresented:onDismiss:content:)に変更されていました。

Landmarks/Home.swift

+ @State var showingProfile = false
+ 
+ var profileButton: some View {
+     Button(action: { self.showingProfile.toggle() }) {
+         Image(systemName: "person.crop.circle")
+             .imageScale(.large)
+             .accessibility(label: Text("User Profile"))
+             .padding()
+     }
+ }
+
  var body: some View {
      NavigationView {
          List {
              FeaturedLandmarks(landmarks: featured)
                  .scaledToFill()
                  .frame(height: 200)
                  .clipped()
                  .listRowInsets(EdgeInsets())

              ForEach(categories.keys.sorted(), id: \.self) { key in
                  CategoryRow(categoryName: key, items: self.categories[key]!)
              }
              .listRowInsets(EdgeInsets())

              NavigationLink(destination: LandmarkList()) {
                  Text("See All")
              }
          }
          .navigationBarTitle(Text("Featured"))
-         .navigationBarItems(trailing:
-             PresentationLink(destination: ProfileHost()) {
-                 Image(systemName: "person.crop.circle")
-                     .imageScale(.large)
-                     .accessibility(label: Text("User Profile"))
-                     .padding()
-             }
-         )
+         .navigationBarItems(trailing: profileButton)
+         .sheet(isPresented: $showingProfile) {
+             ProfileHost()
+         }
      }
  }

Animationの仕様変更

Updated the APIs for creating animations. The basic animations are now named after the curve type — such as linear and easeInOut. The interpolation-based spring(mass:stiffness:damping:initialVelocity:) animation is now interpolatingSpring(mass:stiffness:damping:initialVelocity:), and fluidSpring(stiffness:dampingFraction:blendDuration:timestep:idleThreshold:) is now spring(response:dampingFraction:blendDuration:) or interactiveSpring(response:dampingFraction:blendDuration:), depending on whether or not the animation is driven interactively. (50280375)

spring(mass:stiffness:damping:initialVelocity:), fluidSpring(stiffness:dampingFraction:blendDuration:timestep:idleThreshold:)がdeprecatedになりました。
併せて、これらのメソッドに相当するメソッドが新たに追加されています。

  • interpolatingSpring(mass:stiffness:damping:initialVelocity:)
  • spring(response:dampingFraction:blendDuration:)
  • interactiveSpring(response:dampingFraction:blendDuration:)

Landmarks/Supporting View/GraphCapsule.swift

  var animation: Animation {
-     Animation.spring(initialVelocity: 5)
+     Animation.spring(dampingFraction: 0.5)
        .speed(2)
        .delay(0.03 * Double(index))
  }

その他の変更点

  • リファクタリング
    • LandmarkListへの遷移元(Home.swift)でNavigationViewが生成されているため、LandmarkListではNavigationViewが削除されました。

Landmarks/LandmarkList.swift

  var body: some View {
-     NavigationView {
-         List {
-             Toggle(isOn: $userData.showFavoritesOnly) {
-                 Text("Show Favorites Only")
-             }
-             
-             ForEach(userData.landmarks) { landmark in
-                 if !self.userData.showFavoritesOnly || landmark.isFavorite {
-                     NavigationLink(
-                         destination: LandmarkDetail(landmark: landmark)
-                             .environmentObject(self.userData)
-                     ) {
-                         LandmarkRow(landmark: landmark)
-                     }
+     List {
+         Toggle(isOn: $userData.showFavoritesOnly) {
+             Text("Show Favorites Only")
+         }
+         
+         ForEach(userData.landmarks) { landmark in
+             if !self.userData.showFavoritesOnly || landmark.isFavorite {
+                 NavigationLink(
+                     destination: LandmarkDetail(landmark: landmark)
+                 ) {
+                     LandmarkRow(landmark: landmark)
                  }
              }
          }
-         .navigationBarTitle(Text("Landmarks"), displayMode: .large)
      }
+     .navigationBarTitle(Text("Landmarks"), displayMode: .large)
  }

beta 2 → beta 3 での変更点

画面遷移に関する構造体の仕様変更

構造体名が XXXButton から XXXLink に変更されました。
SwiftUI Tutorials では以下の2つが利用されており、名前が変更されています。

beta 2 beta 3
NavigationButton NavigationLink
PresentationButton PresentationLink

※ PresentationLinkはbeta4でdeprecatedになりました。

Landmarks/Home.swift

-     NavigationButton(destination: LandmarkList()) {
+     NavigationLink(destination: LandmarkList()) {
          Text("See All")
      }
  }
  .navigationBarTitle(Text("Featured"))
  .navigationBarItems(trailing:
-     PresentationButton(destination: ProfileHost()) {
+     PresentationLink(destination: ProfileHost()) {
          Image(systemName: "person.crop.circle")
              .imageScale(.large)
              .accessibility(label: Text("User Profile"))

ScrollViewの仕様変更

beta 3ではAxis.SetshowsIndicatorsの組合せで振る舞いを指定するように仕様変更されているのですが、beta 2では指定できていたBounceの指定や細かい組合せが実現できないように見えます。

beta 2ScrollViewのinit仕様

public init(isScrollEnabled: Bool = true, alwaysBounceHorizontal: Bool = false, alwaysBounceVertical: Bool = false, showsHorizontalIndicator: Bool = true, showsVerticalIndicator: Bool = true, content: () -> Content)

beta 3ScrollViewのinit仕様

public init(_ axes: Axis.Set = .vertical, showsIndicators: Bool = true, content: () -> Content)

Landmarks/CategoryRow.swift

- ScrollView(showsHorizontalIndicator: false) {
+ ScrollView(.horizontal, showsIndicators: false) {
      HStack(alignment: .top, spacing: 0) {
          ForEach(self.items.identified(by: \.name)) { landmark in

TextFieldの仕様変更

beta 2での初期化方法がdeprecatedに変更され、beta 3では新たな初期化方法が追加されました。

Landmarks/Profiles/ProfileEditor.swift

  HStack {
      Text("Username").bold()
      Divider()
-     TextField($profile.username)
+     TextField("Username", text: $profile.username)
  }

UIWindowの仕様変更

SwiftUI Tutorialsで利用されていた初期化方法が、beta2beta3では異なりました。
beta 2では、UIViewから継承したinitが利用されていましたが、beta3ではUIWindowとして定義されているinitが利用されており、より良い方法に変更されたようです。

Landmarks/SceneDelegate.swift

- let window = UIWindow(frame: UIScreen.main.bounds)
- window.rootViewController = UIHostingController(rootView: CategoryHome().environmentObject(UserData()))
- self.window = window
- window.makeKeyAndVisible()
+ if let windowScene = scene as? UIWindowScene {
+     let window = UIWindow(windowScene: windowScene)
+     window.rootViewController = UIHostingController(rootView: CategoryHome().environmentObject(UserData()))
+     self.window = window
+     window.makeKeyAndVisible()
+ }

最後に

上記内容は、SwiftUI Tutorialsの変更点を元に調査を行い変更点をまとめました。
最新のbeta(現時点ではXcode 11.0 beta3 - Xcode 11.0 beta 4)の詳細な変更点につきましては、以下のApple公式サイトで確認することが可能ですので、興味がある方は確認してみてください。
https://developer.apple.com/documentation/swiftui?changes=latest_beta
Xcode 11.0 beta2 - Xcode 11.0 beta 3.png

zizi4n5
東急ハンズの内製iPadPOSアプリ、HandsPOS開発チームでテックリードをしています。
hands-lab
ハンズラボは小売業特化型ITソリューション企業です。数十万に及ぶ膨大な商品マスタを扱ってきた豊富なノウハウで、お客様の現場に最適なシステムを提案・開発します。 エンジニア募集中
https://www.hands-lab.com/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away