2

I have this scenario:

  • Perform a post request only when a user taps on a button!
  • Get response data
  • Navigate to another view passing the data I get from the response

I found similar examples but none was passing arguments from the request's response. The code builds but fails while running with this error:

Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value

Also if I remove the argument from the destination like NavigationLink(destination: PostView()) and remove it from the PostView file too, then it works only if I click twice on the button :(. How can we solve this scenario with a better solution?

Here is what I did so far:

// View1
VStack{
    NavigationLink(
        destination: PostView(post: self.PostViewModel.createdPost), // the error is shown here
        isActive: $isActive
    ){
        Button(action: {
            if self.PostViewModel.createdPost != nil {
                self.isActive = true
            } else {
                self.PostViewModel.createPost()
            }
        }) {
            Text("Create a Post")
        }
    }
}
// view2
struct PostView: View {
    private var post: Post
    init(post: Post){
        self.post = post
    }
    var body: some View {
        VStack {
            Text("New Post!")
        }
    }
}
A Akrm
  • 97
  • 7

2 Answers2

1

The NavigationLink creates destination, ie. PostView in your case, in time of creation [!!!] (not navigation as seems you expected), so there is no createdPost model yet, it is nil, and crash happened.

The possible solution is to defer your destination creation till navigation time, so needed arguments would be prepared:

    NavigationLink(
        destination: DeferView { 
                        PostView(post: self.PostViewModel.createdPost)
                     },
        isActive: $isActive
    ){

Find DeferView in my different answer https://stackoverflow.com/a/61242931/12299030.

Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Thanks for the reply, it solved one issue regarding the nil values.. but I'm still struggling with the button.. it navigates to the postView only if I click twice on it.. – A Akrm Feb 14 '21 at 12:53
0

How about placing the logic in PostView and wrap it in a calculated property?

struct PostView: View {
private let context : PostViewModel

private var post: Post {
    if let createdPost = context.createdPost {
        return createdPost
    } else {
        context.PostViewModel.createPost() // handle error here
        return context.createdPost
    }
}

init(context: PostViewModel) {
    self.context = context
}

var body: some View {
    VStack {
        Text("\(post.label)")
    }
}

}

Then there is no need for the button. Just navigate to the PostView and wait for the ViewModel to do its thing. The navigation link becomes

NavigationLink(
    destination: PostView(context: self.PostViewModel),
    isActive: $isActive
) {
    Text("Create a Post")
}
Helperbug
  • 529
  • 3
  • 8
  • hmmm not ideal, as it would be better to navigate to a new view only when the request succeeds to prevent useless view transitions. – A Akrm Feb 14 '21 at 07:31
  • The flow of the answer is the same as the question. There was no error path in the question. Might as well display the error in PostView. Did you have another place to direct an error? – Helperbug Feb 14 '21 at 08:09
  • @A Akrm was the answer useful? – Helperbug Feb 19 '21 at 02:49