5

我需要向登录视图 (SwiftUI) 发布一个 http 请求,我的代码遵循我在 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 类刷新。

这部分代码不起作用:

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

“认证”仍然是假的。

我该如何解决它,谢谢。

4

2 回答 2

9

虽然接受的答案符合目标,但我认为这不是最好的方法,因为它实现@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()
    }
}

内容视图.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()
        }
    }
}
于 2019-11-11T15:29:23.443 回答
1

你可以试试这个:

代替

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)

在类之外的某个地方作为全局变量

var manager = HttpAuth()

那么它应该可以工作。

于 2019-11-10T12:31:20.980 回答