PC パソコン

Swift「Cannot infer contextual base」の原因と解決方法

SwiftやSwiftUIでEnum Case、Static Member、OptionSetなどを先頭のドットを使って省略記法で指定したとき、Xcodeに次のようなErrorが表示されることがあります。

Cannot infer contextual base in reference to member 'center'

ほかにも:

Cannot infer contextual base in reference to member 'horizontal'

や:

Cannot infer contextual base in reference to member 'red'

など、末尾のMember名だけが変わったDiagnosticが表示されます。

このErrorは、.centerや.redのような省略記法を使っているものの、Swift Compilerが「どのTypeのcenterなのか」「どのTypeのredなのか」をContextから判断できない場合に発生します。

基本的な解決方法は、対象のTypeを明示する、VariableやParameterにType Annotationを付ける、または周囲の別のType Errorを先に修正することです。

  1. 「Cannot infer contextual base」とは
  2. 代表的な発生状況
  3. 主な原因
    1. 1. Enum Caseだけを書いている
    2. 2. Function ArgumentのExpected Typeが決まらない
    3. 3. SwiftUIのAlignmentでTypeが曖昧
    4. 4. ColorやShapeStyleのContextが不足している
    5. 5. OptionSetのTypeが不明
    6. 6. 空Arrayと省略Memberを組み合わせている
    7. 7. Generic Function内でType情報が不足している
    8. 8. ClosureのParameter Typeが不明
    9. 9. Optional Contextが不足している
    10. 10. 三項演算子の両Branchが曖昧
    11. 11. Function ReferenceやOverloadが多い
    12. 12. SwiftUI Modifierの別ArgumentにErrorがある
    13. 13. BindingとValueが間違っている
    14. 14. OptionalとNon-Optionalが一致していない
    15. 15. SDKやPackage UpdateでAPI Signatureが変わった
  4. まず確認すること
    1. 1. ErrorになっているMemberを完全なType名で書く
    2. 2. VariableにType Annotationを付ける
    3. 3. Errorの1行前や別Argumentを確認する
  5. 解決方法1:Enum Typeを明示する
  6. 解決方法2:Static MemberのType名を書く
  7. 解決方法3:Alignmentの種類を明示する
  8. 解決方法4:SwiftUI Modifier内で完全なType名を使う
  9. 解決方法5:ArrayへElement Typeを付ける
  10. 解決方法6:DictionaryのValue Typeを明示する
  11. 解決方法7:ClosureのResult Typeを明示する
  12. 解決方法8:Closure Return Typeを直接書く
  13. 解決方法9:三項演算子のResult Typeを付ける
  14. 解決方法10:Optional Typeを明示する
  15. 解決方法11:Function Parameter Typeを確認する
  16. 解決方法12:Overloadを明示的に区別する
  17. 解決方法13:Generic Result Typeを明示する
  18. 解決方法14:OptionSetのTypeを明示する
  19. 解決方法15:SwiftUIのColorを明示する
  20. 解決方法16:SwiftUIのAxisを明示する
  21. 解決方法17:SwiftUIのTextAlignmentを明示する
  22. 解決方法18:Binding Errorを先に直す
  23. 解決方法19:Optionalを先にUnwrapする
  24. 解決方法20:Generic FunctionへConcrete Type情報を与える
  25. 解決方法21:Function ReferenceのTypeを明示する
  26. 解決方法22:複雑なExpressionを分割する
  27. 解決方法23:SwiftUIのCustom View Argument Typeを確認する
  28. 解決方法24:SwiftUIのModifierを一度分割する
  29. 解決方法25:同じ名前のTypeをModule名で区別する
  30. 解決方法26:古いAPI Sampleを現在のSignatureへ合わせる
  31. 解決方法27:Cannot infer contextual baseより前のErrorを修正する
  32. 解決方法28:Autocompleteで完全なMember名を確認する
  33. 解決方法29:TypealiasやGeneric Aliasを確認する
  34. 解決方法30:省略記法を使わず完全修飾名で切り分ける
  35. SwiftUIでよくある例
    1. frameのalignment
    2. multilineTextAlignment
    3. HStackのalignment
    4. VStackのalignment
  36. Enumで発生する場合
  37. mapで発生する場合
  38. switchで発生する場合
  39. Resultで発生する場合
  40. 「Typeを明示しても直らない」場合
  41. Clean Build Folderで直るか
  42. DerivedData削除は必要か
  43. やってはいけない対処
  44. 最短で確認するチェックリスト
  45. まとめ

「Cannot infer contextual base」とは

Swiftでは、Expected Typeが分かっている場合にEnum CaseやStatic MemberのType名を省略できます。

例えば:

enum Direction { case left case right }

let direction: Direction = .left

directionのTypeがDirectionだと分かっているため、Compilerは:

.left

を:

Direction.left

として解釈できます。

ところが:

let direction = .left

だけでは、.leftがどのTypeに属しているのか判断できません。

そのため、Contextによっては:

Cannot infer contextual base in reference to member 'left'

のようなErrorになります。

ポイント:「contextual base」とは、簡単に言えば「このMemberが属しているはずのType」です。CompilerがそのTypeをContextから推論できないと、このErrorが発生します。

代表的な発生状況

  • Enum Caseを.caseNameだけで代入した
  • VariableのType Annotationがない
  • Generic Functionへ省略形のEnum Caseを渡した
  • SwiftUI ModifierでExpected Typeを特定できない
  • Color、Alignment、AxisなどのStatic Memberを省略している
  • OptionSetのMemberをContextなしで使用した
  • OptionalやGenericのTypeが曖昧になっている
  • Function Overloadが複数ありParameter Typeを決められない
  • Closure内部でType情報が失われている
  • 前のCompiler ErrorによってExpected Typeを推論できなくなっている

主な原因

1. Enum Caseだけを書いている

問題:

let direction = .left

Compilerには.leftがどのEnumのCaseなのか分かりません。

修正:

let direction: Direction = .left

または:

let direction = Direction.left

2. Function ArgumentのExpected Typeが決まらない

例えば複数のOverloadがある場合です。

func configure( alignment: HorizontalAlignment ) { }

func configure(
alignment: VerticalAlignment
) {
}

次のようなCallでは、Member名によってはどちらを使うべきか判断できないことがあります。

configure( alignment: .center )

HorizontalAlignment.centerとVerticalAlignment.centerの両方がCandidateになるためです。

3. SwiftUIのAlignmentでTypeが曖昧

SwiftUIには:

  • Alignment
  • HorizontalAlignment
  • VerticalAlignment

など、似たMemberを持つTypeがあります。

.centerだけでは、周囲のAPIが曖昧な場合にTypeを推論できないことがあります。

4. ColorやShapeStyleのContextが不足している

例えば:

let color = .red

だけでは、CompilerがどのTypeのredなのか判断できないことがあります。

修正:

let color: Color = .red

または:

let color = Color.red

5. OptionSetのTypeが不明

例えば:

let options = [.allowAirPlay, .mixWithOthers]

Contextがない場合、どのOptionSetのMemberなのかCompilerが判断できないことがあります。

6. 空Arrayと省略Memberを組み合わせている

let values = [.one, .two]

Element Typeを推論できない場合、それぞれのMemberのBase Typeも分かりません。

7. Generic Function内でType情報が不足している

Generic ParameterのConcrete Typeが確定していないと、Static MemberやEnum Caseを省略した記述が成立しない場合があります。

8. ClosureのParameter Typeが不明

items.map { item in .selected }

Result Typeが決まっていない場合、.selectedのTypeも推論できません。

9. Optional Contextが不足している

例えば:

let state = condition ? .active : nil

Expected Optional Typeがないと、.activeのBase Typeを決められない場合があります。

10. 三項演算子の両Branchが曖昧

let alignment = isHorizontal ? .leading : .trailing

Variable Typeがない場合、Memberの所属Typeを決められないことがあります。

11. Function ReferenceやOverloadが多い

Call SiteでExpected Parameter Typeを1つへ絞れないと、引数の省略Memberも推論できません。

12. SwiftUI Modifierの別ArgumentにErrorがある

例えばModifierの別Parameterが不正なためにInitializerやMethod Candidateを選べず、その結果として.centerなどへ「Cannot infer contextual base」が表示されることがあります。

13. BindingとValueが間違っている

SwiftUIで正しいInitializerを選べないと、そのInitializerに依存していたEnumやStatic MemberのExpected Typeも失われます。

14. OptionalとNon-Optionalが一致していない

本来のOverloadがCandidateから外れ、結果的に省略MemberのTypeが推論できなくなることがあります。

15. SDKやPackage UpdateでAPI Signatureが変わった

以前はExpected Typeが明確だったParameterが削除・変更されると、古いCodeの省略Memberだけが残り、このErrorが出る場合があります。

まず確認すること

1. ErrorになっているMemberを完全なType名で書く

例えば:

.center

でErrorなら、意図しているTypeを確認して:

Alignment.center

または:

HorizontalAlignment.center

などにします。

これで直るなら、原因はContext不足です。

2. VariableにType Annotationを付ける

let alignment: HorizontalAlignment = .center

Expected Typeが確定します。

3. Errorの1行前や別Argumentを確認する

Compilerが本来使うべきFunctionやInitializerを特定できていない可能性があります。

重要:「Cannot infer contextual base」が表示されたMember自体が間違っているとは限りません。別のArgument Type、Argument Label、Bindingなどが原因でContextが失われていることがあります。

解決方法1:Enum Typeを明示する

修正前:

let state = .active

修正後:

let state: UserState = .active

または:

let state = UserState.active

解決方法2:Static MemberのType名を書く

SwiftUIで:

let color = Color.red

のようにBase Typeを直接書きます。

解決方法3:Alignmentの種類を明示する

例えば:

let alignment: HorizontalAlignment = .center

または:

let alignment = HorizontalAlignment.center

解決方法4:SwiftUI Modifier内で完全なType名を使う

例えば意図したTypeがAlignmentなら:

.frame( maxWidth: .infinity, alignment: Alignment.center )

と書くことで曖昧さを取り除けます。

解決方法5:ArrayへElement Typeを付ける

問題:

let states = [ .active, .inactive ]

修正:

let states: [UserState] = [ .active, .inactive ]

解決方法6:DictionaryのValue Typeを明示する

let states: [String: UserState] = [ "user1": .active, "user2": .inactive ]

解決方法7:ClosureのResult Typeを明示する

例えば:

let states: [UserState] = users.map { user in user.isActive ? .active : .inactive }

Result ArrayのTypeを指定すると、各Enum CaseのBase Typeが確定します。

解決方法8:Closure Return Typeを直接書く

let states = users.map { (user: User) -> UserState in
    user.isActive
        ? .active
        : .inactive
}

解決方法9:三項演算子のResult Typeを付ける

let alignment: HorizontalAlignment = isCentered ? .center : .leading

解決方法10:Optional Typeを明示する

let state: UserState? = condition ? .active : nil

これで.activeがUserStateであることをCompilerが判断できます。

解決方法11:Function Parameter Typeを確認する

例えば:

func setState( _ state: UserState ) { }

なら:

setState(.active)

で問題ありません。

ここでErrorになる場合、別Overloadや別ErrorによってExpected Typeを決められていない可能性があります。

解決方法12:Overloadを明示的に区別する

例えば:

func configure( alignment: HorizontalAlignment ) { }

func configure(
alignment: VerticalAlignment
) {
}

のように同名Overloadがあり、Member名も重なるなら:

configure( alignment: HorizontalAlignment.center )

とします。

解決方法13:Generic Result Typeを明示する

let value: Result<User, AppError> = .success(user)

.successがどのResult Typeなのか明確になります。

解決方法14:OptionSetのTypeを明示する

例えば:

let options: AVAudioSession.CategoryOptions = [ .mixWithOthers, .allowAirPlay ]

のようにOptionSet Typeを指定すると、各MemberのBaseが確定します。

解決方法15:SwiftUIのColorを明示する

let backgroundColor: Color = .red

または:

let backgroundColor = Color.red

解決方法16:SwiftUIのAxisを明示する

例えば:

let axis: Axis = .horizontal

複数選択のOptionSetならAPIが要求する具体的なTypeを確認してください。

解決方法17:SwiftUIのTextAlignmentを明示する

let alignment: TextAlignment = .center

HorizontalAlignment.centerやAlignment.centerとは別Typeなので、用途に対応したTypeを選びます。

解決方法18:Binding Errorを先に直す

例えば:

@State private var text = ""

TextField(
"Name",
text: $text
)

Bindingを要求するParameterへ通常のStringを渡していると、Initializer候補自体を決められず、周辺MemberにContext Errorが出ることがあります。

解決方法19:Optionalを先にUnwrapする

if let user { UserView( user: user ) }

OptionalによってOverload選択が複雑になっている場合、Concrete Typeへしてから渡します。

解決方法20:Generic FunctionへConcrete Type情報を与える

例えば:

let result: Result<User, AppError> = makeResult()

Return TypeからGeneric Parameterを確定させます。

解決方法21:Function ReferenceのTypeを明示する

let handler: (UserState) -> Void = setState

OverloadされたFunction Referenceを使う場合にもType Contextを与えると解決しやすくなります。

解決方法22:複雑なExpressionを分割する

例えば:

let state = users .filter { $0.isActive } .first .map { _ in .active }

でType推論が難しい場合:

let activeUser: User? = users.first { $0.isActive }

let state: UserState? =
activeUser.map { _ in
.active
}

のようにType Boundaryを作ります。

解決方法23:SwiftUIのCustom View Argument Typeを確認する

例えば:

struct StatusView: View { let state: UserState
var body: some View {
    Text(state.title)
}

}

Call Site:

StatusView( state: .active )

ならExpected TypeがUserStateなので、省略Caseを利用できます。

これでErrorになる場合はCustom Initializerや別Argumentも確認してください。

解決方法24:SwiftUIのModifierを一度分割する

長いModifier Chainで発生する場合、問題部分をVariableやSub Viewに分けます。

let alignment: Alignment = .center

Text("Hello")
.frame(
maxWidth: .infinity,
alignment: alignment
)

解決方法25:同じ名前のTypeをModule名で区別する

複数Moduleに同名Typeがある場合:

FrameworkA.Status.active

のようにModuleまで明示するとContextを限定できます。

解決方法26:古いAPI Sampleを現在のSignatureへ合わせる

SDKやPackage Update後にErrorが発生した場合は、XcodeのAutocompleteやJump to Definitionで現在のParameter Typeを確認してください。

以前のAPIでは:

alignment: .center

で利用できていても、現在は別TypeのParameterになっている可能性があります。

解決方法27:Cannot infer contextual baseより前のErrorを修正する

同じExpression内に:

Cannot convert value of type ... Ambiguous use of ... Missing argument for parameter ... Generic parameter could not be inferred

などがある場合は、それらを先に修正してください。

Expected Typeが復元されると、「Cannot infer contextual base」も消えることがあります。

解決方法28:Autocompleteで完全なMember名を確認する

例えば.centerを使いたい箇所で、一度:

HorizontalAlignment.

や:

Alignment.

まで入力してAutocomplete候補を確認します。

これにより、自分が想定しているTypeにそのMemberが本当に存在するか確認できます。

解決方法29:TypealiasやGeneric Aliasを確認する

複雑なTypealiasによって実際のExpected Typeが分かりにくい場合があります。

一時的にConcrete Typeへ展開すると原因を特定しやすくなります。

解決方法30:省略記法を使わず完全修飾名で切り分ける

原因が分からない場合は、一度すべてのDot Syntaxを完全なType名へ置き換えます。

例えば:

let state = UserState.active

let alignment =
HorizontalAlignment.center

let color =
Color.red

これでCompileできれば、Type Context不足が原因だったと判断できます。

SwiftUIでよくある例

frameのalignment

正しい例:

Text("Hello") .frame( maxWidth: .infinity, alignment: .center )

通常はframeのParameter Typeから.centerを推論できます。

しかし別ArgumentにErrorがある場合などは:

Text("Hello") .frame( maxWidth: .infinity, alignment: Alignment.center )

と完全なTypeを明示して切り分けます。

multilineTextAlignment

Text("Hello") .multilineTextAlignment( .center )

ここで要求されるのはTextAlignmentです。

明示するなら:

Text("Hello") .multilineTextAlignment( TextAlignment.center )

HStackのalignment

HStack( alignment: .center ) { Text("A") Text("B") }

HStackのalignmentはVerticalAlignmentです。

明示するなら:

HStack( alignment: VerticalAlignment.center ) { Text("A") Text("B") }

VStackのalignment

VStack( alignment: .leading ) { Text("A") Text("B") }

VStackのalignmentはHorizontalAlignmentです。

明示するなら:

VStack( alignment: HorizontalAlignment.leading ) { Text("A") Text("B") }

注意:SwiftUIには似た名前のAlignment Typeが複数あります。.centerだけを見て判断せず、そのAPIが要求する具体的なTypeを確認してください。

Enumで発生する場合

例えば:

enum LoadingState { case idle case loading case completed }

これはErrorになり得ます。

let state = .loading

修正:

let state: LoadingState = .loading

または:

let state = LoadingState.loading

mapで発生する場合

問題:

let states = users.map { user in user.isActive ? .active : .inactive }

修正:

let states: [UserState] = users.map { user in user.isActive ? .active : .inactive }

switchで発生する場合

通常、switch対象のTypeが分かっていればCaseを省略記法で書けます。

switch state { case .active: print("Active")

case .inactive:
print("Inactive")
}

もしCase側でContext Errorが出るなら、state自体のTypeが正しく確定しているか確認してください。

Resultで発生する場合

問題:

let result = .success(user)

ではFailure Typeが分からない場合があります。

修正:

let result: Result<User, AppError> = .success(user)

「Typeを明示しても直らない」場合

その場合は次を確認してください。

  • 指定したTypeにそのMemberが本当に存在するか
  • Argument Labelが正しいか
  • 別ArgumentのTypeが正しいか
  • Overload Candidateが複数ないか
  • OptionalとNon-Optionalが一致しているか
  • SwiftUIでBindingとValueを間違えていないか
  • Generic Parameterが推論できているか
  • 古いAPI Signatureを使っていないか

Clean Build Folderで直るか

通常は直りません。

このErrorは、Source Codeから必要なType Contextを得られないことが主な原因です。

補助的に:

Product → Clean Build Folder

を実行できますが、.centerや.activeのBase Typeが不明なままなら再びErrorになります。

DerivedData削除は必要か

IndexやBuild Cacheが不自然な場合は:

rm -rf ~/Library/Developer/Xcode/DerivedData/*

を補助的に試すことはできます。

ただし、本質的な解決方法はTypeを明示するか、周囲のType Errorを修正することです。

注意:このErrorでは、DerivedData削除を繰り返すより、Expected TypeをCompilerが判断できるCodeになっているか確認してください。

やってはいけない対処

Errorを消すためだけに、意図を確認せず適当なTypeへCastするのは避けてください。

例えば.centerには、Contextによって:

  • Alignment.center
  • HorizontalAlignment.center
  • VerticalAlignment.center
  • TextAlignment.center

など異なる意味があります。

どのAPIへ渡す値なのか確認してから、正しいTypeを指定してください。

最短で確認するチェックリスト

  1. Errorになっている省略Memberの完全なType名を書く
  2. VariableへType Annotationを付ける
  3. Enum CaseのTypeを明示する
  4. SwiftUIのAlignment Typeを確認する
  5. Color、Axis、TextAlignmentなどのTypeを確認する
  6. Array・DictionaryのElement Typeを明示する
  7. ClosureのReturn Typeを明示する
  8. Optional Typeを明示する
  9. Generic Result Typeを明示する
  10. Function Overloadを確認する
  11. Bindingと通常Valueを確認する
  12. OptionalとNon-Optionalを確認する
  13. 別ArgumentのType Errorを確認する
  14. 現在のAPI Signatureを確認する
  15. より前に表示されているCompiler Errorを先に修正する

まとめ

Swiftの「Cannot infer contextual base in reference to member」は、.active、.center、.redなどの省略Memberが、どのTypeに属するものなのかCompilerがContextから判断できない場合に発生します。

代表的な原因は、VariableのType Annotation不足、Enum CaseのContext不足、Function Overload、Generic Type、Optional、Closure、SwiftUIのAlignmentやBindingなどです。

最も簡単な解決方法は、UserState.active、Alignment.center、Color.redのようにBase Typeを明示することです。

また、let state: UserState = .activeのようにVariable側へExpected Typeを与える方法も有効です。

SwiftUIではAlignment、HorizontalAlignment、VerticalAlignment、TextAlignmentなど似たTypeがあるため、そのModifierやView Initializerが要求するTypeを確認してください。

Typeを明示しても直らない場合は、省略Memberそのものではなく、Argument Label、別ArgumentのType、Binding、Optional、Generic Parameterなどが原因でExpected Typeを失っていないか確認することが重要です。