SwiftUI http 请求

SwiftUI http request

我需要 post 一个 http 请求来登录视图 (SwiftUI),我的代码如下 我在 HttpAuth.swift:

import Foundation
import Combine

struct ServerMessage: Decodable {
   let res, message: String
}

class HttpAuth: ObservableObject {

  let objectWillChange = PassthroughSubject<HttpAuth, Never>()

  var authenticated = false {
      didSet {
           objectWillChange.send(self)
      }
   }

    func postAuth(username: String, password: String) {
    guard let url = URL(string: "http://mysite/loginswift.php") else { return }

    let body: [String: String] = ["username": username, "password": password]

    let finalBody = try! JSONSerialization.data(withJSONObject: body)

    var request = URLRequest(url: url)
    request.httpMethod = "POST"
    request.httpBody = finalBody
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")

    URLSession.shared.dataTask(with: request) { (data, response, error) in

        guard let data = data else { return }

        let resData = try! JSONDecoder().decode(ServerMessage.self, from: data)

        print(resData.res)

        if resData.res == "correct" {
            DispatchQueue.main.async {
                self.authenticated = true
            }

        }

    }.resume()

  }

}

在ContentView.swift中:

import SwiftUI

struct ContentView: View {

@State private var username: String = ""
@State private var password: String = ""

@State var manager = HttpAuth()


var body: some View {

    VStack(alignment: .leading) {

        if self.manager.authenticated {
            Text("Correct!").font(.headline)
        }


        Text("Username")
        TextField("placeholder", text: $username)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .border(Color.green)
            .autocapitalization(.none)

        Text("Password")
        SecureField("placeholder", text: $password)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .border(Color.green)

        Button(action: {

            self.manager.postAuth(username: self.username, password: self.password)

        }) {
            HStack{
                Spacer()
                Text("Login")
                Spacer()
            }
            .accentColor(Color.white)
            .padding(.vertical, 10)
            .background(Color.red)
            .cornerRadius(5)
            .padding(.horizontal, 40)

        }

    }.padding()

   }
  }

  struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
   }

有效,我从服务器收到了答复,但它没有更新 ContentView,ContentView 中的 'self.manager.authenticated' 没有从 HttpAuth class.

刷新

这部分代码不起作用:

if self.manager.authenticated {
        Text("Correct!").font(.headline)
    }

'authenticated'还是假的。

请问如何解决,谢谢。

你可以试试这个:

而不是

let objectWillChange = PassthroughSubject<HttpAuth, Never>()

  var authenticated = false {
      didSet {
           objectWillChange.send(self)
      }
   }

使用

@published var authenticated = false

而不是

@State var manager = HttpAuth()

使用

@EnvironmentObject private var manager: HttpAuth

当然在调用 ContentView 时这样做:

ContentView().environmentObject(manager)

和 class 之外的某个地方作为全局变量 do

var manager = HttpAuth()

那么应该可以了。

虽然接受的答案符合 objective 我不认为这是最好的方法,因为它实现了 @EnvironmentObject 并且它应该用于全局应用程序问题而不是特定的请求查看。

您可以实现特定于视图的 "ViewModel",以完成请求的工作并更新变量。

文档:https://developer.apple.com/documentation/combine/observableobject

根据@Chris 建议的更改,您的代码的实现如下所示:

这适用于 Xcode 版本 11.2 (11B52)

HttpAuth.swif:

import Foundation
import SwiftUI
import Combine

class HttpAuth: ObservableObject {

    @Published var authenticated = false

    func postAuth(username: String, password: String) {
        guard let url = URL(string: "http://mysite/loginswift.php") else { return }

        let body: [String: String] = ["username": username, "password": password]

        let finalBody = try! JSONSerialization.data(withJSONObject: body)

        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.httpBody = finalBody
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")

        URLSession.shared.dataTask(with: request) { (data, response, error) in
            guard let data = data else { return }
            let resData = try! JSONDecoder().decode(ServerMessage.self, from: data)
            print(resData.res)
            if resData.res == "correct" {
                DispatchQueue.main.async {
                    self.authenticated = true
                }
            }
        }.resume()
    }
}

ContentView.swift

import SwiftUI

struct ContentView: View {

    @State private var username: String = ""
    @State private var password: String = ""

    @ObservedObject var manager = HttpAuth()

    var body: some View {

        VStack(alignment: .leading) {

            if self.manager.authenticated {
                Text("Correct!").font(.headline)
            }

            Spacer()

            Text("Username")
            TextField("placeholder", text: $username)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .border(Color.green)
                .autocapitalization(.none)

            Text("Password")
            SecureField("placeholder", text: $password)
                .textFieldStyle(RoundedBorderTextFieldStyle())
                .border(Color.green)

            Button(action: {
                self.manager.postAuth(username: self.username, password: self.password)
            }) {
                HStack{
                    Spacer()
                    Text("Login")
                    Spacer()
                }
                .accentColor(Color.white)
                .padding(.vertical, 10)
                .background(Color.red)
                .cornerRadius(5)
                .padding(.horizontal, 40)

            }
            Spacer()
        }.padding()
    }

    struct ContentView_Previews: PreviewProvider {
        static var previews: some View {
            ContentView()
        }
    }
}