amelia
amelia

Reputation: 111

Can someone explain what "extra trailing closure passed in call" means?

I'm receiving the error "Extra trailing closure passed in call" on the authViewModel.fetchUser() function. From what I've gathered researching online this means that fetchuser can't have the trailing closure (the brackets), I am confused about what in my fetchuser function says that it cannot have the {} after the call. Or maybe I'm not understanding the error at all. Thank you in advance!

FeedCellViewModel

import Foundation

class FeedCellViewModel: ObservableObject {
    @Published var posts = [Post]()
    let service = PostService()
    let authViewModel = AuthViewModel()
    
    init() {
        fetchPosts()
    }
    
    func fetchPosts() {
        service.fetchPosts { posts in
            self.posts = posts
            
            for i in 0 ..< posts.count {
                let uid = posts[i].uid
                
                self.authViewModel.fetchUser() { user in
                    self.posts[i].user = user
               }
            }
        }
    }
} 

AuthViewModel

import SwiftUI
import FirebaseAuth
import FirebaseCore
import FirebaseStorage
import FirebaseFirestore
import FirebaseFirestoreSwift

class AuthViewModel: NSObject, ObservableObject {
    @Published var userSession: FirebaseAuth.User?
    @Published var currentUser: User?
    @Published var selectedImage: UIImage?
    @Published var didAuthenticateUser = false
    private var tempUserSession: FirebaseAuth.User?

    
    private let service = UserService()
        
    static let shared = AuthViewModel()
    
    override init() {
        super.init()
        userSession = Auth.auth().currentUser
        
        fetchUser()
    }
    
    func login(withEmail email: String, password: String) {
        Auth.auth().signIn(withEmail: email, password: password) { result, error in
            if let error = error {
                print("DEBUG: Failed to sign in with error \(error.localizedDescription)")
                return
            }
            
            self.userSession = result?.user
            self.fetchUser()
        }
    }
    
    func register(withEmail email: String, password: String, fullname: String) {
        Auth.auth().createUser(withEmail: email, password: password) { result, error in
            if let error = error {
                print("DEBUG: Failed to register with error \(error.localizedDescription)")
                return
            }
            
            guard let user = result?.user else { return }
            self.tempUserSession = user

            
            let data = ["email": email,
                        "fullname": fullname,
                        "uid": user.uid]
            
            COLLECTION_USERS
                .document(user.uid)
                .setData(data) { _ in
                    self.didAuthenticateUser = true
                }
            
            self.uploadProfileImage(self.selectedImage)
            self.fetchUser()
        }
    }
    
    func signOut() {
        // sets user session to nil so we show login view
        self.userSession = nil
        
        // signs user out on server
        try? Auth.auth().signOut()
    }
    
    func uploadProfileImage(_ image: UIImage?) {
        guard let uid = tempUserSession?.uid else { return }

        ImageUploader.uploadImage(image: image) { profileImageUrl in
            Firestore.firestore().collection("users")
                .document(uid)
                .updateData(["profileImageUrl": profileImageUrl]) { _ in
                 self.userSession = self.tempUserSession
                }
        }
    }
    
    func fetchUser() {
        guard let uid = userSession?.uid else { return }
        
        COLLECTION_USERS.document(uid).getDocument { snapshot, _ in
            guard let user = try? snapshot?.data(as: User.self) else { return }
            self.currentUser = user
        }
    }
}

Upvotes: 2

Views: 12843

Answers (1)

Bradley Mackey
Bradley Mackey

Reputation: 7668

You're seeing this error because your fetchUser() function doesn't take a closure parameter (or any parameters for that matter).

A trailing closure is just a nicer way of passing a closure as a parameter, given it's the last parameter to a method. Try running this example in a playground to get a feel for this:

func hello(closure: () -> Void) {
  print("calling closure")
  closure()
  print("finished")
}

// these are the same
hello(closure: { print("hello!!") })
hello { print("hello!!") }

If you want to provide the user in a closure to the caller, return the user as a parameter to the closure in addition to setting the currentUser.

func fetchUser(finishedFetching: @escaping (User) -> Void) {
  guard let uid = userSession?.uid else { return }
        
  COLLECTION_USERS.document(uid).getDocument { snapshot, _ in
    guard let user = try? snapshot?.data(as: User.self) else { return }
    self.currentUser = user
    finishedFetching(user)
  }
}

Read more about closures in the Swift language guide. They are essentially unnamed functions that you can store and pass around. You'll learn there why I marked our closure as @escaping.

Upvotes: 5

Related Questions