Skip to content

fix: decide a task again where the OS has no URLProtocol.task getter - #9

Merged
ryan-son merged 1 commit into
mainfrom
fix/task-getter-fallback
Oct 14, 2026
Merged

ryan-son merged 1 commit into
mainfrom
fix/task-getter-fallback

Conversation

@ryan-son

Copy link
Copy Markdown
Owner

Summary

A consuming app on iOS 16.4 (simulator, iPhone 14 Pro, built from feature/ios-16-support at 340dd4c) crashed on its first mocked request:

-[NectoMapLocalPlugin.MapLocalURLProtocol task]: unrecognized selector  (SIGABRT)
MapLocalURLProtocol.startLoading → _CF_forwarding_prep_0 → ___forwarding___

startLoading reads self.task to take back the decision canInit(with task:) made. The SDK declares URLProtocol.task from iOS 8, so this compiles, but on 16.4 the getter is missing at run time. 17.5 and later have it. Nothing between 16.4 and 17.5 was measured, so main, which supports iOS 17.0, may be affected too. That's why the fix goes to main and reaches feature/ios-16-support by the usual merge.

  • MapLocalURLProtocol.readsTask asks the running OS once: instancesRespond(to: #selector(getter: URLProtocol.task)). It isn't a #available boundary, because nobody knows the version where the getter appears.
  • Where the getter exists, nothing changes: a task answers with its first decision even when the configuration changes before it starts.
  • Where it doesn't, startLoading decides the request again with engine.decide(request). That fallback already existed for protocols created from a request. canInit(with task:) keeps no decision there, since nothing would take it back.

Cost where the getter is missing: a configuration change between canInit and startLoading changes the answer for that one task. That is the behaviour a request created without a task already had.

Changelog

The 0.2.0 section gets a line in #8 once this is merged, so this PR doesn't touch the changelog that #8 restructures.

Agent interface

No change. No operation, input, output, error or maplocal.state field changes. On an OS without the getter, the request log (maplocal.requests.*) still records each mocked request once.

Test Plan

  • Two tests, each run first against the code before the fix with only the switch added, and each failing there:
    • With readsTask false, a task whose rule switches its response between canInit and startLoading answers with the new one (500), not the stored one. Before the fix: 200.
    • With readsTask false, canInit keeps no decision for the task. Before the fix: one was kept.
  • The existing "a task answers with its first decision even when the configuration changes before it starts" still passes, so the contract holds where the getter exists.
  • script/ci exit 0: Swift 89 + 131 → 91 + 131, vitest 1143 unchanged.
  • iOS 16.4: not available on this machine (18.x, 26.x and 27.0 runtimes only). The app's verification session on the machine that has 16.4 checks it from feature/ios-16-support once this is merged in.
  • iOS 17.0–17.4: not measured.

On iOS 16.4 the first mocked request ended the app with SIGABRT:
-[MapLocalURLProtocol task]: unrecognized selector, from startLoading.
The SDK declares URLProtocol.task from iOS 8, so it compiles, but the
getter is missing at run time there (17.5 has it; nothing in between
was measured).

The protocol now asks the running OS once whether instances answer the
getter. Where they do, a task still answers with the decision canInit
made. Where they don't, startLoading decides the request again (the
engine's decision is a pure function of the request and configuration),
and canInit keeps no decision that nothing would take back.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016HvfREejSbdqHNbZ87WYdW
@ryan-son
ryan-son merged commit 93561e8 into main Oct 14, 2026
1 check passed
@ryan-son ryan-son mentioned this pull request Oct 14, 2026
6 tasks done
@ryan-son
ryan-son deleted the fix/task-getter-fallback branch October 14, 2026 02:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant