Skip to content

💚 app: raise ios deployment targets - #1429

Draft
cruzdanilo wants to merge 1 commit into
mainfrom
claude/ios-xcode27-deployment-target-nq4io8
Draft

cruzdanilo wants to merge 1 commit into
mainfrom
claude/ios-xcode27-deployment-target-nq4io8

Conversation

@cruzdanilo

@cruzdanilo cruzdanilo commented Oct 18, 2026 •

Copy link
Copy Markdown
Member

Requested by danilo · project thread

before: the first fresh ios build on eas's latest image (xcode 27.1), e2e:local on the @exactly/mobile@1.2.36 tag, failed in xcodebuild. xcode 27 rejects any target below ios 15.0, and five were below it: the resource bundles of sdwebimage (9.0), reachabilityswift (12.0), rnsvg's filters (12.4) and asyncstorage (13.4), and the onesignal notification service extension (11.0). every other ios build since the image change reused a binary built on the old image, so the next native change or production ios build would hit it too. the last production and beta ios builds (10-14, 10-16) ran on the old image.

after: those pod resource bundles and the extension target ios 15.1, like the app.

how:

  • react native's react_native_post_install raises each pod to its 15.1 minimum, but only the pod's main target, not its resource bundles. patches/react-native.patch backports the upstream fix verbatim (Fix iOS deployment targets for CocoaPods resource bundles react/react-native#58687). it is on react native's main but in no release yet, so the patch goes away with the upgrade that ships it.
  • onesignal-expo-plugin 2.4.0 adds the extension with a hardcoded 11.0 default (2.7.2 moved it to 15.0). its own iPhoneDeploymentTarget option sets 15.1.
  • fingerprints change (e2e:local ios da028c61 to 3334b138, android b26eff2f to 97d6e49f), so the next release needs new store builds.
  • empty changeset.

evidence: e2e:local run 01a1505d on the same tree, building both apps fresh (in progress).


Generated by Claude Code

Summary by CodeRabbit

  • Platform Updates
    • iOS deployment-target settings are now applied consistently across native components, with OneSignal configured for iOS 15.1.
    • Native components use the configured deployment target when it meets the minimum supported version, or the minimum when it does not.

@changeset-bot

changeset-bot Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 42e5595

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 0 packages

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cruzdanilo cruzdanilo self-assigned this Oct 18, 2026
@coderabbitai

coderabbitai Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 15 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: d61481af-41c8-42ba-8b06-87f2969d1891
📥 Commits

Reviewing files that changed from the base of the PR and between 0e608c9 and 42e5595.

⛔ Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing: 4
  • .changeset/sturdy-pods-align.md
  • app.config.ts
  • package.json
  • patches/react-native.patch

Walkthrough

The OneSignal iOS deployment target is set to 15.1. A React Native patch applies the minimum supported deployment target to pod and resource-bundle targets. The package configuration registers the patch, and an empty changeset file is added.

Changes

iOS Deployment Target Alignment

Layer / File(s) Summary
Configure and apply iOS deployment targets
app.config.ts, package.json, patches/react-native.patch, .changeset/sturdy-pods-align.md
The OneSignal plugin target is set to 15.1. The React Native patch is registered and applies the minimum supported version to pod and resource-bundle targets. An empty changeset file is added.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: dieguezguille

Merge Risk: 🟠 High · up to 0e608

The iOS configuration change requires a new production build, but the locked router code fails compilation at the current iOS 15.1 target. The next iOS release build is blocked until the router is fixed or the deployment target is raised.

Pre-merge checks: Passed 4
Check name Status Explanation
Description Check Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check Passed The title clearly and concisely describes the primary change: raising iOS deployment targets for the app and related targets.
Linked Issues check Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR
✨ Simplify code
  • Commit to this branch
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-18T19:15:29.190869Z 37003e9 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@cruzdanilo
cruzdanilo marked this pull request as draft October 18, 2026 19:19

Copy link
Copy Markdown
Member Author

e2e:local / ios_build fails here as expected, and this pr is parked as a draft on purpose.

this commit clears the deployment-target errors. the next xcode 27.1 error is in expo-router 55: 'subtitle' is only available in iOS 16.0 or newer (LinkPreviewNativeActionView.swift:141, build b61d1692). expo-router 55.0.18 and expo's main branch have the same line, so no fix exists to port yet.

not fixing it now: every xcode 27.1 fix changes the expo fingerprint, and the fingerprint has to stay frozen so ota keeps working. this waits for the next fingerprint-breaking upgrade, which needs an expo-router fix or a newer sdk on top of this commit.


Generated by Claude Code

@cruzdanilo
cruzdanilo force-pushed the claude/ios-xcode27-deployment-target-nq4io8 branch from 37003e9 to 0e608c9 Compare October 18, 2026 19:29
Base automatically changed from test-fixes to main October 18, 2026 19:53

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: e176d817-203e-42d6-9f60-081e509c5a94
📥 Commits

Reviewing files that changed from the base of the PR and between be91245 and 0e608c9.

⛔ Files ignored due to path filters: 1
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing: 4
  • .changeset/sturdy-pods-align.md
  • app.config.ts
  • package.json
  • patches/react-native.patch

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.

Comment thread app.config.ts
"onesignal-expo-plugin",
{
mode: env.NODE_ENV === "production" ? "production" : "development",
iPhoneDeploymentTarget: "15.1",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

git diff --no-ext-diff --unified=20 be91245dcdc099be97f7aa9b891a4087ac742d6f 0e608c93dea3f966e29c0e50e419e41e6354f707 -- app.config.ts
rg -n 'ios_build|Xcode|image|fingerprint|expo-router|subtitle' .eas .github app.config.ts package.json

Repository: exactly/exa

Length of output: 4565


🏁 Script executed:

set -u
printf '%s\n' '--- changed files ---'
git diff --name-status be91245dcdc099be97f7aa9b891a4087ac742d6f 0e608c93dea3f966e29c0e50e419e41e6354f707
printf '%s\n' '--- production workflow ---'
nl -ba .eas/workflows/production.yaml
printf '%s\n' '--- local workflow ios section ---'
nl -ba .eas/workflows/local.yaml | sed -n '100,165p'
printf '%s\n' '--- dependency declarations and lockfile matches ---'
rg -n -F --glob 'package.json' -e '"expo-router"' -e '"expo"' -e '"expo-build-properties"' -e '"onesignal-expo-plugin"'
rg -n -F --glob '*lock*' --glob 'yarn.lock' --glob 'package-lock.json' --glob 'pnpm-lock.yaml' -e 'expo-router@' -e 'expo-router:' -e 'onesignal-expo-plugin'
printf '%s\n' '--- relevant source and patches ---'
rg -n -F --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' -e 'LinkPreviewNativeActionView' -e 'subtitle' -e 'iPhoneDeploymentTarget' -e 'react-native.patch' .
printf '%s\n' '--- app config and package scripts ---'
nl -ba app.config.ts | sed -n '70,165p'
nl -ba package.json | sed -n '1,180p'
printf '%s\n' '--- package manager files ---'
find . -maxdepth 2 -type f \( -name 'yarn.lock' -o -name 'package-lock.json' -o -name 'pnpm-lock.yaml' -o -name 'bun.lockb' -o -name 'bun.lock' \) -print

Repository: exactly/exa

Length of output: 41021


🏁 Script executed:

set -u
printf '%s\n' '--- EAS build profiles and image configuration ---'
nl -ba eas.json
printf '%s\n' '--- all build/profile/image references ---'
rg -n -F --glob '.eas/**' --glob 'eas.json' --glob 'package.json' -e 'e2e:production' -e 'ios' -e 'image' -e 'resourceClass' -e 'distribution' -e 'developmentClient'
printf '%s\n' '--- full PR diff for mobile build inputs ---'
git diff --no-ext-diff --unified=12 be91245dcdc099be97f7aa9b891a4087ac742d6f 0e608c93dea3f966e29c0e50e419e41e6354f707 -- package.json pnpm-lock.yaml patches/react-native.patch .changeset/sturdy-pods-align.md
printf '%s\n' '--- exact locked package blocks ---'
sed -n '9660,9695p' pnpm-lock.yaml
sed -n '12435,12465p' pnpm-lock.yaml
printf '%s\n' '--- repository references to reported build/error ---'
rg -n -F --hidden --glob '!node_modules/**' --glob '!dist/**' --glob '!build/**' -e 'b61d1692' -e 'LinkPreviewNativeActionView.swift' -e 'subtitle.*iOS 16' -e 'Xcode 27.1' -e 'expo-router 55' -e 'fingerprint' .

Repository: exactly/exa

Length of output: 45647


🌐 Web query:

official expo-router 55.0.8 LinkPreviewNativeActionView.swift subtitle iOS 16

💡 Result:

In the Expo **SDK 55** source for `LinkPreviewNativeActionView.swift`, both `menuAction.subtitle = subtitle` and `baseUiAction.subtitle = subtitle` are **outside** an `if #available(iOS 16.0, *)` check. The nearby availability checks guard other properties, not these subtitle assignments. That supports the concern that this code may fail to compile when the deployment target is below iOS 16. ([github.com](https://github.com/expo/expo/blob/sdk-55/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift))

**Version caveat:** I verified the `sdk-55` branch, not the exact `expo-router@55.0.8` published package, so I can’t confirm those lines are identical in that patch release. The npm listing confirms `55.0.8` exists. ([github.com](https://github.com/expo/expo/blob/sdk-55/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift))

Citations:

- 1: https://github.com/expo/expo/blob/sdk-55/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift
- 2: https://github.com/expo/expo/blob/sdk-55/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift

🏁 Script executed:

python3 - <<'PY'
import io, json, tarfile, urllib.request
urls = [
    "https://registry.npmjs.org/expo-router/55.0.8",
    "https://registry.npmjs.org/react-native/0.83.4",
]
for url in urls:
    print("---", url, "---")
    with urllib.request.urlopen(url, timeout=20) as response:
        data = json.load(response)
    print("version:", data.get("version"))
    print("dist.tarball:", data.get("dist", {}).get("tarball"))
    if data.get("name") == "expo-router":
        tarball = data["dist"]["tarball"]
        with urllib.request.urlopen(tarball, timeout=20) as response:
            archive = tarfile.open(fileobj=io.BytesIO(response.read()), mode="r:gz")
        for member in archive.getmembers():
            if member.name.endswith("ios/LinkPreview/LinkPreviewNativeActionView.swift"):
                print("SOURCE", member.name)
                text = archive.extractfile(member).read().decode()
                for n, line in enumerate(text.splitlines(), 1):
                    if "subtitle" in line or "available" in line or "menuAction" in line or "baseUiAction" in line:
                        print(f"{n}: {line}")
PY
printf '%s\n' '--- fingerprint configuration ---'
nl -ba fingerprint.config.cjs
printf '%s\n' '--- base/head native-target inputs ---'
git show be91245dcdc099be97f7aa9b891a4087ac742d6f:app.config.ts | nl -ba | sed -n '95,115p'
git show be91245dcdc099be97f7aa9b891a4087ac742d6f:package.json | nl -ba | sed -n '245,262p'
git show be91245dcdc099be97f7aa9b891a4087ac742d6f:pnpm-lock.yaml | rg -n -F -m 1 -e 'react-native@0.83.4:' -e 'expo-router@55.0.8:' -e 'react-native:' -e 'expo-router:'

Repository: exactly/exa

Length of output: 3997


🌐 Web query:

Expo expo-router LinkPreviewNativeActionView subtitle iOS 16 fix issue

💡 Result:

If you’re seeing **`'subtitle' is only available in iOS 16.0 or newer`** in `LinkPreviewNativeActionView.swift`, the code assigns `subtitle` to both `UIMenu` and `UIAction` without an iOS availability check. ([raw.githubusercontent.com](https://raw.githubusercontent.com/expo/expo/main/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift))

**Workaround:** set your app’s iOS deployment target to **16.0 or later** (and ensure the CocoaPods targets are compatible), then regenerate/reinstall the iOS project. Expo’s `ios.deploymentTarget` controls the app’s minimum iOS version. (GitHub issue 8447 in multica-ai/multica (link omitted to avoid creating a cross-reference))

If you need to support iOS 15 or earlier, the source shown doesn’t establish an Expo fix; the native code would need to guard both subtitle assignments with `if #available(iOS 16.0, *)`. Note the reported error is from an Expo Router 55.0.24 setup with Xcode 27, so verify against your installed version. (GitHub issue 8447 in multica-ai/multica (link omitted to avoid creating a cross-reference))

Citations:

- 1: https://raw.githubusercontent.com/expo/expo/main/packages/expo-router/ios/LinkPreview/LinkPreviewNativeActionView.swift
- 2: GitHub issue 8447 in multica-ai/multica (link omitted to avoid creating a cross-reference)
- 3: GitHub issue 8447 in multica-ai/multica (link omitted to avoid creating a cross-reference)

🏁 Script executed:

python3 - <<'PY'
import io, json, tarfile, urllib.request
url = "https://registry.npmjs.org/react-native/0.83.4"
with urllib.request.urlopen(url, timeout=20) as response:
    metadata = json.load(response)
tarball = metadata["dist"]["tarball"]
with urllib.request.urlopen(tarball, timeout=20) as response:
    archive = tarfile.open(fileobj=io.BytesIO(response.read()), mode="r:gz")
for member in archive.getmembers():
    name = member.name
    if name.endswith("scripts/cocoapods/utils.rb") or name.endswith("scripts/cocoapods/helpers.rb") or name.endswith("scripts/cocoapods/constants.rb"):
        text = archive.extractfile(member).read().decode(errors="replace")
        print("---", name, "---")
        for n, line in enumerate(text.splitlines(), 1):
            if "min_ios_version_supported" in line or "IPHONEOS_DEPLOYMENT_TARGET" in line:
                print(f"{n}: {line}")
PY
printf '%s\n' '--- current app iOS config ---'
nl -ba app.config.ts | sed -n '1,75p'
printf '%s\n' '--- base and head deployment-target references ---'
for rev in be91245dcdc099be97f7aa9b891a4087ac742d6f 0e608c93dea3f966e29c0e50e419e41e6354f707; do
  echo "--- $rev ---"
  git grep -n -E 'deploymentTarget|IPHONEOS_DEPLOYMENT_TARGET|iPhoneDeploymentTarget|min_ios_version_supported' "$rev" -- app.config.ts package.json pnpm-lock.yaml patches eas.json .eas 2>/dev/null || true
done
printf '%s\n' '--- exact expo-router source excerpt from the published package ---'
python3 - <<'PY'
import io, json, tarfile, urllib.request
url = "https://registry.npmjs.org/expo-router/55.0.8"
with urllib.request.urlopen(url, timeout=20) as response:
    metadata = json.load(response)
with urllib.request.urlopen(metadata["dist"]["tarball"], timeout=20) as response:
    archive = tarfile.open(fileobj=io.BytesIO(response.read()), mode="r:gz")
member = next(m for m in archive.getmembers() if m.name.endswith("ios/LinkPreview/LinkPreviewNativeActionView.swift"))
lines = archive.extractfile(member).read().decode().splitlines()
for n in range(100, 146):
    print(f"{n}: {lines[n-1]}")
PY

Repository: exactly/exa

Length of output: 7277


🏁 Script executed:

python3 - <<'PY'
import io, json, tarfile, urllib.request
metadata = json.load(urllib.request.urlopen("https://registry.npmjs.org/react-native/0.83.4", timeout=20))
archive = tarfile.open(fileobj=io.BytesIO(urllib.request.urlopen(metadata["dist"]["tarball"], timeout=20).read()), mode="r:gz")
for member in archive.getmembers():
    if member.name.endswith("scripts/cocoapods/helpers.rb"):
        lines = archive.extractfile(member).read().decode(errors="replace").splitlines()
        for n in range(75, 92):
            print(f"{member.name}:{n}: {lines[n-1]}")
PY

Repository: exactly/exa

Length of output: 1258


Make expo-router compatible with the iOS 15.1 target before merging.

expo-router@55.0.8 assigns subtitle to iOS 16-only APIs without availability guards. The fresh build required by this fingerprint change can therefore fail on the latest Xcode image. This failure is pre-existing in expo-router, but it blocks the new build required by this PR. Upgrade to a compatible release or apply the availability fix. Raising the app target to iOS 16 is an alternative, but it removes iOS 15.1 support.

This branch was successfully deployed

1 active deployment
test — 42e55953 Deployed Oct 20, 2026 by cruzdanilo via e2e (credit) #15079
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