I’ve been chasing my own tail for days.. Maybe the architecture is all wrong. I just can't get it all to work at the same time. Any help would be greatly appreciated.
I have a LoginView
which takes an email and password, and validates to the server.
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)
}
If validated, the above code does the following:
isLoggedIn
to true
which changes the view to MainView
via the following StartingView
:import SwiftUI
struct StartingView: View {
@EnvironmentObject var userAuth: UserAuth
var body: some View {
if !userAuth.isLoggedIn {
LoginView()
} else {
MainView()
}
}
}
AppData().getData(userId: userAuth.userId)
to the server for data.Here is the AppData
class that the above API is pointing to.
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
}
}
}
And here is MainView
where I want to display the data
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)
}
}
}
}
}
Issues that I am experiencing:
I am able to print(apiData)
and see the data, however @Published var userData
and self.userData = apiData
are not making the data available on MainView
via @ObservedObject var profileData = AppData()
getData()
is not getting triggered every 60 seconds with the Timer
because I am not able to figure out how to pass the (userId: userAuth.userId)
parameter in there.
I appreciate any direction at all. If this is not set-up in an ideal way, please tell me, I want to do this correctly.
Thank you!
The instance of AppData().getData(userId: userAuth.userId)
in LoginView
is not the same as @ObservedObject var profileData = AppData()
.
The ObservedObject
never sees what the LoginView
one is doing.
You have to share the instance by either using the SwiftUI wrappers like you have with UserAuth
or a singleton (less recommended).
class Singleton {
static let sharedInstance = Singleton()
}
Also, what is AppDataModel
? is it an ObservableObject
? You can't chain them.
If so, these changes are userData.appTopLine = "not working"
are not being observed. You won't see them.