【Swift】「Extra argument in call」エラーを解消する
初めに
今回は SwiftUI で VisionOS を触っている途中で発生した 「Extra argument in call」 というエラーを解消する方法を共有したいと思います。
記事の対象者
- Swift, SwiftUI 学習者
- 上記エラーを解決したい方
目的
今回は以下のように実装中に発生した「Extra argument in call」エラーを解決します。
環境
- Xcode 15.1 beta
- MacBook Air M1, 2020
- visionOS Simulator
エラー原因
「Extra argument in call」 は 「呼び出しの引数が余分です」の意味のようです。
エラーが発生したコードは以下のとおりです。
import SwiftUI
@main
struct InstructionManualApp: App {
@ObservedObject private var model = AreaViewModel()
var body: some Scene {
WindowGroup(id: model.contentAreaId) {
ContentView()
}
WindowGroup(id: model.shelfContentAreaId) {
ShelfContentView()
}
WindowGroup(id: model.shelfRealityAreaId) {
ShelfRealityArea()
}
.defaultSize(CGSize(width: 800, height: 1000))
WindowGroup(id: model.equipmentRealityAreaId) {
EquipmentRealityArea()
}
.defaultSize(CGSize(width: 700, height: 700))
WindowGroup(id: model.videoAreaId) {
VideoArea(videoSourcePath: "ScrewDetail")
}
.defaultSize(CGSize(width: 700, height: 700))
WindowGroup(id: model.completedAreaId) {
CompletedRealityArea()
}
.defaultSize(CGSize(width: 800, height: 900))
WindowGroup(id: model.robotCleanerContentAreaId) {
RobotCleanerContentView()
}
WindowGroup(id: model.robotCleanerNumId) {
RobotCleanerNumRealityArea()
}
WindowGroup(id: model.robotCleanerMaintenanceId) {
RobotCleanerMaintenanceArea()
}
WindowGroup(id: model.robotCleanerMaintenanceTermTableAreaId) {
RobotCleanerMaintenanceTermTableArea()
}
WindowGroup(id: model.robotCleanerMaintenanceDustBoxRealityAreaId) {
RobotCleanerMaintenanceDustBoxRealityArea()
}
}
}
この場合において「呼び出しの引数が余分」とは、「Scene
で呼び出している引数の数が多すぎますよ」ということかと思います。
上記のコードでは、Scene
の引数になっている WindowGroup
は10個あります。
試しに WindowGroup
を1つ削除してみると、エラーが解消されました。
したがって、このエラーは記述の通り、1つの Scene
の引数が多すぎることが原因であるとわかります。
エラーを調べているうちにこちらの記事に辿り着きました。
この記事にもある通り、Scene
を遡って調べてみました。
以下のように Scene
には body
があり、body
は SceneBuilder
として定義されていることがわかります。
今回は Scene
に中括弧で WindowGroup
は全て body
に含まれていることがわかります。
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public protocol Scene {
/// The type of scene that represents the body of this scene.
///
/// When you create a custom scene, Swift infers this type from your
/// implementation of the required ``SwiftUI/Scene/body-swift.property``
/// property.
associatedtype Body : Scene
/// The content and behavior of the scene.
///
/// For any scene that you create, provide a computed `body` property that
/// defines the scene as a composition of other scenes. You can assemble a
/// scene from built-in scenes that SwiftUI provides, as well as other
/// scenes that you've defined.
///
/// Swift infers the scene's ``SwiftUI/Scene/Body-swift.associatedtype``
/// associated type based on the contents of the `body` property.
@SceneBuilder @MainActor var body: Self.Body { get }
}
body
に含まれている SceneBuilder
をさらに深ぼってみます。
SceneBuilder
の中には以下の記述があります。
SceneBuilder
では C0 ~ C9
まで Scene
を10個のみ受け付けています。しかし、今回は10以上を渡してしまったため、「引数が余分である」という判定になったようです。
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
extension SceneBuilder {
public static func buildBlock<C0, C1, C2, C3, C4, C5, C6, C7, C8, C9>
(_ c0: C0, _ c1: C1, _ c2: C2, _ c3: C3, _ c4: C4, _ c5: C5, _ c6: C6,
_ c7: C7, _ c8: C8, _ c9: C9) -> some Scene where C0 : Scene, C1 : Scene,
C2 : Scene, C3 : Scene, C4 : Scene, C5 : Scene, C6 : Scene, C7 : Scene,
C8 : Scene, C9 : Scene
}
解決策
改善後のコードは以下のようになります。
import SwiftUI
@main
struct InstructionManualApp: App {
@ObservedObject private var model = AreaViewModel()
var body: some Scene {
WindowGroup(id: model.contentAreaId) {
ContentView()
}
Group { // Group でまとめる
WindowGroup(id: model.shelfContentAreaId) {
ShelfContentView()
}
WindowGroup(id: model.shelfRealityAreaId) {
ShelfRealityArea()
}
.defaultSize(CGSize(width: 800, height: 1000))
WindowGroup(id: model.equipmentRealityAreaId) {
EquipmentRealityArea()
}
.defaultSize(CGSize(width: 700, height: 700))
WindowGroup(id: model.videoAreaId) {
VideoArea(videoSourcePath: "ScrewDetail")
}
.defaultSize(CGSize(width: 700, height: 700))
WindowGroup(id: model.completedAreaId) {
CompletedRealityArea()
}
.defaultSize(CGSize(width: 800, height: 900))
}
Group { // Group でまとめる
WindowGroup(id: model.robotCleanerContentAreaId) {
RobotCleanerContentView()
}
WindowGroup(id: model.robotCleanerNumId) {
RobotCleanerNumRealityArea()
}
WindowGroup(id: model.robotCleanerMaintenanceId) {
RobotCleanerMaintenanceArea()
}
WindowGroup(id: model.robotCleanerMaintenanceTermTableAreaId) {
RobotCleanerMaintenanceTermTableArea()
}
WindowGroup(id: model.robotCleanerMaintenanceDustBoxRealityAreaId) {
RobotCleanerMaintenanceDustBoxRealityArea()
}
}
ImmersiveSpace(id: model.immersiveAreaId) {
ImmersiveView()
}
.immersionStyle(selection: .constant(.full), in: .full)
}
}
Group
でそれぞれ関連性の高い WindowGroup
をまとめることで Scene
に渡す引数を減らすことができ、エラーが解消されます。
今回調べてみたところ、先述の記事にあった VStack
や HStack
、View
などは ViewBuilder
として指定されており、ViewBuilder
も引数に10以上の要素を指定できないようになっています。
今のところ、Flutterでは今回のエラーのような、パフォーマンスの低下を防ぐためのエラーは出たことがなかったかと思うので、よく考えられているなと感じました。
Group
としてまとめることで、可読性も上がったかと思います。
まとめ
最後まで読んでいただいてありがとうございました。
誤っている点や他の実装方法等あればご指摘いただけると幸いです。
参考
Discussion