Как я могу вернуть объект, созданный в своем классе службы данных, через Firebase?

Я пытаюсь придерживаться практики MVC и хранить весь сетевой код внутри класса службы данных, который я использую в своем приложении. На одном экране у меня есть имя пользователя и имя пользователя, которое нужно отобразить. При обновлении этого я вызываю эту функцию:

func grabUserData() -> User {
    REF_USERS.child(getCurrentUID()).observeSingleEvent(of: .value) { 
        (snapshot) in
        if let userDict = snapshot.value as? Dictionary<String, String> {
            let user = User(
                first:    userDict["firstName"]!, 
                last:     userDict["lastName"]!, 
                username: userDict["username"]!
            )
            return user
        } 
    }
}

Но я получаю сообщение об ошибке, пытаясь вернуть пользователя! В нем говорится:

Неожиданное непустое возвращаемое значение в функции void.

Но функция явно не пустая. Итак, что мне делать?


person Cody Lucas    schedule 29.05.2017    source источник


Ответы (1)


Вы путаете возвращаемое значение grabUserData функции с возвращаемым значением замыкания Firebase — первое — User, а второе — Void ;)

На самом деле вы возвращаетесь из этого замыкания — сейчас я использую явный тип возврата, чтобы было понятно:

{ 
    (snapshot) -> Void in
    if let userDict = snapshot.value as? Dictionary<String,String> {
        let user = User(
            first:    userDict["firstName"]!, 
            last:     userDict["lastName"]!, 
            username: userDict["username"]!
        )
        return user
    }
}

который передается в качестве последнего аргумента функции observeSingleEvent Firebase. Это очень распространенная ошибка ;)

Обработчик завершения. Стандартным шаблоном здесь является возврат желаемого User через обработчик завершения. Это решение прекрасно моделирует асинхронный характер сетевых запросов, таких как вызовы базы данных Firebase. Например:

func grabUserData(completion: @escaping (User?) -> Void) {
    REF_USERS.child(getCurrentUID()).observeSingleEvent(of: .value) { 
        (snapshot) in
        if let userDict = snapshot.value as? Dictionary<String, String> {
            let user = User(
                first:    userDict["firstName"]!, 
                last:     userDict["lastName"]!, 
                username: userDict["username"]!
            )
            completion(user) // Returns user!
        } else {
            completion(nil) // User not found!
        }
    }
}

Наконец, в клиентском коде службы данных вызовите ее следующим образом:

grabUserData() { 
    (user) in
    if let user = user {
        print("Grabbed user: \(user)")
    } else {
        print("User not found!")
    }        
}
person Paulo Mattos    schedule 29.05.2017
comment
Просто хотел сказать спасибо - я только что реализовал его, и он отлично работает. Единственное предложение Xcode состояло в том, чтобы добавить @escaping к обработчику завершения, потому что он должен избежать закрытия. Спасибо, Пауло! - person Cody Lucas; 30.05.2017