Background
WikiMessageExtensionTests exists with coverage for MessageBuilder, WikipediaService, HTTPClient, the DTOs, and Article — but the suite cannot currently run in CI. The PR #3 modernization branch was forced to drop test execution and run xcodebuild build only.
Why tests can't run today
iMessage extensions don't have a real launchable iOS host app. The WikiMessage target's product is Apple's MessagesApplicationStub (/Applications/Xcode_15.4.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Library/Application Support/MessagesApplicationStub), a placeholder binary whose only job is to embed the .appex. iOS's FrontBoard refuses to launch it, with:
Application "com.timfall.WikiMessage" is unknown to FrontBoard.
This means:
TEST_HOST = .../WikiMessage.app/WikiMessage → can't launch (FrontBoard rejects the stub)
TEST_HOST = .../WikiMessage MessagesExtension.appex/... → can't launch (.appex isn't a launchable bundle)
- No
TEST_HOST + BUNDLE_LOADER pointing at the .appex → linker resolves symbols but XCTest still has no host process to load the test bundle into
So the test bundle compiles but has nowhere to run.
Proposed fix: extract a shared framework
Create a WikiMessageCore framework (or Swift package) target containing the code that's worth testing — the parts that have no UI dependencies:
Data/Article.swift
Data/WikipediaService.swift
Data/DTOs/SummaryDTO.swift
Data/DTOs/SearchResponseDTO.swift
Messaging/MessageBuilder.swift (and probably MessageComposer.swift)
Networking/HTTPClient.swift
Networking/NetworkMonitor.swift
Both the extension and the test target link against this framework. The test target becomes a "logic test" — no TEST_HOST required, no host app launch, runs directly under xctest.
The SwiftUI views, MessagesViewController, AppModel, SearchModel, etc. stay in the extension target since they depend on UIKit/SwiftUI/Messages and aren't unit-testable in isolation anyway.
Steps
- Add a new framework target
WikiMessageCore to the Xcode project (iOS 16, Swift 5.7, same concurrency settings as the extension).
- Move the files listed above from
WikiMessage MessagesExtension/ into the framework target.
- Make types that need to be visible across the boundary
public (most are already internal, so this is mostly DTO fields, Article's init, WikipediaService's methods, MessageBuilder.build, HTTPClient's public surface).
- Update the extension target to depend on and embed
WikiMessageCore.
- Update the test target to:
- Depend on
WikiMessageCore
- Drop
BUNDLE_LOADER and TEST_HOST from build settings
- Remove the
WikiMessage target dependency
- Switch
@testable import WikiMessage_MessagesExtension → @testable import WikiMessageCore
- Re-enable the test step in
.github/workflows/ci.yml (revert the change in commit fd8e889):
Verification
xcodebuild test -scheme "WikiMessage MessagesExtension" -destination 'platform=iOS Simulator,name=iPhone 15,OS=latest' runs all tests successfully on macOS 14 + Xcode 15.4.
- All 4 test files (
SummaryDTOTests, SearchResponseDTOTests, WikipediaServiceTests, MessageBuilderTests) execute and pass.
- Extension still builds and the iMessage app behaves identically — the framework split is an internal refactor with no user-visible change.
Why not just add the source files to the test target?
Compiling each file into both the extension and the test target works but produces duplicate symbols, breaks @testable import, and doubles incremental compile times. A framework is the architecturally correct boundary and what Apple recommends for testing extension code.
Tracked from
PR #3 (modernization branch).
Background
WikiMessageExtensionTestsexists with coverage forMessageBuilder,WikipediaService,HTTPClient, the DTOs, andArticle— but the suite cannot currently run in CI. The PR #3 modernization branch was forced to drop test execution and runxcodebuild buildonly.Why tests can't run today
iMessage extensions don't have a real launchable iOS host app. The
WikiMessagetarget's product is Apple'sMessagesApplicationStub(/Applications/Xcode_15.4.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Library/Application Support/MessagesApplicationStub), a placeholder binary whose only job is to embed the.appex. iOS's FrontBoard refuses to launch it, with:This means:
TEST_HOST = .../WikiMessage.app/WikiMessage→ can't launch (FrontBoard rejects the stub)TEST_HOST = .../WikiMessage MessagesExtension.appex/...→ can't launch (.appexisn't a launchable bundle)TEST_HOST+BUNDLE_LOADERpointing at the.appex→ linker resolves symbols but XCTest still has no host process to load the test bundle intoSo the test bundle compiles but has nowhere to run.
Proposed fix: extract a shared framework
Create a
WikiMessageCoreframework (or Swift package) target containing the code that's worth testing — the parts that have no UI dependencies:Data/Article.swiftData/WikipediaService.swiftData/DTOs/SummaryDTO.swiftData/DTOs/SearchResponseDTO.swiftMessaging/MessageBuilder.swift(and probablyMessageComposer.swift)Networking/HTTPClient.swiftNetworking/NetworkMonitor.swiftBoth the extension and the test target link against this framework. The test target becomes a "logic test" — no
TEST_HOSTrequired, no host app launch, runs directly underxctest.The SwiftUI views,
MessagesViewController,AppModel,SearchModel, etc. stay in the extension target since they depend on UIKit/SwiftUI/Messages and aren't unit-testable in isolation anyway.Steps
WikiMessageCoreto the Xcode project (iOS 16, Swift 5.7, same concurrency settings as the extension).WikiMessage MessagesExtension/into the framework target.public(most are alreadyinternal, so this is mostly DTO fields,Article's init,WikipediaService's methods,MessageBuilder.build,HTTPClient's public surface).WikiMessageCore.WikiMessageCoreBUNDLE_LOADERandTEST_HOSTfrom build settingsWikiMessagetarget dependency@testable import WikiMessage_MessagesExtension→@testable import WikiMessageCore.github/workflows/ci.yml(revert the change in commitfd8e889):clean testVerification
xcodebuild test -scheme "WikiMessage MessagesExtension" -destination 'platform=iOS Simulator,name=iPhone 15,OS=latest'runs all tests successfully on macOS 14 + Xcode 15.4.SummaryDTOTests,SearchResponseDTOTests,WikipediaServiceTests,MessageBuilderTests) execute and pass.Why not just add the source files to the test target?
Compiling each file into both the extension and the test target works but produces duplicate symbols, breaks
@testable import, and doubles incremental compile times. A framework is the architecturally correct boundary and what Apple recommends for testing extension code.Tracked from
PR #3 (modernization branch).