Issue #87: iOS 27 ships, Xcode agents get hands, and ArrangementView for the Duo
iOS 27 and Xcode 27 went final on Monday, so everything we've been calling "beta" since June is now what your users run. This week is about what the stable release changes for your build and review pipeline, what landed in Xcode 27 for agents, testing, and debugging, a few Swift 6.4 details worth knowing, and the first community deep dives on building for iPhone Duo. 🚀📱
From the Community!
You all are shipping some great stuff, and these three picks come straight from the iOS Code Reviews community. Check them out and Show some love to the developers building them! 🛠️
A day planner for time blindness. Give each task a time estimate and watch your finish time move, then work through distraction-free focus sessions with Live Activities and a Dynamic Island. |
Want your tool, Mac app, or iOS app in the classifieds next week? Placements are $30. Reply to this email or write to ioscodereviews@gmail.com.
iOS 27 and Xcode 27 Are Final (iOS 27.0 / Xcode 27, stable)
iOS 27.0 (24A437) started rolling out Monday, September 14, alongside iPadOS, macOS, tvOS, visionOS, and watchOS 27. Xcode 27 shipped as build 27A266a, the same build as the RC, and it includes Swift 6.4. App Store submissions built with the iOS 27 SDK have been open since September 9.
Three items from Apple's submission notice need action from you, and one of them has a deadline.
The age rating questionnaire has a new question. iOS 27 adds Time Allowances, which let parents limit time across categories like Entertainment, Games, and Social Media. Per Apple, "if your app or game includes social media capabilities, you'll need to indicate them in App Store Connect." If your app has a feed, DMs, or public profiles, answer this before your next submission.
The App Store has new product page headers and search result assets, with a preview tool in App Store Connect listed as "coming soon."
And the deadline: starting April 2027, iOS and iPadOS apps uploaded to App Store Connect must be built with the iOS 27 SDK or later. That covers Liquid Glass adoption (Issue #83) too, so budget for it now.
If you ship a Mac app, Xcode 27 itself "will only install and run on Apple silicon Macs," and targets with a macOS 27 deployment target no longer build Universal by default because ARCHS_STANDARD drops x86_64. You can still add x86_64 to ARCHS and back-deploy Universal builds to macOS 12.

Xcode 27: Agents Can Now Run Your App (Xcode 27, stable)
We covered the headless MCP server in Issue #85. The final release notes show how much more the agent tooling does now. Agents in Xcode "can now boot simulators, install and launch apps, synthesize touch events, and capture screenshots to verify UI behavior." The Xcode MCP server gained tools to control the active run state, read the debugger console, switch schemes and run destinations, and edit build settings, entitlements, and Info.plist keys. The Preview Snapshot tool renders light and dark, portrait and landscape, and Dynamic Type variants in one call.
Planning is a first-class step now. Plans show up as editable Markdown next to the conversation, and you approve them before the agent writes code. Agents can be extended with plugins that bundle skills, MCP servers, and Agent Client Protocol configurations, and skills run as slash commands. Xcode also ships Apple-built specialists for localization, UIKit resizing, and accessibility, and Google Gemini joins the model list.
Two items worth acting on today. The first is the new security layer that "monitors and controls filesystem access by coding agents and any processes they spawn," which is off by default and lives in Coding Intelligence settings. The second lives outside Xcode: LLDB now ships its own MCP server, lldb-mcp, so any agent you already use can drive a debug session.

Testing: Chasing Flaky Tests from the Command Line (Xcode 27 / SwiftPM, stable)
If you have a test that fails one run in forty, swift test can now rerun it until it breaks, and only the matching cases repeat:
# Rerun until a failure shows up, capped at 50 attempts
swift test --filter CheckoutTests --maximum-repetitions 50 --repeat-until fail
# Or rerun a known-flaky test until it passes
swift test --filter CheckoutTests --maximum-repetitions 10 --repeat-until pass
swift test also prints a summary of failing test targets at the end of the run, so you stop scrolling for the red line.
Three more testing changes in Xcode 27. XCUIVoiceOverService lets UI tests drive VoiceOver and assert on focus, spoken output, and navigation. Your test plan can set app crashes during UI tests to off, warning, failure, or fatal failure. And calling an #expect from an XCTest test (or XCTAssert from a Swift Testing test) now surfaces a runtime issue with warning severity when the assertion fails.
Large Swift Testing suites with many parameterized cases have "significantly better performance" in Xcode 27, and each argument case now gets a unique test link that includes a hash of its arguments. If you followed the @Test(arguments:) pattern from earlier issues, you can now link a teammate to the exact failing input.
Swift 6.4: New Hashable Conformances and One Source Break (Swift 6.4, stable)
Swift 6.4 adds Hashable to types that could already be compared with == but couldn't go in a Set: Dictionary.Keys, CollectionOfOne, and EmptyCollection (SE-0514), plus UnownedTaskExecutor (SE-0523). The practical one is Dictionary.Keys, which lets you track payload shapes without converting to a Set<String> first:
// Swift 6.4
var knownSchemas: Set<[String: Any].Keys> = []
func record(_ payload: [String: Any]) {
if knownSchemas.insert(payload.keys).inserted {
print("New payload shape: \(payload.keys.sorted())")
}
}
The value type doesn't need to be Hashable, only the keys, and they already are.
New Hashable conformances in Swift 6.4 "Swift 6.4 adds Hashable conformances for several standard library types that could already be compared but could not be stored in a Set."
The source break is small and specific. Xcode 27 lists it as a known issue from SE-0508: a computed property with an init accessor and a collection literal default no longer compiles if the getter comes first. Swap the order:
struct S {
var _strings: [String]
// Breaks in Swift 6.4: getter declared before init
// Fix: declare the init accessor first
var strings: [String] = ["hello"] {
@storageRestrictions(initializes: _strings)
init { _strings = newValue }
get { _strings }
}
}
One more build note: the dependency scanner is faster in Xcode 27, but every Clang module reachable from a single scan now needs a unique name. If you vendor a third-party library that ships a module.modulemap redeclaring an SDK module, expect a scan error the first time you build.

Debugging: Smaller dSYMs and a Task Tree in LLDB (Swift 6.3 / 6.4)
Adrian Prantl's post on Swift.org explains why po has historically been slow or wrong in large apps. LLDB found Swift modules by name, which breaks when several variants exist, and fell back to recompiling SDK modules from source when it couldn't find one. Starting in Swift 6.3 and finishing in 6.4, debug info records each object file's exact module path, binary Swift modules are no longer embedded in dSYM bundles, and LLDB can import precompiled bridging headers directly. Xcode and SwiftPM users get this automatically. If you run a custom build system (Bazel, Buck), you need to pass -debug-module-path yourself.
While you're in LLDB, Xcode 27 adds a command for anyone debugging structured concurrency:
(lldb) language swift task tree
It prints every Swift task the debugger knows about, as a tree, so you can see which child tasks are still alive under a parent that should have finished.

SwiftUI: Debounced Search Without Stale Results (iOS 15+)
A search field that fires a request per keystroke has two bugs: it spams your backend, and a slow response for "cat" can land after the fast one for "cats" and overwrite it. Natascha Fadeeva's fix uses only task(id:) and cooperative cancellation, no Combine:
struct SearchView: View {
@State private var query = ""
@State private var results: [Item] = []
let searchService: SearchService
var body: some View {
List(results) { item in
Text(item.title)
}
.searchable(text: $query)
.task(id: query) {
let submittedQuery = query
do {
// Debounce: this sleep is cancelled if the user keeps typing
try await Task.sleep(for: .milliseconds(300))
let newResults = try await searchService.search(for: submittedQuery)
// Drop the response if a newer query has taken over
try Task.checkCancellation()
guard submittedQuery == query else { return }
results = newResults
} catch is CancellationError {
// Expected when the query changes
} catch {
// Handle real errors here
}
}
}
}
The detail to get right is that cancellation is cooperative. In her words, task(id:) "requests cancellation of the previous task by marking it as cancelled. It doesn't forcibly terminate the task at that exact point." If your search service ignores cancellation, the old request still finishes, which is why the checkCancellation() and query guard sit right before you publish.

iPhone Duo: ArrangementView (iOS 27.1 SDK, beta coming later this month)
Last issue covered NavigationSplitView for list-detail apps. For two views that aren't a hierarchy, like a player and its transcript or a canvas and its controls, iOS 27.1 adds ArrangementView. You give it a primary and a secondary view and let the system place them around the available space and the fold:
NavigationStack {
ArrangementView {
PlayerView()
} secondary: {
TranscriptView()
}
.arrangementViewStyle(.split)
}
Use .split when both views need to stay visible on their own, and .overlay when one can sit on top of the other, like controls over a video. Note the NavigationStack wrapper: ArrangementView "does not provide navigation infrastructure itself," so it goes inside your navigation container instead of replacing it. UIKit gets UIArrangementViewController.

iPhone Duo: The Details Buried in Apple's Videos
Apple posted six iPhone Duo sessions last week, and Jordan Morgan went through all of them and pulled out the details. A few that weren't in the Tech Talk we covered in #86:
reservedRegion on GeometryProxy and UIView returns two kinds of region: "division regions describe the fold, while occlusion regions describe obstructions" like the camera. Keep tappable controls out of both.
onHingeChange in SwiftUI and UIHingeInteraction in UIKit expose the hinge angle for continuous effects, and you have to "handle a missing hinge on other devices." On every other iPhone there is no hinge to report.
"New windows can only be created on the inner display." If you just turned on multiple scenes, don't build a flow that opens a second window while the phone is closed.
For camera apps, the virtual front camera switches automatically between displays but is "limited to 1080p/60fps and no depth," and "a camera marked .front can face away from the person using your app." Use AVCaptureDeviceDirectionCoordinator to find out which way a camera actually faces before you decide whether to mirror the preview.
iPhone Duo: First Developer Good-to-Knows "A camera marked .front can face away from the person using your app."
Swiftjectivec Jordan Morgan
Apple is running online iPhone Duo Group Labs on September 16 (8 to 9 p.m. PT) and September 17 (8 to 9 a.m. PT), plus SwiftUI, UIKit, and Photos & Camera Q&As on the Developer Forums on September 23. The Designing for iPhone Duo section of the HIG is live now. Sign up from the iPhone Duo developer page.
Quick Hits
Michael Tsai collected developer frustration with SwiftUI toolbars, including Phil Zakharchenko's point that there is still no customizable ToolbarItemGroup. Natasha Murashev explains ViewThatFits as a low-effort way to handle multi-device layouts. Natascha Fadeeva builds a transit tracker in Getting started with Live Activities in SwiftUI. Natalia Panferova draws a multi-level donut in Building a sunburst diagram in Swift Charts. Xu Yang argues that slow SwiftData lists usually trace back to the model, in SwiftData: Optimization Starts with Modeling. And Matt Massicotte tries Godot with Swift for game development.
What to Do This Week
Move your CI to Xcode 27 (27A266a) and make sure your machines are Apple silicon, because Xcode 27 won't run on anything else. Answer the new social media question in the age rating questionnaire before your next submission. Build once and fix any SE-0508 init accessor errors and duplicate Clang module name errors. Turn on the filesystem security layer for coding agents. Point swift test --repeat-until fail at your flakiest test. Then watch the Releases page for the Xcode 27.1 beta so you can start on the Duo work from #86 the day it drops.
✌️
Alright, that's it for today! Let's spread the good code vibes ✨🧘☀️


Member discussion