Got these messages today; I had set webView.navigationDelegate, but not the setting mentioned above. But adding Background fetch and Background Processing capabilities stopped the messages. I didn't need to add Remote Notifications.
Thank you @MrOfu !
Post
Replies
Boosts
Views
Activity
@foreignfilm I can confirm this not working as expected under iOS, while working as expected under macOS.
It doesn't work under iOS when drag source and drop destination are contained within the same list.
i.e., you're able to drop items from another view that is not part of the same list.
Could we use NavigationStack in sidebar like this? It seems to work for iPhone, iPad and Mac:
import SwiftUI
struct Thing: Identifiable, Hashable {
let id = UUID()
let name: String
}
struct ContentView: View {
let things: [Thing] = [
Thing(name: "One"),
Thing(name: "Two"),
Thing(name: "Three")
]
@EnvironmentObject var navigation: NavigationModel
var body: some View {
NavigationSplitView {
NavigationStack {
sidebar
}
.navigationDestination(for: Thing.ID.self) { id in
DetailView(selectedThingId: id)
}
} detail: {
}
}
var sidebar: some View {
ScrollView(.vertical) {
LazyVStack {
ForEach(things) { thing in
NavigationLink("Thing: \(thing.name) \( navigation.selectedThingId == thing.id ? "selected" : "" )",
value: thing.id)
}
SomeOtherViewHere()
NavigationLink("Navigate to something else", value: things[1].id)
}
}
}
}
struct DetailView: View {
let selectedThingId: Thing.ID?
var body: some View {
if let selectedThingId {
Text("There is a thing ID: \(selectedThingId)")
} else {
Text("There is no thing.")
}
}
}
struct SomeOtherViewHere: View {
var body: some View {
Text("Some other view")
}
}
Same error in 14.1 beta (14B5024h) as well.
Here's a workaround, updating the NavigationPath using the selection in the DetailView; this works under iPadOS and MacOS.
import SwiftUI
// Sample model definitions used to trigger navigation with navigationDestination API.
struct SampleModel1: Hashable, Identifiable {
let id = UUID()
static let samples = [SampleModel1(), SampleModel1(), SampleModel1()]
}
struct SampleModel2: Hashable, Identifiable {
let id = UUID()
static let samples = [SampleModel2(), SampleModel2(), SampleModel2()]
}
// The initial view loaded by the app. This will initialize the NavigationSplitView
struct ContentView: View {
@State var path = NavigationPath()
enum NavItem: Hashable, Equatable {
case first
}
var body: some View {
NavigationSplitView {
List {
NavigationLink(value: NavItem.first) {
Label("First", systemImage: "house")
}
}
} detail: {
SampleListView(path: $path)
}
}
}
// A list of SampleModel1 instances wrapped in a NavigationStack with multiple navigationDestinations
struct SampleListView: View {
// Get the selection from DetailView and append to path
// via .onChange
@State var selection2: SampleModel2? = nil
@Binding var path: NavigationPath
var body: some View {
NavigationStack(path: $path) {
VStack {
Text("Path: \(path.count)")
.padding()
List(SampleModel1.samples) { model in
NavigationLink("Model1: \(model.id)", value: model)
}
.navigationDestination(for: SampleModel2.self) { model in
Text("Model 2 ID \(model.id)")
.navigationTitle("navigationDestination(for: SampleModel2.self)")
}
.navigationDestination(for: SampleModel1.self) { model in
SampleDetailView(model: model, path: $path, selection2: $selection2)
.navigationTitle("navigationDestination(for: SampleModel1.self)")
}
.navigationTitle("First")
}
.onChange(of: selection2) { newValue in
path.append(newValue!)
}
}
}
}
// A detailed view of a single SampleModel1 instance. This includes a list
// of SampleModel2 instances that we would like to be able to navigate to
struct SampleDetailView: View {
var model: SampleModel1
@Binding var path: NavigationPath
@Binding var selection2: SampleModel2?
var body: some View {
NavigationStack {
Text("Path: \(path.count)")
.padding()
List(SampleModel2.samples, selection: $selection2) { model2 in
NavigationLink("Model2: \(model2.id)", value: model2)
// This also works (without .onChange):
// Button(model2.id.uuidString) {
// path.append(model2)
// }
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The problem is there even in Apple's sample Bringing robust navigation structure to your SwiftUI app | Apple Developer Documentation: on Mac Related Recipes cannot be selected after choosing Experience > Two Columns > Dessert > Apple Pie.
Could you please provide a navigation sequence showing the issue?
For example the following works as expected under iOS in portrait mode:
Two Columns > Dessert > Apple Pie > Related Recipes: Pie Crust shows the details for Pie Crust
Defining the detail of the NavigationSplitView in the following manner works as expected (persisting the selection is needed for .compact class:
final class NavigationModel: ObservableObject {
@Published var detailSelection: String?
}
@main
struct NavigationLink_711115App: App {
@StateObject var navigationModel = NavigationModel()
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(navigationModel)
}
}
}
struct ContentView: View {
@EnvironmentObject var navigationModel: NavigationModel
@State var splitViewVisibility: NavigationSplitViewVisibility = .all
var body: some View {
NavigationSplitView(columnVisibility: $splitViewVisibility) {
List(selection: $navigationModel.detailSelection) {
NavigationLink("First", value: "First")
}
} detail: {
if let detailSelection = navigationModel.detailSelection {
DetailView(string: detailSelection)
} else {
Text("Select the first view from the list")
}
}
.navigationSplitViewStyle(.balanced)
}
}
struct DetailView: View {
@EnvironmentObject var navigationModel: NavigationModel
var string: String
var body: some View {
NavigationStack {
List {
NavigationLink("Second", value: "Second")
}
.navigationDestination(for: String.self, destination: { string in
Text(string)
})
}
.navigationTitle(string)
}
}
Since List is very versatile, I'm not sure why it cannot be used here. For example, the following works fine in iOS and iPadOS (by persisting the selection):
import SwiftUI
class Q708440NavigationModel: ObservableObject {
@Published var selectedThingId: Thing?
}
struct Thing: Identifiable, Hashable {
let id: UUID = UUID()
let name: String
}
class ThingData: ObservableObject {
@Published var things: [Thing] = [
Thing(name: "One"),
Thing(name: "Two"),
Thing(name: "Three")
]
}
struct ThingDetail: View {
let thing: Thing
@EnvironmentObject var navigationModel: Q708440NavigationModel
var body: some View {
Text("Thing: \(thing.name)")
}
}
struct SomeOtherViewHere: View {
var body: some View {
Text("Some other view")
}
}
@main
struct Q708440App: App {
@StateObject var navigationModel = Q708440NavigationModel()
@StateObject var thingData = ThingData()
var body: some Scene {
WindowGroup {
Q708440(things: $thingData.things)
.environmentObject(navigationModel)
}
}
}
struct Q708440: View {
@Binding var things: [Thing]
@EnvironmentObject var navigationModel: Q708440NavigationModel
var body: some View {
NavigationSplitView {
List(selection: $navigationModel.selectedThingId) {
Section("Data driven selection") {
ForEach(things) { thing in
Button("Thing: \(thing.name) \( navigationModel.selectedThingId?.id == thing.id ? "selected" : "" )") {
navigationModel.selectedThingId = thing
}
}
}
Section("Some other view") {
SomeOtherViewHere()
}
Section("Non-Interactive") {
Text("Selection-driven, three-column NavigationSplitView sometimes fails to push when collapsed to a single column. (93673059)")
}
Section("Another selection") {
Button("Navigate to thing 2") { navigationModel.selectedThingId = things[1] }
}
Section("Using NavtigationLink") {
NavigationLink("Goto to thing 1", value: things[0])
}
}
} detail: {
ThingDetail(thing: navigationModel.selectedThingId ?? Thing(name: "Select"))
}
}
}
Persisting navigation path seems to get these to work as expected:
import SwiftUI
class NavigationModel: ObservableObject {
@Published var presentedItems: [Int] = []
}
struct SidebarEntry: Identifiable, Hashable {
let id = UUID()
var localizedName: String
}
let sidebarEntries = [
SidebarEntry(localizedName: "Files"),
SidebarEntry(localizedName: "Bookmarks"),
]
// MARK: - main -
@main
struct NewNavigationTestApp: App {
@State private var sidebarSelection: SidebarEntry?
@StateObject var navigationModel = NavigationModel()
var body: some Scene {
WindowGroup {
NavigationSplitView {
List(sidebarEntries, selection: $sidebarSelection) { entry in
NavigationLink("\(entry.localizedName)", value: entry)
}
} detail: {
ZStack { // workaround
if let sidebarSelection {
if sidebarSelection.localizedName == "Files" {
TestStackView()
} else if sidebarSelection.localizedName == "Bookmarks" {
Text("Bookmarks View")
} else {
Text("Unknown View")
}
} else {
Text("No selection")
}
}
}
.environmentObject(navigationModel)
}
}
}
// MARK: - FilesView -
struct TestStackView: View {
@EnvironmentObject var navigationModel: NavigationModel
var body: some View {
NavigationStack(path: $navigationModel.presentedItems) {
List(0 ..< 999) { idx in
NavigationLink("\(idx)", value: idx)
}
.navigationTitle("Numbers")
.navigationDestination(for: Int.self) { idx in
Text("Hello \(idx)")
}
}
}
}
It's not a shortcut, but an additional effect on a text selection of the editor preference:
Preferences > Text Editing > Code completion > Automatically insert closing braces ("}")
(which is turned on by default)
To test it, select a line and then enter a { - line will be wrapped as appeared in the video.