') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); Fix watchOS build failure on Xcode 27 by maximbilan · Pull Request #52 · Recouse/EventSource · GitHub
Skip to content

Fix watchOS build failure on Xcode 27 - #52

Merged
Recouse merged 1 commit into
Recouse:mainfrom
maximbilan:fix/xcode-27-watchos-availability
Aug 27, 2026
Merged

Fix watchOS build failure on Xcode 27#52
Recouse merged 1 commit into
Recouse:mainfrom
maximbilan:fix/xcode-27-watchos-availability

Conversation

@maximbilan

Copy link
Copy Markdown
Contributor

Problem

The package fails to compile for watchOS with Xcode 27 beta:

Sources/EventSource/EventParser.swift:91:25: error: 'split(by:)' is unavailable in watchOS:
This method is not recommended on watchOS 9.0+
note: 'split(by:)' was obsoleted in watchOS 9.0

Cause

Xcode 27 raises the minimum supported deployment targets, so the floors declared in Package.swift get clamped upward:

PlatformXcode 26 minXcode 27 minobsoleted: atResult
watchOS4.09.09.0error
iOS12.015.016.0ok
tvOS12.015.016.0ok
macOS10.1312.013.0ok

Package.swift declares .watchOS(.v6), so Xcode 26 compiled with -target arm64-apple-watchos6.0 and the fallback was legal. Xcode 27 clamps to watchos9.0, which meets obsoleted: 9.0 exactly and makes the symbol a hard error.

Being inside the else of if #available(..., watchOS 9.0, *) does not rescue it — an #available else branch does not lower the availability context, so the call is still checked against the deployment target.

Fix

Remove the @available attributes from the fileprivate fallback and rename it legacySplit(by:). They were decorative on a method nothing outside the file could call, but they broke the build. This also removes the need for the #if os(visionOS) special case added in 1c45c73, which worked around the same root cause on a different platform.

iOS and macOS are only one OS release away from the same collision (clamped to 15.0/12.0 against obsoleted: 16.0/13.0), so removing the annotations rather than bumping them should prevent a repeat on the next toolchain.

Deployment targets in Package.swift are unchanged — not a breaking change for consumers.

Verification

  • Clean builds on watchOS, iOS, tvOS, visionOS, macOS under Xcode 27.0 beta (27A5252f)
  • Still builds under Xcode 26.6
  • All 7 tests pass

One note: the fallback path isn't exercised by the test suite — #available takes the modern branch on the test host — but the body is byte-identical to before, only renamed.

🤖 Generated with Claude Code

Xcode 27 raises the minimum watchOS deployment target from 4.0 to 9.0, so
the package's declared `.watchOS(.v6)` floor is clamped up to 9.0. That
collides exactly with the `obsoleted: 9.0` annotation on the private
`split(by:)` back-deployment fallback:
error: 'split(by:)' is unavailable in watchOS:
This method is not recommended on watchOS 9.0+
Sitting in the `else` of `if #available(..., watchOS 9.0, *)` does not help:
an `#available` else branch does not lower the availability context, so the
call is still checked against the deployment target and rejected.
Drop the `@available` attributes from the helper and rename it to
`legacySplit(by:)`. They were decorative on a fileprivate method nothing
outside the file could call, but they broke the build. This also removes the
need for the `#if os(visionOS)` special case added in 1c45c73, which worked
around the same root cause.
iOS and macOS are one OS release from hitting this too (clamped to 15.0/12.0
against `obsoleted:` 16.0/13.0), so removing the annotations avoids a repeat
on the next toolchain.
Deployment targets in Package.swift are unchanged; this is not a breaking
change for consumers.
Verified: clean builds on watchOS, iOS, tvOS, visionOS and macOS under
Xcode 27.0 beta (27A5252f); still builds under Xcode 26.6; all 7 tests pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Recouse
Recouse merged commit 2fc1977 into Recouse:mainAug 27, 2026
3 checks passed
@Recouse

Copy link
Copy Markdown
Owner

Thank you!

Sign up for freeto 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.

2 participants

@maximbilan@Recouse