Regarding FB14055676, I received a message from Apple indicating that this issue has been fixed in iOS 18 Beta 6. As far as I have tested with the sample code and our production code, it appears that the issue has been resolved.
Thank you for fixing this issue.
Post
Replies
Boosts
Views
Activity
This issue still occurs in iOS 18.0 (22A5316j), Xcode 16.0 beta4(16A5211f).
I have confirmed that the problem persists even when using @Observable with Observation. The result changes only depending on whether @Environment is defined in the lower-level View.
When tapping through the cells to display the DetailView, the screen automatically pops back to the WordBookView.
If I comment out @Environment(Env.self) in the WordBookView, the automatic pop does not occur.
This issue occurs only on iOS 18.0 beta. It does not occur in iOS 17.5 or earlier.
import SwiftUI
import Observation
@Observable
class Env {
}
struct Book: Identifiable, Sendable, Hashable {
let id: UUID = UUID()
let name: String
var words: [Word] = []
init(name: String) {
self.name = name
}
}
struct Word: Identifiable, Sendable, Hashable {
let id: UUID = UUID()
var word: String
init(word: String) {
self.word = word
}
}
let staticBooks: [Book] = {
(1...10).enumerated().map {
var b = Book(name: "Book-\($0)")
b.words = (1...100).enumerated().map {
return Word(word: "\(b.name)-Word-\($0)")
}
return b
}
}()
@main
struct iOS18NavigaationStackSampleApp: App {
var body: some Scene {
WindowGroup {
NavigationStack() {
ContentView()
}
}
}
}
struct ContentView: View {
@State private var env = Env()
var body: some View {
WordListView()
.environment(env)
}
}
struct WordListView: View {
@Environment(Env.self) private var env
@State var books:[Book] = staticBooks
@State var selected: Book?
var body: some View {
List(selection: $selected) {
ForEach(books) { book in
NavigationLink(value: book) {
Text(book.name)
}
}
}
.navigationDestination(item: $selected) { book in
WordBookView(book: book)
.environment(env)
}
}
}
struct WordBookView: View {
@Environment(Env.self) var env
let book: Book
@State var selected: Word?
var body: some View {
List(selection: $selected) {
ForEach(book.words) { word in
NavigationLink(value: word) {
Text(word.word)
}
}
}
.navigationDestination(item: $selected) { word in
DetailView(word: word)
}
}
}
struct DetailView: View {
let word: Word
var body: some View {
Text(word.word)
}
}
I forgot to include the environment details:
Xcode: Version 16.0 beta 3 (16A5202i)
Swift: Swift 6
Strict Concurrency Checking: Complete
Thank you for the detailed explanation.
I have a question.
This example shows undefined behavior.
How can SwiftUI developers know that this behavior is undefined? I couldn't find it in the NavigationLink documentation.
And thank you for the code rewrite. The sample code worked as expected. In our production code, I will avoid mixing value-destination and view-destination and will use value-destination exclusively.
I also understood that using get-set binding for navigationDestination(isPresented:) can cause animation issues. To add, I am using this method to support iOS versions prior to iOS16, where navigationDestination(item:destination) is not available, and it works for versions before iOS18.
However, after slightly modifying the rewritten code to match our production code, I encountered a pop issue again. Specifically, when I changed .navigationDestination(for:) to .navigationDestination(item:) within RootView, it automatically pops during the first transition from ContentView to SubView. This issue occurs when using value-destination.
In any case, the issue in this article has been resolved, so I will continue the discussion in the other post: iOS18 beta2: NavigationStack, Views Being Popped Automatically.
Thank you for the explanation at [https://developer.apple.com/forums/thread/758371].
As you understand, I am using navigationDestination(isPresented
) with get/set binding because navigationDestination(item:) is only supported from iOS 17 onwards. Our product code supports iOS 16 and later. (The product itself also supports iOS 15, so we are using NavigationView as well.)
Additionally, when I modified the code provided in the post to be closer to our product code by changing navigationDestination(for:) to navigationDestination(item:), the pop issue reoccurred (during the transition from contentView to subView).
Therefore, I believe this issue is not related to the view-destination or value-destination problem. As a workaround, I have confirmed that commenting out @Environment(.dismiss) defined in ContentView avoids the issue.
While the code in this article is very simple, our product code is enormous and has a very complex structure, making it extremely difficult to pinpoint the cause when behavior changes with or without @Environment.
The following code is automatically popped when navigating from ContentView to SubView on iOS 18 beta 2, but this does not happen on iOS 17.5.
import SwiftUI
struct Selection: Hashable, Identifiable {
var num: Int
var id: Int { num }
}
enum Kind: Hashable {
case a
case b
}
struct RootView3: View {
@State var kind: Kind? = nil
@State var vals: [Selection] = (1...5).map(Selection.init(num:))
@State var selection: Selection?
var body: some View {
if #available(iOS 17.0, *) {
NavigationStack {
List(selection: $kind) {
NavigationLink("Album-a", value: Kind.a)
NavigationLink("Album-b", value: Kind.b)
}
.navigationDestination(item: $kind, destination: { kind in
ContentView3(vals: $vals, selection: $selection)
})
// .navigationDestination(for: Kind.self) { kind in
// ContentView3(vals: $vals, selection: $selection)
// }
}
}
}
}
@available(iOS 17.0, *)
struct ContentView3: View {
@Binding var vals: [Selection]
@Binding var selection: Selection?
@Environment(\.dismiss) private var dismiss
var body: some View {
List(selection: $selection) {
ForEach(self.vals) { val in
NavigationLink(value: val) {
Text("\(String(describing: val))")
}
}
}
.navigationDestination(item: $selection) { sel in
SubView3(kind: .a, selection: sel)
}
}
}
struct SubView3: View {
let kind: Kind
let selection: Selection
var body: some View {
Text("Content. \(kind): \(selection)")
}
}
I have posted FB14125143, and FB11599516 around the time of iOS 16 beta.
Thank you for your response.
I tried the suggested solution, and although it resolved the issue of the view being popped automatically, a different problem has now arisen.
(The new issue: Tapping Back from a lower-level View returns to the Root View / No transition animation )
However, while this fix may work for the simplest code, I think it cannot be applied to our production code for the following three reasons:
This fix requires placing navigationDestination directly under NavigationStack. In our production code, we have multiple levels such as two, three, or more, each handling different data models within a large navigation view. It's not practical to describe all levels of navigationDestination directly under NavigationStack.
This fix introduces another problem, making it unsuitable for inclusion in our production code at this time.
I am unsure whether this behavior is due to a specification change in iOS 18 or a bug in the iOS 18 beta, and whether this solution is just a temporary workaround.
Regardless, thank you for your advice. I hope the behavior of NavigationStack and SwiftUI stabilizes soon.
This problem may not be a regression but rather an issue that has not been fixed since iOS16.
Out of curiosity, have you tried using the Observable macro instead of the older ObservableObject?
Thank you for your response.
Using Observation, this issue no longer occurs.
Since this problem does not occur in iOS 17.x, it seems to be an issue with iOS 18.0 beta 2.
Additionally, as our product code needs to support iOS 15 and later, replacing Observation—which is only available from iOS 17—with Model and View code would lead to considerable duplication and make it difficult.
I also want to share that I was able to reproduce the issue with the following code, which does not use ObservableObject or Observation.
import SwiftUI
enum Kind { case none, a, b, c }
// Selection is storing the selected values in the NavigationStack.
struct Selection: Hashable, Identifiable {
let id = UUID()
let num: Int
init(num: Int) {
self.num = num
print("id: \(id), num: \(num)")
}
}
// Data is corresponding to the selection.
struct Data {
let data: Int
}
@main
struct iOS16_4NavigationSampleApp: App {
var body: some Scene {
WindowGroup {
RootView()
}
}
}
struct RootView: View {
var body: some View {
if #available(iOS 16.0, *) {
NavigationStack {
NavigationLink {
ContentView()
} label: {
Text("album")
}
}
} else {
EmptyView()
}
}
}
struct ContentView: View {
@State var kind: Kind = .a
@State var vals: [Selection] = {
return (1...5).map { Selection(num: $0) }
}()
@State var selection: Selection?
@Environment(\.dismiss) private var dismiss
var body: some View {
list
.onChange(of: self.selection) { newValue in
print("changed: \(String(describing: newValue?.num))")
}
}
@ViewBuilder
private var list: some View {
if #available(iOS 16.0, *) {
List(selection: $selection) {
ForEach(self.vals) { val in
NavigationLink(value: val) {
Text("\(String(describing: val))")
}
}
}
.navigationDestination(isPresented: .init(get: {
return selection != nil
}, set: { newValue in
if !newValue {
selection = nil
}
}), destination: {
SubView(kind: .a)
})
}
}
}
//
struct SubView: View {
init(kind: Kind) {
}
init() {
}
var body: some View {
Text("Content")
}
}
I have posted FB14055676
This issue is likely caused by the following problem with Firebase. Apologies for suspecting StoreKit2.
Release note: v10.22.0
Related:
https://firebase.google.com/support/release-notes/ios#version_10220_-_march_4_2024
https://github.com/firebase/firebase-ios-sdk/issues/11403
How about trying this?
@MainActor class UserManager: ObservableObject {
@Published var user: User
private var cancelSet: Set<AnyCancellable> = .init()
init() {
self.user = User()
self.user.objectWillChange.sink { [weak self] in
self?.objectWillChange.send()
}
.store(in: &cancelSet)
Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
Task { @MainActor in
self.user.ID += "1"
}
}
}
}
struct ContentView: View {
@ObservedObject var userManager: UserManager
var body: some View {
VStack {
Text("\(userManager.user.ID)")
}
.padding()
}
}
Update.
It seems that Transaction.price from iOS 17.2 can be used to get applied price.
https://developer.apple.com/documentation/ios-ipados-release-notes/ios-ipados-17_2-release-notes#StoreKit
https://developer.apple.com/documentation/storekit/transaction/4312704-price
Update:
I have tested on both the iPad Simulator and an actual iPad device.
iPad Simulator (iPadOS 17.2 (21C5029e)): The issue occurs
iPad real device (iPadOS 17.2 (21C5029g)): The issue does not occur
I haven't been able to confirm on an actual iPhone, but it might be an issue that occurs only on the Simulator.
It may be related to the thread below.
Simulator crashing with iOS < 14. Started happening since Big Sur
https://developer.apple.com/forums/thread/667921?page=2
It seems that this issue was fixed in iOS 17.1(21B74)