SwiftUI, получите результат из сообщения URLSession и сохраните его в переменной

#swift #api #session #swiftui #request

Вопрос:

Я хотел бы подключить пользователя к Apple SignIn, сохранить его информацию в базе данных и сохранить информацию, возвращенную с обратной стороны], в переменной и получить к ним доступ в SwiftUI.

Я попробовал несколько вещей, и у меня есть несколько более или менее правильных результатов.

Прежде всего, моя модель пользователя :

 struct User: Codable {
    let firstname: String
    let lastname: String
    let email: String
}
 

У меня есть моя первая функция, которая позволяет мне извлекать информацию о пользователе и форматировать ее :

 func handle(_ signInWithAppleResult: SignInWithAppleResult, completion: @escaping (Result<Bool, Error>) -> ()) {
        
        @ObservedObject var userVM = UserViewModel()
        
        let uid = signInWithAppleResult.authDataResult.user.uid
        
        let fullName = signInWithAppleResult.appleIDCredential.fullName
        let firstname = fullName?.givenName ?? ""
        let lastname = fullName?.familyName ?? ""
        let email = signInWithAppleResult.authDataResult.user.email ?? ""
        
        let user = User(firstname: firstname, lastname: lastname, email: email)
        
        userVM.postUser(user: user) { result in
            completion(result)
        }
    }

 

После этого я вызываю функцию postUser, которая позволит мне вызвать функцию POST и поместить возврат back в мою пользовательскую переменную :

 class UserViewModel: ObservableObject {
    @Published private var user: User?

    var email: String {
        guard let email = user?.email else {
            return ""
        }
        return email
    }

    func postUser(user: User, completion: @escaping (Result<Bool, Error>) -> ()) {
        UserService().postUser(user: user) { result in
            switch result {
            case .success(let user):
                DispatchQueue.main.async {
                    self.user = user
                    print("user", user as Any)
                    completion(.success(true))
                }
            case .failure(_ ):
                print("Error")
            }
        }
    }
}
 

Переменная электронной почты позволяет мне получить переменную электронной почты, содержащуюся в объекте пользователя (обычно).

А вот и последний фрагмент кода, позволяющий сделать POST-запрос.

 class UserService {

func postUser(user: User, completion: @escaping(Result<User?, NetworkError>)-> Void) {
        
        guard let url = URL.apiURL(endpoint: "/userRegistration") else {
            return completion(.failure(.BadURL))
        }
        
        // MARK: URLRequest
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.addValue("application/json", forHTTPHeaderField: "Content-Type")
        
        //MARK: POST Request V.2.0
        let user = [
            "firstname" : user.firstname,
            "lastname" : user.lastname,
            "email": user.email
        ]
        
        guard let httpBody = try? JSONSerialization.data(withJSONObject: user, options: []) else {
            return completion(.failure(.UnableToSerialize))
        }
        
        // MARK: Set httpBody
        request.httpBody = httpBody
        
        URLSession.shared.dataTask(with: request) { data, response, error in
            guard let data = data, error == nil else {
                return completion(.failure(.NoData))
            }
            let userResponse = try? JSONDecoder().decode(User.self, from: data)
            if let userResponse = userResponse {
                print("userResponse", userResponse)
                completion(.success(userResponse))
            } else {
                completion(.failure(.DecodingError))
            }
        }.resume()
    }

}
 

Here is the result of userResponse (Decoded JSON) :

User(firstname: «Hello», lastname: «World», email: «helloworld@privaterelay.appleid.com»)

For the moment, all is well, the request registers well the information in base. However, the function of the second file :

(func postUser(user: User, completion: @escaping (Result<Bool, Error>) -> ())

return me that :

user Optional(my_project.User(firstname: «hello», lastname: «world», email: «helloworld@privaterelay.appleid.com»))

And in my SwiftUI View, i’m not able to access to this object, the result is nil, and i don’t know why

 struct Home: View {
    
    @ObservedObject private var userVM = UserViewModel()
    @AppStorage("log_status") var log_Status = false

    var body: some View {
        VStack {
            Text("Hello : (self.userVM.email)")
                .foregroundColor(.white)
            Spacer()
            Button(action: {
                DispatchQueue.global(qos: .background).async {
                    try? Auth.auth().signOut()
                }
                withAnimation(.easeInOut) {
                    log_Status = false
                }
            }, label: {
                Text("Log Out")
                    .fontWeight(.semibold)
                    .foregroundColor(.white)
                    .padding(.vertical, 12)
                    .frame(width: UIScreen.main.bounds.width / 2)
                    .clipShape(Capsule())
            })
        }
    }
}
 

Я не упоминал свой API, он возвращает объект :

 result
    {
        user_id: 1,
        firstname: 'hello',
        lastname: 'world',
        email: 'helloworld@privaterelay.appleid.com'
    }
 

Но я хотел бы, чтобы мой API возвращал массив, даже если он содержит один единственный объект и имеет тот же результат, и на данный момент, если это массив одного объекта, он вызывает эту ошибку, и ничего не работает :

     case .failure(_ ):
        print("Error")
    }
 

Находится во втором файле.

Я провел много исследований, но я действительно не понимаю, почему это не работает, и я хотел бы получить некоторую помощь, если это возможно, спасибо.

Комментарии:

1. Это немного сложно понять, но я думаю , что одна из проблем, с которой вы столкнулись, является результатом наличия нескольких экземпляров UserViewModel . Каждый раз , когда вы вводите UserViewModel() текст, вы создаете новый экземпляр . Если вы хотите, чтобы несколько представлений/объектов имели доступ к @Published свойству одного экземпляра, вам нужно передать ссылку на этот экземпляр UserViewModel , а не создавать новый. Как это сделать в вашем коде, мне неясно, потому что я не уверен, как различные части, которые вы включили, сочетаются друг с другом.

2.Кроме того, наличие @ObservedObject var userVM = UserViewModel() внутри функции не имеет смысла-оболочка @ObservedObject свойств используется в a View для указания представлению реагировать на обновления.

3. @jnpdx Спасибо за ваш ответ, я только что последовал этому уроку : bathicodes.medium.com/… и, похоже, это работает для выборки