#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
свойств используется в aView
для указания представлению реагировать на обновления.3. @jnpdx Спасибо за ваш ответ, я только что последовал этому уроку : bathicodes.medium.com/… и, похоже, это работает для выборки