Description
I am developing a feature to download some files and share with others.
I have to show an alert view to notice user the download progress.
When the download complete, it will automatically dismiss the alert and show the share activity view.
However, when I change the destination state after the download completed,
it will dismiss the view controller instead of presenting the share activity view.
The more weird thing is that it seems only happened when presenting UIActivityController,
I got not problems when presenting normal view controller or alert view controller.
I found out a workaround by adding a short buffer time between the destination state change.
I have made a simple implementation below that showcase the weird behavior.
Checklist
Expected behavior
Dismiss the alert and then present the UIActivityController.
Actual behavior
The alert is dismissed and the parent view controller is also dismissed.
Steps to reproduce
import UIKit
import UIKitNavigation
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
viewSetup()
}
func viewSetup() {
let button = UIButton(type: .system)
button.setTitle("Present ViewController", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
button.addAction(
.init(
handler: { [weak self] _ in
guard let self else { return }
present(PresentedViewController(), animated: true)
}
),
for: .touchUpInside
)
}
}
@Observable
class PresentedViewModel {
@CasePathable
enum Destination {
case alert
case shareActivity
}
var destination: Destination?
func onButtonTap() {
destination = .alert
// this will dismiss the presented view controller
Task {
// Doing some async works, like downloading files
try? await Task.sleep(for: .seconds(2))
// Workaround, if adding short buffer time to change state, it will be fine
// try? await Task.sleep(for: .milliseconds(500))
// destination = nil
// try? await Task.sleep(for: .milliseconds(500))
destination = .shareActivity
}
}
func onAlertButtonTap() {
// No problems here
destination = .shareActivity
}
}
class PresentedViewController: UIViewController {
@UIBindable var viewModel = PresentedViewModel()
override func viewDidLoad() {
super.viewDidLoad()
viewSetup()
navigation()
}
func viewSetup() {
view.backgroundColor = .systemBackground
let button = UIButton(type: .system)
button.setTitle("Show Alert Now", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(button)
NSLayoutConstraint.activate([
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
button.addAction(
.init(
handler: { [weak self] _ in
guard let self else { return }
viewModel.onButtonTap()
}
),
for: .touchUpInside
)
}
func navigation() {
present(isPresented: UIBinding($viewModel.destination.alert)) {
let alertVC = UIAlertController(
title: "Alert",
message: "Wait to show Share Activity",
preferredStyle: .alert
)
alertVC.addAction(
.init(
title: "Show",
style: .destructive,
handler: { [weak self] _ in
guard let self else { return }
viewModel.onAlertButtonTap()
}
)
)
return alertVC
}
present(isPresented: UIBinding($viewModel.destination.shareActivity)) {
let shareVC = UIActivityViewController(
activityItems: ["Texts to share"],
applicationActivities: nil
)
return shareVC
// Weird, no problems when presenting a normal view controller
// return PresentedViewController()
}
}
}
SwiftUI Navigation version information
2.2.3
Destination operating system
iOS 18
Xcode version information
16.2
Swift Compiler version information
swift-driver version: 1.115.1 Apple Swift version 6.0.3 (swiftlang-6.0.3.1.10 clang-1600.0.30.1)
Target: arm64-apple-macosx15.0
Description
I am developing a feature to download some files and share with others.
I have to show an alert view to notice user the download progress.
When the download complete, it will automatically dismiss the alert and show the share activity view.
However, when I change the destination state after the download completed,
it will dismiss the view controller instead of presenting the share activity view.
The more weird thing is that it seems only happened when presenting
UIActivityController,I got not problems when presenting normal view controller or alert view controller.
I found out a workaround by adding a short buffer time between the destination state change.
I have made a simple implementation below that showcase the weird behavior.
Checklist
mainbranch of this package.Expected behavior
Dismiss the alert and then present the
UIActivityController.Actual behavior
The alert is dismissed and the parent view controller is also dismissed.
Steps to reproduce
SwiftUI Navigation version information
2.2.3
Destination operating system
iOS 18
Xcode version information
16.2
Swift Compiler version information