UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

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!

2      

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

2      

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 == "" {
  …
}

2      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

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

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

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.