WWDC22 SALE: Save 50% on all my Swift books and bundles! >>

Day 60 - Milestone Challenge - Navigation "Back" button bug

Forums > 100 Days of SwiftUI

Hi everyone, happy Friday!

I just finished Day 60 and implemented all functions I had planned. I ended up having 1 bug that I cannot fix and 1 functionality that I wish to improve but don't know how to.

1. Bug

I added a feature in my UserDetail view where you can click on a user from the friends list to go to that user's UserDetail view. Here's a snippet of that.

https://github.com/lululucaschae/iFriend/issues/1#issue-1242694993

The feature works as intended. However, as you can see from the video, sometimes the back button says "Back", whereas sometimes it (correctly) displays the NavigationTitle of the previous view ("June Pollard"). I have absolutely no idea why this is happening!

Here's my UserDetailView. (inlineForm is a custom view extension I made. loadDataFromUrl is a custom array extension that basically updates the User array with data from a URL.)

struct UserDetailView: View {
    let source = "https://www.hackingwithswift.com/samples/friendface.json"
    let user: User

    @State private var usersFromUrl = [User]()

    var body: some View {
        Form {
            Section {
                inlineForm(title: "Name", value: user.name)
                inlineForm(title: "Address", value: user.address)
                inlineForm(title: "Company", value: user.company)
                inlineForm(title: "Age", value: String(user.age))
            } header: {
                Text("User Info")
                    .font(.title3)
                    .padding(.bottom, 4)
            }
            Section {
                    ForEach(user.friends, id: \.id) {friend in
                        // Search this friend from user array and store it in thisUser
                        let thisUser = usersFromUrl.first{$0.id == friend.id}
                        NavigationLink {
                            UserDetailView(user: thisUser ?? user)
                        }
                    label: {Text("\(friend.name)")}
                    }
            } header: {
                Text("Friends List")
                    .font(.title3)
                    .padding(.bottom, 4)
            }
        }
        .task {
                await usersFromUrl.loadDataFromUrl(url: source)
        }
        .navigationTitle("\(user.name)")
        .navigationBarTitleDisplayMode(.inline)
    }
}

2. Improvement idea

As you can also see from the video, download happens every time you enter a new UserView (as it's a new instance of UserView and the user array usersFromUrl is a private state variable inside UserView). This will be avoided if the user array (usersFromUrl) is a global variable that can be accessed across all views. But I don't know what the best/right way is to achieve this. (Where and how to declare this global variable).

Thank you in advance!

   

However, as you can see from the video, sometimes the back button says "Back", whereas sometimes it (correctly) displays the NavigationTitle of the previous view ("June Pollard"). I have absolutely no idea why this is happening!

The Back button by default shows the title of the previous screen as its label. But if the title of the current screen is too long, the Back button label is shortened to Back. If the title of the current screen gets really long, the Back button label text is dropped altogether and you'll just see <. This is not a bug.

It takes into account both the length of the previous screen's title (Friendsies in your example) and the length of the current screen's title. You'll note that June Pollard is shorter than Alford Rodriguez, which is why the shortening is triggered.

Here's a quick and dirty example I whipped up to demonstrate:

back button changes

   

There are several ways to achieve a 'global variable', and this most likely is easiest to do with a class

Create a swift file with your class definition as an ObservableObject, together with the @Published value.

In your …app.swift file add @StateObject for the variable, remembering to initialised it in the init() section.

Then for the ContextView (assuming your main view is called the default 'ContextView'), add .environmentObject() for your StateObject.

class AGlobalVar: ObservableObject {
    @Published var globalString = ""
}

in the …app.swift file after the @main and struct <<appname>>: App {

    @StateObject var globalVar: AGlobalVar 

    init() {
        let globalVar = AGlobalVar()
        _globalVar = StateObject(wrappedValue: globalVar)
    }

    … // in the var body: some Scene {    -- or similar

    ContentView( … )
        .environmentObject(globalVar)

then use it like this

@EnvironmentObject var accessGlobalVar: AGlobalVar    // the name accessGlobalVar is just a local name, so could be called something else such as myGlobal, or globalVar, etc.
…

// access the value
accessGlobalVar.globalString = "New text"
…
if accessGlobalVar.globalString == "" {
  …
}

   

Save 50% in my Black Friday sale.

SAVE 50% To celebrate WWDC22, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.