0

I made 30 seconds video of issue I am describing below:

I have UserService class which is a part of SceneDelegate and injected it to my first displayed LoginView:

class SceneDelegate: UIResponder, UIWindowSceneDelegate {
    var window: UIWindow?
    var userService = UserService() 

    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        let contentView = LoginView().environmentObject(userService)
...

In the LoginView I have navigation link which I trigger via async callback programatically:

NavigationLink(destination: WelcomeView(), isActive: $showWelcomeView) { EmptyView() }

When I get user from the userService I show WelcomeView

struct WelcomeView: View {

    @EnvironmentObject var userService: UserService

var body: some View {
                VStack {
                    CircleImage(image: userService.user.image!)
                        .offset(y: -220)
                        .frame(height: 140)
                        .frame(width: 140)
...

as you see my userService is an @EnvironmentObject.

First time I show WelcomeView from navigation link all works good, but when I pop back to LoginView and login again to push WelcomeView I see this error:

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

enter image description here

By some reason my userService.user.image became nil after I pop back to previous Login view and push Welcome view again.

Then I tried to debug it and found out that somehow my async call back here is ignored. You can see I make two async calls I've added a comment below where I never get callback for the second run:

func getUser(with email: String, completion: @escaping (UserFetchResult) -> Void) {

        let findUsers:PFQuery = PFUser.query()!
        findUsers.whereKey("email",  equalTo: email)
        findUsers.findObjectsInBackground { (objects, error) in
            if error == nil {
                guard let firstFoundUser = objects?.first as? PFUser else {
                    print("Get users error")
                    completion(.failure)
                    return
                }

                self.user = User(pfUser: firstFoundUser)
                // IGNORED THIS CALL AND DISPLAY WelcomeView without waiting completion handler.
                self.user.avatar?.getDataInBackground(block: { (data, error) in

                    if error == nil {
                        if let unwrappedData = data {
                            if let unwrappedUIImage = UIImage(data: unwrappedData) {
                                self.user.image = Image(uiImage: unwrappedUIImage)
                            }
                        }
                    }

                    if self.user.image == nil {
                        self.user.image = Image("ManPlaceholderAvatar")
                    }

                    completion(.success(data: self.user))
                })

            } else {
                print("Get users error")
                completion(.failure)
            }
        }
    }

You can check debug details in this video.

Matrosov Oleksandr
  • 25,505
  • 44
  • 151
  • 277
  • I have different question - why getUser should be called at all second time if the user already fetched & image retrieved? I think it design issue here - there have not be such refetch for just constructed user at all, no? – Asperi Apr 23 '20 at 11:36
  • @Asperi thanks for your comments! I have user fetched but it's kind of a first sing in check when we can identify that we have this email in the system actually. Welcome screen just needed for entering password if desired user was found by first request. Anyway your thoughts started me thinking that's probably the issue I faced is not with SwiftUI but with Parse. Looks like `self.user.avatar?.getDataInBackground` will be never invoked because it's cached already. But still can't understand how navigation works ignoring completion handler. – Matrosov Oleksandr Apr 23 '20 at 20:05
  • @Asperi I've created a public [repo](https://github.com/matrosovDev/swiftui) if you have time to take a look. there are just two Views. So for the first time all works good, but second time the WelcomeView pushed without waiting completion handler actually (I mean if we pop back and push again one more time). I've added print command. – Matrosov Oleksandr Apr 23 '20 at 20:57
  • @Asperi looks like when I pop back to login screen from welcome the welcome screen still exist. and when I try to update observable property it lead to this update issue. – Matrosov Oleksandr Apr 26 '20 at 17:09
  • @Asperi looks like I found the issue with deallocating WelcomeView. This post not so clear then as it's not connected to actually async call but to allocating and deallocating issue. I've posted new question [here](https://stackoverflow.com/questions/61448125/swiftui-pop-back-in-navigation-stack-does-not-deallocate-a-view). I would like to close this question if you don't mind. thanks for help. – Matrosov Oleksandr Apr 26 '20 at 21:17

0 Answers0