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:
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.