Swift - 将 json 数据传递给其他视图
Swift - Pass json data to other views
我想弄清楚如何传递我从成功结果中收到的 json 解码数据,以便我可以开始使用它在另一个屏幕视图上显示特定信息。我一直在尝试解决这个问题,但运气不佳,我是 swift 以及应用程序开发的新手,所以这对我来说都是一次学习经历。如果有人可以提供帮助,我们将不胜感激。这是我的一些网络代码
func request<T: Decodable>(endPoint: EndPoint, method: Method, parameters: [String: Any]? = nil, completion: @escaping(Result<T, Error>) -> Void) {
// Creates a urlRequest
guard let request = createRequest(endPoint: endPoint, method: method, parameters: parameters) else {
completion(.failure(AppError.invalidUrl))
return
}
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
var results: Result<Data, Error>?
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
completion(.failure(AppError.badStatusCode))
return
}
if let response = response {
// Gets the JSESSIONID
let cookieName = "JSESSIONID"
if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { [=12=].name == cookieName }) {
debugPrint("\(cookieName): \(cookie.value)")
}
print(response)
}
// Look into this
if let data = data {
results = .success(data)
/*
// Converts data to readable String
let responseString = String(data: data, encoding: .utf8) ?? "unable to convert to readable String"
print("Server Response: \(responseString.description)")
*/
} else if let error = error {
print("NO this happen")
results = .failure(error)
print("Server Error: \(error.localizedDescription)")
}
DispatchQueue.main.sync {
self.handleResponse(result: results, completion: completion)
}
}.resume()
}
private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
guard let result = result else {
completion(.failure(AppError.unknownError))
return
}
switch result {
case .success(let data):
/*
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Server JsonObject response: \(json)")
} catch {
completion(.failure(AppError.errorDecoding))
}*/
let decoder = JSONDecoder()
// Decodes that json data
do {
let json = try decoder.decode(T.self, from: data)
completion(.success(json))
} catch {
completion(.failure(error))
}
case .failure(let error):
print("This happen")
completion(.failure(error))
}
}
这是我用来创建请求的函数
func signIn(username: String, password: Any, completion: @escaping(Result<LoginResponseData.Root, Error>) -> Void) {
let params = ["username": "\(username)", "password": "\(password)"]
request(endPoint: .Login, method: .post, parameters: params, completion: completion)
}
这是我的登录查看码
struct SignIn: View {
@Binding var userID: String
@Binding var passcode: String
@State private var showAlert = false
@EnvironmentObject var authentication: AuthenticationCheck
var body: some View {
Button(action: {
// Remove
print("Button action")
NetworkService.shared.signIn(username: userID, password: passcode) { (result) in
switch result {
case .success(let user):
print("This user last name is: \(user.result.login.userName.name.fullName)")
authentication.updateValidation(success: true)
showAlert = false
case .failure(let error):
print("The error is: \(error.localizedDescription)")
showAlert.toggle()
}
}
}) {
Text("Sign In")
.multilineTextAlignment(.center)
.padding()
}
.frame(width: 150.0, height: 43.0)
.background(/*@START_MENU_TOKEN@*//*@PLACEHOLDER=View@*/Color(red: 0.584, green: 0.655, blue: 0.992)/*@END_MENU_TOKEN@*/)
.foregroundColor(.white)
.cornerRadius(20)
.disabled(userID.isEmpty || passcode.isEmpty)
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Invalid Credentials"), message: Text("Either username or password is incorrect. Please try again"), dismissButton: .cancel())
})
}
这是我的身份验证和应用程序
class AuthenticationCheck: ObservableObject {
@Published var isValidated = false
func updateValidation(success: Bool) {
withAnimation {
isValidated = success
}
}
}
@main
struct SomeApp: App {
@StateObject var authenticationCheck = AuthenticationCheck()
var body: some Scene {
WindowGroup {
if authenticationCheck.isValidated {
ContentView()
.environmentObject(authenticationCheck)
} else {
Signin()
.environmentObject(authenticationCheck)
}
//TestView()
}
}
}
这是您的代码的简化版本
class AuthViewModel: ObservableObject {
// I don't have your Model info
//You can have an empty object of your model or you can make it an optional, etc
@Published var user: YourModel? = nil
@Published var alert: CustomAlert? = nil
let networkService: NetworkService = NetworkService.shared
var authentication: AuthenticationCheck? = nil
func signIn(username: String, password: String) {
networkService.signIn(username: username, password: password){ (result) in
switch result {
case .success(let user):
print("This user last name is: \(user)")
//Assign you value to the Published variable here
self.user = user
self.authentication?.updateValidation(success: true)
self.alert = CustomAlert(title: "Success", message: "You logged in")
case .failure(let error):
print("The error is: \(error)")
//Reset the variable
self.user = nil
self.authentication?.updateValidation(success: false)
//You can pass a better message to the user like this
self.alert = CustomAlert(title: "Invalid Credentials", message: "\(error)")
}
}
}
func logout() {
self.user = nil
authentication?.updateValidation(success: false)
self.alert = CustomAlert(title: "See you later", message: "You logged out")
}
}
class AuthenticationCheck: ObservableObject {
@Published var isValidated = false
func updateValidation(success: Bool) {
withAnimation {
isValidated = success
}
}
}
struct SampleNetworkView: View {
@StateObject var vm: AuthViewModel = AuthViewModel()
@StateObject var authentication: AuthenticationCheck = AuthenticationCheck()
@State var username: String = ""
@State var password: String = ""
var body: some View {
NavigationView{
switch authentication.isValidated{
case true:
VStack{
Text("Signed In - you are now in the content view")
.toolbar(content: {
Button("log out", action: {
vm.logout()
})
})
}
case false:
VStack{
TextField("username", text: $username).textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("password", text: $password).textFieldStyle(RoundedBorderTextFieldStyle())
Button("sign in", action: {
vm.signIn(username: username, password: password)
}).disabled(username.isEmpty || password.isEmpty)
}
}
}
//Inject the StateObjects to the navigation view so you can access the variables
.environmentObject(authentication)
.environmentObject(vm)
//Create a shared Alert for the ViewModel
.alert(item: $vm.alert, content: { customAlert in
Alert(title: Text(customAlert.title), message: Text(customAlert.message), dismissButton: .default(Text("ok")))
})
//Pass the authentication to the ViewModel so you can pass info
.onAppear(perform: {
vm.authentication = authentication
})
}
}
struct SampleNetworkView_Previews: PreviewProvider {
static var previews: some View {
SampleNetworkView()
}
}
//This assists in creating a shared alert
struct CustomAlert : Identifiable {
let id: UUID = UUID()
var title: String
var message: String
}
我想弄清楚如何传递我从成功结果中收到的 json 解码数据,以便我可以开始使用它在另一个屏幕视图上显示特定信息。我一直在尝试解决这个问题,但运气不佳,我是 swift 以及应用程序开发的新手,所以这对我来说都是一次学习经历。如果有人可以提供帮助,我们将不胜感激。这是我的一些网络代码
func request<T: Decodable>(endPoint: EndPoint, method: Method, parameters: [String: Any]? = nil, completion: @escaping(Result<T, Error>) -> Void) {
// Creates a urlRequest
guard let request = createRequest(endPoint: endPoint, method: method, parameters: parameters) else {
completion(.failure(AppError.invalidUrl))
return
}
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
var results: Result<Data, Error>?
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
completion(.failure(AppError.badStatusCode))
return
}
if let response = response {
// Gets the JSESSIONID
let cookieName = "JSESSIONID"
if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { [=12=].name == cookieName }) {
debugPrint("\(cookieName): \(cookie.value)")
}
print(response)
}
// Look into this
if let data = data {
results = .success(data)
/*
// Converts data to readable String
let responseString = String(data: data, encoding: .utf8) ?? "unable to convert to readable String"
print("Server Response: \(responseString.description)")
*/
} else if let error = error {
print("NO this happen")
results = .failure(error)
print("Server Error: \(error.localizedDescription)")
}
DispatchQueue.main.sync {
self.handleResponse(result: results, completion: completion)
}
}.resume()
}
private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
guard let result = result else {
completion(.failure(AppError.unknownError))
return
}
switch result {
case .success(let data):
/*
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Server JsonObject response: \(json)")
} catch {
completion(.failure(AppError.errorDecoding))
}*/
let decoder = JSONDecoder()
// Decodes that json data
do {
let json = try decoder.decode(T.self, from: data)
completion(.success(json))
} catch {
completion(.failure(error))
}
case .failure(let error):
print("This happen")
completion(.failure(error))
}
}
这是我用来创建请求的函数
func signIn(username: String, password: Any, completion: @escaping(Result<LoginResponseData.Root, Error>) -> Void) {
let params = ["username": "\(username)", "password": "\(password)"]
request(endPoint: .Login, method: .post, parameters: params, completion: completion)
}
这是我的登录查看码
struct SignIn: View {
@Binding var userID: String
@Binding var passcode: String
@State private var showAlert = false
@EnvironmentObject var authentication: AuthenticationCheck
var body: some View {
Button(action: {
// Remove
print("Button action")
NetworkService.shared.signIn(username: userID, password: passcode) { (result) in
switch result {
case .success(let user):
print("This user last name is: \(user.result.login.userName.name.fullName)")
authentication.updateValidation(success: true)
showAlert = false
case .failure(let error):
print("The error is: \(error.localizedDescription)")
showAlert.toggle()
}
}
}) {
Text("Sign In")
.multilineTextAlignment(.center)
.padding()
}
.frame(width: 150.0, height: 43.0)
.background(/*@START_MENU_TOKEN@*//*@PLACEHOLDER=View@*/Color(red: 0.584, green: 0.655, blue: 0.992)/*@END_MENU_TOKEN@*/)
.foregroundColor(.white)
.cornerRadius(20)
.disabled(userID.isEmpty || passcode.isEmpty)
.alert(isPresented: $showAlert, content: {
Alert(title: Text("Invalid Credentials"), message: Text("Either username or password is incorrect. Please try again"), dismissButton: .cancel())
})
}
这是我的身份验证和应用程序
class AuthenticationCheck: ObservableObject {
@Published var isValidated = false
func updateValidation(success: Bool) {
withAnimation {
isValidated = success
}
}
}
@main
struct SomeApp: App {
@StateObject var authenticationCheck = AuthenticationCheck()
var body: some Scene {
WindowGroup {
if authenticationCheck.isValidated {
ContentView()
.environmentObject(authenticationCheck)
} else {
Signin()
.environmentObject(authenticationCheck)
}
//TestView()
}
}
}
这是您的代码的简化版本
class AuthViewModel: ObservableObject {
// I don't have your Model info
//You can have an empty object of your model or you can make it an optional, etc
@Published var user: YourModel? = nil
@Published var alert: CustomAlert? = nil
let networkService: NetworkService = NetworkService.shared
var authentication: AuthenticationCheck? = nil
func signIn(username: String, password: String) {
networkService.signIn(username: username, password: password){ (result) in
switch result {
case .success(let user):
print("This user last name is: \(user)")
//Assign you value to the Published variable here
self.user = user
self.authentication?.updateValidation(success: true)
self.alert = CustomAlert(title: "Success", message: "You logged in")
case .failure(let error):
print("The error is: \(error)")
//Reset the variable
self.user = nil
self.authentication?.updateValidation(success: false)
//You can pass a better message to the user like this
self.alert = CustomAlert(title: "Invalid Credentials", message: "\(error)")
}
}
}
func logout() {
self.user = nil
authentication?.updateValidation(success: false)
self.alert = CustomAlert(title: "See you later", message: "You logged out")
}
}
class AuthenticationCheck: ObservableObject {
@Published var isValidated = false
func updateValidation(success: Bool) {
withAnimation {
isValidated = success
}
}
}
struct SampleNetworkView: View {
@StateObject var vm: AuthViewModel = AuthViewModel()
@StateObject var authentication: AuthenticationCheck = AuthenticationCheck()
@State var username: String = ""
@State var password: String = ""
var body: some View {
NavigationView{
switch authentication.isValidated{
case true:
VStack{
Text("Signed In - you are now in the content view")
.toolbar(content: {
Button("log out", action: {
vm.logout()
})
})
}
case false:
VStack{
TextField("username", text: $username).textFieldStyle(RoundedBorderTextFieldStyle())
SecureField("password", text: $password).textFieldStyle(RoundedBorderTextFieldStyle())
Button("sign in", action: {
vm.signIn(username: username, password: password)
}).disabled(username.isEmpty || password.isEmpty)
}
}
}
//Inject the StateObjects to the navigation view so you can access the variables
.environmentObject(authentication)
.environmentObject(vm)
//Create a shared Alert for the ViewModel
.alert(item: $vm.alert, content: { customAlert in
Alert(title: Text(customAlert.title), message: Text(customAlert.message), dismissButton: .default(Text("ok")))
})
//Pass the authentication to the ViewModel so you can pass info
.onAppear(perform: {
vm.authentication = authentication
})
}
}
struct SampleNetworkView_Previews: PreviewProvider {
static var previews: some View {
SampleNetworkView()
}
}
//This assists in creating a shared alert
struct CustomAlert : Identifiable {
let id: UUID = UUID()
var title: String
var message: String
}