🍎

Xcode26からPrivateHeadersのモジュールが読めなくなった

に公開

問題

Xcode Cloudのバージョンを26にしたら依存ライブラリの影響でCIが失敗した

Unable to find module dependency: 'ExternCModule'
import ExternCModule
       ^

.swiftinterfaceファイル内でimportしようとした際に、モジュールが見つからないというエラーが発生した、と言っている。

ライブラリのmodule.modulemapを開くとこうなっていた

framework module NormalModule {
  umbrella header "NormalModule.h"
  export *

  module * { export * }
}

module NormalModule.Swift {
  header "NormalModule-Swift.h"
  requires objc
}

framework module ExternCModule [extern_c]{
    umbrella header "ExternCModule.h"
    export *
    module * { export * }
}

NormalModule.hNormalModule-Swift.hHeadersフォルダにあるが、ExternCModulePrivateHeadersフォルダに入っていた。HeadersはPublic、PrivateHeadersはPrivateという切り分けをしているらしい。
https://developer.apple.com/documentation/xcode/customizing-the-build-phases-of-a-target

Public and private headers define the API that the target exposes to external clients. Xcode copies these header files into Headers and PrivateHeaders subfolders within the built product. Project headers contain the API that the target uses, but Xcode doesn’t expose the API to external clients.

原因

Xcode26の変更でExplicit Moduleが自動でONになるらしい

https://developer.apple.com/documentation/xcode-release-notes/xcode-26-release-notes

Starting from Xcode 26, Swift explicit modules will be the default mode for building all Swift targets.

多分だけど、Xcode 16 まではデフォルトで Swift InterfaceをImplicitに読み込んでいたため、importを評価するタイミングではすでにPrivateHeadersの内容が見えていたのだと思う。
一方でExplicit Moduleが有効になると、buildの前段階で依存モジュールをすべて明示的に解決するため、PrivateHeadersにあるモジュールは読み込めずにエラーになる。正直あまりよくわかってないけど、調べた感じこれが一番納得の行く理由だった。

解決

SWIFT_ENABLE_EXPLICIT_MODULES=NOにすると解決した人が何人かいた
https://forums.swift.org/t/xcode-26-unable-to-find-module-dependency/80516

僕の場合は治らなかったので、ライブラリのPrivateHeadersのファイルをすべてHeadersフォルダに入れ、NormalModule.hで読み込んだら一旦はエラーが消えた。

ただNormalModuleにはextern_cがついていない。またこのライブラリは問題なくなったが、次は他のライブラリで別の理由でbuildがコケるようになった。全部直していてもキリがないので、しばらくはXCode16を使い続けようという結論に至った。

Discussion