SwiftUI 使用计时器每 60 秒发布 API 数据

SwiftUI publishing API data every 60 seconds with a Timer

我这几天一直在追自己的尾巴。。可能是架构全错了。我只是不能让所有的东西同时工作。任何帮助将不胜感激。

我有一个 LoginView,它接受电子邮件和密码,并向服务器验证。

import SwiftUI
struct LoginView: View {

    @EnvironmentObject var userAuth: UserAuth
    @State private var email: String = ""
    @State private var password: String = ""

    var body: some View {
      TextField("Email Address", text: $email)
      SecureField("Password", text: $password)
      Button(action: {
        guard let url = URL(string: "https://www.SomeLoginApi.com/login") else { return }
        let body: [String: String] = ["emailAddress": email, "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, _, _) in
          guard let data = data else { return }
          let loginResponse = try! JSONDecoder().decode(ServerResponse.self, from: data)
          if loginResponse.message == "authorized" {
            DispatchQueue.main.async {
                self.userAuth.isLoggedIn = true
                self.userAuth.userId = loginResponse.userId
                AppData().getData(userId: userAuth.userId)
            }
          } else {
            var isLoggedin = false 
          }
        }
        .resume()
      }) {
        Text("LOGIN")
      }
      .disabled(email.isEmpty || password.isEmpty)
    }

如果经过验证,上面的代码将执行以下操作:

  1. isLoggedIn 设置为 true,通过以下 StartingView:
  2. 将视图更改为 MainView
import SwiftUI
struct StartingView: View {

    @EnvironmentObject var userAuth: UserAuth

    var body: some View {
        if !userAuth.isLoggedIn {
            LoginView()
        } else {
            MainView()
        }
    }
}
  1. 向服务器发送第二次 API 调用 AppData().getData(userId: userAuth.userId) 以获取数据。

这里是上面的API指向的AppDataclass

import Foundation
import SwiftUI
import Combine
import CoreImage
import CoreImage.CIFilterBuiltins

class AppData : ObservableObject {
    @Published var userData: AppDataModel = AppDataModel(data1: "", data2: "", data3: "", data4: "", data5: "", data6: "", data7: "", bool1: false, data8: "", data9: "", bool2: true, bool3: true, bool4: true, bool5: true, bool6: true, data10: "", data11: "", data12: "", data13: "", array1:[], array2: [], array3: [], array4: [], array5: [], array6: [])
    @Published var time = ""
    @Published var greet = ""
    @Published var bgImage = Image.init("")
    
    init() {
        var greetingTimer: Timer?
        greetingTimer = Timer.scheduledTimer(timeInterval: 60.0, target: self, selector: #selector(getData), userInfo: nil, repeats: true)
    }
    
    @objc func getData(userId: String) {
        let bgImgArr = ["appBackAnimals1", "appBackAnimals2", "appBackAnimals3", "appBackAnimals4", "appBackAnimals5", "appBackAnimals6", "appBackAnimals7", "appBackAnimals8", "appBackAnimals9", "appBackAnimals10", "appBackAnimals11", "appBackAnimals12", "appBackAnimals13"]
        let bgImg = bgImgArr.randomElement()!
        guard let inputImage = UIImage(named: bgImg) else { return }
        let beginImage = CIImage(image: inputImage)
        let context = CIContext()
        let currentFilter = CIFilter.vignette()
        currentFilter.inputImage = beginImage
        currentFilter.intensity = 6
        // get a CIImage from our filter or exit if that fails
        guard let outputImage = currentFilter.outputImage else { return }
        // attempt to get a CGImage from our CIImage
        if let cgimg = context.createCGImage(outputImage, from: outputImage.extent) {
            // convert that to a UIImage
            let uiImage = UIImage(cgImage: cgimg)
            // and convert that to a SwiftUI image
            self.bgImage = Image(uiImage: uiImage)
        }
        let today = Date()
        let formatter = DateFormatter()
        formatter.dateFormat = "EEEE h:mma"
        formatter.amSymbol = "am"
        formatter.pmSymbol = "pm"
        let calendar = Calendar.current
        let hour = calendar.component(.hour, from: today)
        time = formatter.string(from: today)

        if hour >= 5 && hour <= 11 {
            greet = "morning"
        } else if hour >= 12 && hour <= 17 {
            greet = "afternoon"
        } else if hour >= 18 && hour <= 20 {
            greet = "evening"
        } else if hour >= 21 && hour <= 24 {
            greet = "night"
        } else if hour >= 0 && hour <= 4 {
            greet = "night"
        } 

        guard let url = URL(string: "https://www.SomeDataApi.com/data") else { return }
        let body: [String: String] = ["userId": userId]
        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, _, _) in
            guard let data = data else { return }
            let apiData = try! JSONDecoder().decode(AppDataModel.self, from: data)
            if apiData.message == "data" {
                DispatchQueue.main.async {
                    self.userData = apiData
                }
            }
        }
        .resume()
        if userData.appTopLine == "" {
            userData.appTopLine = "Good " + greet + " " + userData.appName
        } else {
            userData.appTopLine = "not working"
        }
        if userData.appBottomLine == "" {
            userData.appBottomLine = "It's " + time
        }
    }
}

这里是 MainView 我想显示数据的地方

import SwiftUI

struct MainView: View {

  @ObservedObject var profileData = AppData()
  @EnvironmentObject var userAuth: UserAuth
  
  var body: some View {
    ZStack {
      profileData.bgImage
      HStack {
        VStack(alignment: .leading) {
            Text(profileData.userData.appTopLine)
            Text(profileData.userData.appBottomLine)
        }
      }
    }
  }
}

我遇到的问题:

  1. 我能够 print(apiData) 并查看数据,但是 @Published var userDataself.userData = apiData 无法通过 MainView 提供数据=27=]

  2. getData() 不会用 Timer 每 60 秒触发一次,因为我不知道如何在其中传递 (userId: userAuth.userId) 参数。

我非常感谢任何指导。如果这不是 set-up 理想的方式,请告诉我,我想正确地做到这一点。

谢谢!

LoginViewAppData().getData(userId: userAuth.userId)的实例与@ObservedObject var profileData = AppData()不同。

ObservedObject 永远看不到 LoginView 在做什么。

您必须像使用 UserAuthsingleton(不太推荐)那样使用 SwiftUI 包装器来共享实例。

class Singleton {
    static let sharedInstance = Singleton()
}

另外,AppDataModel是什么?是 ObservableObject 吗?你不能链接它们。

如果是,则这些变化 userData.appTopLine = "not working" 没有被观察到。你不会看到它们。

@Published var timeElapsed = false

func delayText() {
        // Delay of 7.5 seconds
        DispatchQueue.main.asyncAfter(deadline: .now() + 7.5) {
            self.timeElapsed = true
        }
    }

那是一个定时器,希望对你有帮助。这对我有用,希望对你也有用。