Hi everyone,
I have the following issue that I have tried to tweak every possible modifier of ScrollView and still got the same result in iOS 26.
Description:
Create a SwiftUI ScrollView with scrollTargetBehavior of paging, also create a bottom UI view below the ScrollView.
If the starting index is not 0, the position of current page will be off with part of previous page shown above it.
It only happens on iOS 26, not on iOS 18.
Also if bottom UI view (text view in this case) is removed, it also works fine.
I want to see if there is a solution for it or it's an iOS 26 bug. Thanks!
import SwiftUI
struct ContentView: View {
@State private var currentPageIndex: Int? = 3
var body: some View {
VStack {
scrollView
Text("Bottom Bar")
.frame(maxWidth: .infinity)
.frame(height: 80)
.background(.red)
}
.background(.black)
}
@ViewBuilder
var scrollView: some View {
VerticalPagerView(
currentPageIndex: $currentPageIndex,
itemCount: 10,
content: Array(0...9).map { index in
content(for: index)
}
)
}
@ViewBuilder
private func content(for index: Int) -> some View {
// Empty view with random background color
Color(
red: Double((index * 25 + 0) % 255) / 255.0,
green: Double((index * 25 + 80) % 255) / 255.0,
blue: Double((index * 25 + 160) % 255) / 255.0
)
}
}
struct VerticalPagerView<Content: View>: View {
@Binding private var currentPageIndex: Int?
private let itemCount: Int
private let content: [Content]
init(
currentPageIndex: Binding<Int?>,
itemCount: Int,
content: [Content]
) {
self._currentPageIndex = currentPageIndex
self.itemCount = itemCount
self.content = content
}
var body: some View {
GeometryReader { geometryReader in
ScrollViewReader { reader in
ScrollView(.vertical) {
LazyVStack(spacing: 0) {
ForEach(0 ..< itemCount, id: \.self) { index in
content[index]
.id(index)
.containerRelativeFrame(.vertical, alignment: .center)
.clipped()
}
}
.frame(minHeight: geometryReader.size.height)
.scrollTargetLayout()
}
.scrollIndicators(.hidden)
.onAppear {
guard let currentPageIndex = currentPageIndex else { return }
reader.scrollTo(currentPageIndex, anchor: .center)
}
}
.scrollPosition(id: $currentPageIndex, anchor: .center)
.ignoresSafeArea()
.scrollTargetBehavior(.paging)
.onChange(of: currentPageIndex) { oldIndex, newIndex in
}
}
}
}
Swift
RSS for tagSwift is a powerful and intuitive programming language for Apple platforms and beyond.
Posts under Swift tag
200 Posts
Selecting any option will automatically load the page
Post
Replies
Boosts
Views
Activity
Description:
1. When initiating the print flow via UIPrintInteractionController, and no printer is initially connected, iOS displays all possible paper sizes in the paper selection UI. However, if a printer connects in the background after this view is shown, the list of paper sizes does not automatically refresh to reflect only the options supported by the connected printer.
2. If the user selects an incompatible paper size (one not supported by the printer that has just connected), the app crashes due to an invalid configuration.
Steps to Reproduce:
Launch the app and navigate to the print functionality.
Tap the Print button to invoke UIPrintInteractionController.
At this point, no printer is yet connected. iOS displays all available paper sizes.
While the paper selection UI is visible, the AirPrint-compatible printer connects in the background.
Without dismissing the controller, the user selects a paper size (e.g., one that is not supported by the printer).
The app crashes.
Expected Result: App should not crash
Once the printer becomes available (connected in the background), the paper size options should refresh automatically.
The list should be filtered to only include sizes that are compatible with the connected printer.
This prevents the user from selecting an invalid option, avoiding crashes.
Actual Result: App crashes
The paper size list remains unfiltered.
The user can still select unsupported paper sizes.
Selecting an incompatible option causes the app to crash, due to a mismatch between UI selection and printer capability.
Demo App Crash : https://drive.google.com/file/d/19PV02wzOJhc2DYI6kAe-uxHuR1Qg15Bu/view?usp=sharing
Apple Files App Crash: https://drive.google.com/file/d/1flHKuU_xaxHSzRun1dYlh8w7nBPJZeRb/view?usp=sharing
In iOS 26 When we download any DRM content first time it is downloading again when we edit audios and Video Quality and start downloading it is freezing complete app. Neither it is crashing not giving any error.
So I have a perplexing situation. I'm loading multiple SwiftUI AsyncImages according to spec (see code below). For some reason, my 1MB app has over 400+ MBs of documents & data. When I view the app's container, I can see that it is caused by a massive number of the images as .tmp files in the "tmp" folder all starting with the name "CFNetworkDownload". It seems that every time an image is loaded, it's stored in here, but does not delete. This makes the app get bigger every time it's opened. What can I do about this issue? Thanks so much! (P.S. I've tried to condense my code down as much as possible for readability, but there's a few files included because I'm not sure where the problem lies.)
Main app Swift file:
@main
struct MyApp: App {
let monitor = NWPathMonitor()
@State private var isConnected = true
var body: some Scene {
monitor.pathUpdateHandler = { path in
if path.status == .satisfied {
if !isConnected {
isConnected.toggle()
}
}
else {
if isConnected {
isConnected.toggle()
}
}
}
let queue = DispatchQueue(label: "Monitor")
monitor.start(queue: queue)
return WindowGroup {
isConnected ? AnyView(ContentView()) : AnyView(ContentViewFailed())
}
}
}
The ContentView that's loaded inside the above WindowGroup:
struct ContentView: View {
var body: some View {
TabView {
HomeView()
.tabItem {
Image(systemName: "house.fill")
Text("Home")
}
. . .
}
}
}
And finally, the HomeView where the images are being loaded:
struct HomeView: View {
var body: some View {
let urlString = "https://www.example.com/Home.json"
if let url = URL(string: urlString) {
if let data = try? Data(contentsOf: url) {
do {
items = try JSONDecoder().decode([Item].self, from: data)
}
catch {
print(error)
}
}
}
return NavigationView {
List {
ScrollView {
VStack(alignment: .leading) {
ZStack {
VStack(alignment: .leading) {
Spacer()
HStack {
AsyncImage(url: URL(string: "https://www.example.com/images/example.png")) { image in
image
.resizable()
.aspectRatio(contentMode: .fill)
.shadow(color: Color(red: 0, green: 0, blue: 0, opacity: 0.25), radius: 1)
} placeholder: {
ProgressView()
.progressViewStyle(.circular)
}
.frame(width: 202, height: 100)
}
. . .
}
}
}
}
}
}
}
}
I really appreciate your time. Not sure if this could just be a bug with AsyncImage itself.
I have an App which supports multiple windows on the iPad. The App can receive URLs from other Apps (via application.openURL()) and also files via "share sheet" (via UIActivityViewController).
When receiving a URL from another App the delegate method
scene(_ scene: UIScene, openURLContexts URLContexts: Set)
will be called on an existing UIScene, however when a file is received through the share sheet from another App, a new UIScene is created and therefore also a new window (eg the delegates
application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions)
and
scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions)
are called).
In both cases I do get the URL and file just fine, however I do not want to get new UIScenes and windows created when receiving a file via share sheet.
How can I prevent to get a new UIScene or window? The received files should be used in the existing windows and should not create new ones.
Please excuse my lack of understanding of what are probably fundamental concepts in iOS/iPadOS development but I have searched far and wide for documentation and haven't had much luck so far. I am not sure that what I want to do is even possible with an iPad iPadOS app.
Goals: Develop a Swift iPadOS app that can digitally sign a
file using a PIV SmartCard/Token (Personal Identity Verification Card):
Insert a PIV SmartCard/Token (such as a Yubikey 5Ci) into the lightning port of an iPadOS device iPad (NOT MacOS)
Interface with the SmartCard/Token to access the user's PIV certificate/signature and "use it" to sign a file
Question 1: How to get the PIV Certificate from
SmartCard/Token/Yubikey into iPadOS keychain?
* Do we need to get the PIV certificate into the
iOS keychain? Is there another way to interact with a SmartCard directly?
* This should prompt the user for their PIN?
Question 2: How to get our Swift app to hook into the event
that the SmartCard/Token is inserted into the device and then interface with
the user's certificate?
* When is the user prompted to enter their PIN for
SmartCard/Token/Yubikey?
* Do we need to use CyrptoTokenKit to interface with
a smartcard inserted into the lightning port of an iOS device?
After porting code to Swift 6 (Xcode 16.4), I get a consistent crash (on simulator) when using UNUserNotificationServiceConnection
It seems (searching on the web) that others have met the same issue. Is it a known Swift6 bug ? Or am I misusing UNUserNotification ?
I do not have the crash when compiling on Xcode 26 ß5, which hints at an issue in Xcode 16.4.
Crash log:
Thread 10 Queue : com.apple.usernotifications.UNUserNotificationServiceConnection.call-out (serial)
As far as I can tell, it seems error is when calling
nonisolated func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void)
I had to declare non isolated to solve a compiler error.
Main actor-isolated instance method 'userNotificationCenter(_:didReceive:withCompletionHandler:)' cannot be used to satisfy nonisolated requirement from protocol 'UNUserNotificationCenterDelegate'
I was advised to:
Add 'nonisolated' to 'userNotificationCenter(_:didReceive:withCompletionHandler:)' to make this instance method not isolated to the actor
I filed a bug report: Aug 10, 2025 at 2:43 PM – FB19519575
Topic:
App & System Services
SubTopic:
Notifications
Tags:
Swift
Xcode
Notification Center
User Notifications
Hello everyone,
I'm working on an app that uses WKWebView.
My app uses a custom menu and we disable the default menu by overriding with:
WKWebAction.canPerformAction()
However, with the new iOS 18.2 release, I am no longer able to override the "Copy Link with Highlight" option that pops up when highlighting a selection as can be seen from the screenshot:
Has anyone found a work around/bypass for this?
Environment
iOS Version: iOS 18.2
Device: iPhone 13 Pro
App platform: iOS
Xcode version: 16.1
MacOS: 14.5
My Xcode project fails to run with the following crash log any time I use a new SwiftUI symbol such as ConcentricRectangle or .glassEffect. I've tried using the legacy linker to no avail. It compiles perfectly fine, and I've tried targeting just macOS 26 also to no avail. This is a macOS project that's compiled just fine for years and compiles and runs on macOS going back to 13.0.
Failed to look up symbolic reference at 0x118e743cd - offset 1916987 - symbol symbolic _____y_____y_____y_____yAAyAAy_____y__________G_____G_____yAFGGSg_ACyAAy_____y_____SSG_____y_____SgGG______tGSgACyAAyAAy_____ATG_____G_AVtGSgtGGAQySbGG______Qo_ 7SwiftUI4ViewPAAE11glassEffect_2inQrAA5GlassV_qd__tAA5ShapeRd__lFQO AA15ModifiedContentV AA6VStackV AA05TupleC0V AA01_hC0V AA9RectangleV AA5ColorV AA12_FrameLayoutV AA24_BackgroundStyleModifierV AA6IDViewV 8[ ]012EditorTabBarC0V AA022_EnvironmentKeyWritingS0V A_0W0C AA7DividerV A_0w4JumpyC0V AA08_PaddingP0V AA07DefaultgeH0V in /Users/[ ]/Library/Developer/Xcode/DerivedData/[ ]-grfjhgtlsyiobueapymobkzvfytq/Build/Products/Debug/[ ]/Contents/MacOS/[ ].debug.dylib - pointer at 0x119048408 is likely a reference to a missing weak symbol
Example crashing code:
import SwiftUI
struct MyView: View {
var body: some View {
if #available(macOS 26.0, *) {
Text("what the heck man").glassEffect()
}
}
}
Dear Apple Support,
We are encountering an issue with deep linking on iOS 26 when using Safari and App Store redirection. Below are the detailed steps to reproduce the problem:
The app is not installed on the device.
User clicks on a Branch link:
For example:- https://qewed.app.link/99u88ef9f?uuid=88dbwh5ubd4b
Safari opens and displays the fallback page.
User taps on “Get the App”, which navigates to the App Store.
User installs and opens the app.
Expected Behavior:
The app should receive the Branch link parameters (in this case, uuid=88dbwh5ubd4b) upon first open.
Actual Behavior:
The app opens successfully, but the uuid parameter is not passed to the app.
This issue seems specific to the Safari → App Store → App flow on iOS 26, as other flows behave correctly.
Could you please advise whether this is a known issue or if there are recommended adjustments on our side to ensure parameters are consistently delivered after App Store redirection?
Note: We are using Branch SDK version 3.11.0 (Cocoapods dependency)
Thank you for your assistance.
Hello,
Environment
macOS 15.6.1 / Xcode 26 beta 7 / iOS 26 Beta 9
In a simple AVFoundation video-playback sample, I’m seeing different behavior between iOS 18 and iOS 26 regarding AVPlayerItem.didPlayToEndTimeNotification.
I’ve attached a minimal sample below. Please replace videoURL with a valid short video URL.
Repro steps
Tap “Play” to start playback and let the video finish.
The AVPlayerItem.didPlayToEndTimeNotification registered with NotificationCenter should fire, and you should see Play finished. in the console.
Without relaunching, tap “Play” again. This is where the issue arises.
Observed behavior
On iOS 18 and earlier: The video does not play again (it does not restart from the beginning), but AVPlayerItem.didPlayToEndTimeNotification is posted and Play finished. appears in the console. The same happens every time you press “Play”.
On iOS 26: Pressing “Play” does not post AVPlayerItem.didPlayToEndTimeNotification. The code path that prints Play finished. is never called (the callback enclosing that line is not invoked again).
Building the same program with Xcode 16.4 and running it on an iOS 26 beta device shows the same phenomenon, which suggests there has been a behavioral change for AVPlayerItem.didPlayToEndTimeNotification on iOS 26. I couldn’t find any mention of this in the release notes or API Reference.
Because the semantics around AVPlayerItem.didPlayToEndTimeNotification appear to differ, we’re forced to adjust our logic. If there is a way to achieve the iOS 18–style behavior on iOS 26, I would appreciate guidance.
Alternatively, if this change is intentional, could you share the reasoning? Is iOS 26 the correct behavior from Apple’s perspective and iOS 18 (and earlier) behavior considered incorrect? Any official clarification would be extremely helpful.
import UIKit
import AVFoundation
final class ViewController: UIViewController {
private let videoURL = URL(string: "https://......mp4")!
private var player: AVPlayer?
private var playerItem: AVPlayerItem?
private var playerLayer: AVPlayerLayer?
private var observeForComplete: NSObjectProtocol?
// UI
private let playerContainerView = UIView()
private let playButton = UIButton(type: .system)
private let stopButton = UIButton(type: .system)
private let replayButton = UIButton(type: .system)
deinit {
if let observeForComplete {
NotificationCenter.default.removeObserver(observeForComplete)
}
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
setupUI()
setupPlayer()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
playerLayer?.frame = playerContainerView.bounds
}
// MARK: - Setup
private func setupUI() {
playerContainerView.translatesAutoresizingMaskIntoConstraints = false
playerContainerView.backgroundColor = .black
view.addSubview(playerContainerView)
// Buttons
playButton.setTitle("Play", for: .normal)
stopButton.setTitle("Pause", for: .normal)
replayButton.setTitle("RePlay", for: .normal)
[playButton, stopButton, replayButton].forEach {
$0.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
$0.translatesAutoresizingMaskIntoConstraints = false
$0.contentEdgeInsets = UIEdgeInsets(top: 10, left: 16, bottom: 10, right: 16)
}
let stack = UIStackView(arrangedSubviews: [playButton, stopButton, replayButton])
stack.axis = .horizontal
stack.spacing = 16
stack.alignment = .center
stack.distribution = .equalCentering
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
NSLayoutConstraint.activate([
playerContainerView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
playerContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
playerContainerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
playerContainerView.heightAnchor.constraint(equalToConstant: 200),
stack.topAnchor.constraint(equalTo: playerContainerView.bottomAnchor, constant: 20),
stack.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
// Action
playButton.addTarget(self, action: #selector(didTapPlay), for: .touchUpInside)
stopButton.addTarget(self, action: #selector(didTapStop), for: .touchUpInside)
replayButton.addTarget(self, action: #selector(didTapReplayFromStart), for: .touchUpInside)
}
private func setupPlayer() {
// AVURLAsset -> AVPlayerItem → AVPlayer
let asset = AVURLAsset(url: videoURL)
let item = AVPlayerItem(asset: asset)
self.playerItem = item
let player = AVPlayer(playerItem: item)
player.automaticallyWaitsToMinimizeStalling = true
self.player = player
let layer = AVPlayerLayer(player: player)
layer.videoGravity = .resizeAspect
playerContainerView.layer.addSublayer(layer)
layer.frame = playerContainerView.bounds
self.playerLayer = layer
// Notification
if let observeForComplete {
NotificationCenter.default.removeObserver(observeForComplete)
}
if let playerItem {
observeForComplete = NotificationCenter.default.addObserver(
forName: AVPlayerItem.didPlayToEndTimeNotification,
object: playerItem,
queue: .main
) { [weak self] _ in
guard self != nil else { return }
Task { @MainActor in
print("Play finished.")
}
}
}
}
// MARK: - Actions
@objc private func didTapPlay() {
player?.play()
}
@objc private func didTapStop() {
player?.pause()
}
// RePlay
@objc private func didTapReplayFromStart() {
player?.seek(to: .zero, toleranceBefore: .zero, toleranceAfter: .zero) { [weak self] _ in
self?.player?.play()
}
}
}
I would greatly appreciate an official response from Apple engineering on whether this is an intentional change, a regression, or an API contract clarification, and what the recommended approach is going forward. Thank you.
(Sorry if this is not the right place to post...)
I upgraded my iPad / macOS to 26 yesterday. Soon, I noticed that the two buttons in the toolbar would sometimes not appear:
Note that they should be visible at all times.
I played a little more to see if there was any pattern, but I could not find any. Has anyone experienced something similar...? Is this an iPadOS26 bug? (I haven't checked with an iPhone yet.)
Thanks.
Hello
I want to implement customisation to swift argumentparser, Here are following changes want to do it in my cli
changing default footer present in help command output
currently help command output coming like this
OVERVIEW: clisample
USAGE: clisample <subcommand>
OPTIONS:
--version show the version.
-h, --help show the help.
SUBCOMMANDS:
logs (default) Export logs for clisample processes.
See 'clisample --help' for more information.'
so instead of
See 'clisample --help' for more information.'
I want my own string
For more details, run 'clisample help <subcommand>'
customise error string getting from validation error
Error: Missing value for '-t <time>'
Help: -t <time> Time window (e.g. 10h, 30m, 2d).
Usage: clisample logs --time <time>
See 'clisample logs --help' for more information.
so I want error output with example and customised footer, like this
Error: Missing value for '-t <time>'
Help: -t <time> Time window (e.g. 10h, 30m, 2d).
Usage: clisample logs --time <time>
Example: clisample logs -t 5m
For more details, run 'clisample help <subcommand>'
Is this changes possible from anyway?
In WWDC25 video 284: Build a UIKit app with the new design, there is mention of a cornerConfiguration property on UIVisualEffectView. But this properly isn't documented and Xcode 26 isn't aware of any such property.
I'm trying to replicate the results of that video in the section titled Custom Elements starting at the 19:15 point. There is a lot of missing details and typos in the code associated with that video.
My attempts with UIGlassEffect and UIViewEffectView do not result in any capsule shapes. I just get rectangles with no rounded corners at all.
As an experiment, I am trying to recreate the capsule with the layers/location buttons in the iOS 26 version of the Maps app.
I put the following code in a view controller's viewDidLoad method
let imgCfgLayer = UIImage.SymbolConfiguration(hierarchicalColor: .systemGray)
let imgLayer = UIImage(systemName: "square.2.layers.3d.fill", withConfiguration: imgCfgLayer)
var cfgLayer = UIButton.Configuration.plain()
cfgLayer.image = imgLayer
let btnLayer = UIButton(configuration: cfgLayer, primaryAction: UIAction(handler: { _ in
print("layer")
}))
var cfgLoc = UIButton.Configuration.plain()
let imgLoc = UIImage(systemName: "location")
cfgLoc.image = imgLoc
let btnLoc = UIButton(configuration: cfgLoc, primaryAction: UIAction(handler: { _ in
print("location")
}))
let bgEffect = UIGlassEffect()
bgEffect.isInteractive = true
let bg = UIVisualEffectView(effect: bgEffect)
bg.contentView.addSubview(btnLayer)
bg.contentView.addSubview(btnLoc)
view.addSubview(bg)
btnLayer.translatesAutoresizingMaskIntoConstraints = false
btnLoc.translatesAutoresizingMaskIntoConstraints = false
bg.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
btnLayer.leadingAnchor.constraint(equalTo: bg.contentView.leadingAnchor),
btnLayer.trailingAnchor.constraint(equalTo: bg.contentView.trailingAnchor),
btnLayer.topAnchor.constraint(equalTo: bg.contentView.topAnchor),
btnLoc.centerXAnchor.constraint(equalTo: bg.contentView.centerXAnchor),
btnLoc.topAnchor.constraint(equalTo: btnLayer.bottomAnchor, constant: 15),
btnLoc.bottomAnchor.constraint(equalTo: bg.contentView.bottomAnchor),
bg.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor),
bg.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
])
The result is pretty close other than the complete lack of capsule shape.
What changes would be needed to get the capsule shape? Is this even the proper approach?
I have some logic which requires NFC support on the device. This is what I'm using to make sure that it's available:
isNFCMissing = !NFCNDEFReaderSession.readingAvailable && !NFCTagReaderSession.readingAvailable && !NFCVASReaderSession.readingAvailable
Is it possible for isNFCMissing to be true even if the device has an NFC chip.
The minimum iOS version for the application is 16 which is only supported on devices with an NFC chip to begin with.
I downloaded the file through Scoot, and when I remove VisionPro, the app will call the StreamDelegate method and return ". endEncountered". How can I solve this problem?
Thank you!
Hi, When pushing a view controller with a toolbar onto a UITabBarController that has a bottom accessory, the toolbar and bottom accessory overlap.
UITabbarController has a bottomAccessory
AViewController push BViewController. And BViewController.hidesBottomBarWhenPushed = true
Xcode version : Xcode 26.0 Release Cantidate
sample code
let flexible = if #available(iOS 26.0, *) {
UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil)
} else {
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
}
let isMemo = isMemo
let emailItem = UIBarButtonItem(image: UIImage(named: "batch_gray_email.png"), style: .plain, target: self, action: #selector(onEmailTapped))
let deleteItem = UIBarButtonItem(image: UIImage(named: "batch_gray_bin.png"), style: .plain, target: self, action: #selector(onDeleteTapped))
deleteItem.tintColor = .systemRed
let editItem = UIBarButtonItem(image: UIImage(named: "batch_gray_compose.png"), style: .plain, target: self, action: #selector(onEditTapped))
let memoItem = UIBarButtonItem(image: UIImage(named: "batch_note.png"), style: .plain, target: self, action: #selector(onMemoTapped))
if isMemo {
setToolbarItems([flexible, deleteItem, flexible, memoItem, flexible], animated: true)
} else {
setToolbarItems([emailItem, flexible, deleteItem, flexible, editItem, flexible, memoItem], animated: true)
}
AViewController *detailViewController = [[AViewController alloc]init];
detailViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:true];
I am an SDK provider working with Swift Package Manager (SPM) to deliver libraries for iOS developers. My SDK currently uses SPM targets to modularize functionality. However, SPM enforces strict resource bundling, which prevents me from efficiently offering multiple targets—each with a different set of localization files—in a single package.
Current Limitation:
When multiple SPM targets share the same source and resource directory but require distinct sets of .lproj localization folders (for app size or client requirements), SPM raises “overlapping sources” errors. The only workaround is to manually split resource directories or have clients prune localizations post-build, which is inefficient and error-prone.
Feature Request:
Please consider adding native support in Swift Package Manager for:
Defining multiple targets within a single package that can process overlapping source/resource directories,
Each target specifying a distinct subset of localization resource files via the exclude or a new designated parameter,
Enabling efficient modular delivery of SDKs to clients needing different localization payloads, without redundant resource duplication or error-prone manual pruning.
Support for this feature would greatly ease SDK distribution, lower app sizes, and improve package maintainability for iOS and all Swift platforms.
I have been banging my head against this problem for a bit now.
I am trying to build a bidirectional, infinitely scrolling list that implements these core requirements:
Loads data up/down on the fly as the user scrolls
Preserves scroll velocity as the list is updated
Restores the scroll to the exact visual location after data has changed
Ensures no flicker when restoring scroll position - the user cannot know the list has updated and should continue scrolling as normal
Because LazyVStack does not play well with animations, I am opting to go with VStack and am implementing my own sliding window for data. This means that data can be removed as well as added, and a simple application of a height delta is not enough when restoring position.
So far I have tried many things:
Relying on ScrollPosition - simply does not work by itself as described (swift UI trying to keep the position stable with ID's)
Relying on ScrollPosition.scrollTo - only kind of works with ID, no way to restore position with pixel perfect accuracy
Intercepting the UIKit scrollView instance, using it to record and access the top row's position, mutating data and then queuing a scroll restoration using CATransaction.setCompletionBlock - this is the closest I've come, and it satisfies the top 3 requirements but sometimes I get a flicker on slightly heavier lists
What I would really like, is a way of using ScrollView and granularly hooking into the lifecycle of the view after layout, and just before draw. At this point I would update the relevant scroll positions, and allow draw to continue. Is this possible? My knowledge is very limited at this point, but I believe I may be able to achieve something of the sort by swizzling layerWillDraw? Does this make sense, and is it prudent?
In general, I'm very interesting in hearing what people have to say about the above, as well as this problem in general.
I’m trying to keep a specific row visually stable while the data backing a ScrollView changes.
Goal
1. Before updating model.items, capture the top row’s offset relative to the scroll view.
2. Mutate the observable state so SwiftUI recomputes layout — but don’t draw yet.
3. Read the new layout, compute the delta, and adjust the scroll position so the previously visible row stays put.
4. Only then draw the new frame.
Reduced example
@Observable
final class SomeModel {
var items: [SomeItem] = [/* ... */]
}
struct MyBox: View {
@Environment(SomeModel.self) private var model
var body: some View {
ScrollView {
VStack {
ForEach(model.items, id: \.id) { item in
Color.red.frame(height: randomStableHeight(for: item.id))
}
}
}
}
}
// Elsewhere:
let oldRow = recordOldRow() // capture the row to stabilize
model.items = generateNewItems() // mutate model (invalidates layout)
let newPos = capturePreviousRowNewPosition(oldRow) // read new layout?
restoreScrollPosition() // adjust so oldRow stays visually fixed
// draw now
Is that pipeline achievable in SwiftUI? If not, what’s the supported way to keep a row visually stable while the list updates?