I am working on a swiftui app where I am getting the sign in data from the user when they login. But for some reason the data that I save to the variable is just not saving. The data get pulled from the api and when the Post function is running the data is there but as soon as the function finishes the data gets turned to nil.
#Published var userData : SignInData?
#Published var signInSuccess: Bool = false
func signIn(bodyData:Any, completion: #escaping((Error?, SignInData?) -> Void)) { /
if let url = URL(string: signInURL) {
let session = URLSession.shared
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = try! JSONSerialization.data(withJSONObject: bodyData)
let task = session.dataTask(with: request) { (data, response, error) in
if error == nil {
let decoder = JSONDecoder()
if let safeData = data {
do {
let results = try decoder.decode(SignInData.self, from: safeData)
DispatchQueue.main.sync {
//print(results)
self.userData = results
completion(nil,results)
self.signInSuccess = true
}
} catch {
print(error)
completion(error,nil)
}
}
}
}
task.resume()
}
}
struct SignInData: Codable{
var Message : String?
var UID : String?
var Dob : String?
var FirstName : String?
var LastName : String?
var UserName : String?
var Status:String?
var Rank : String?
var Cell : String?
var Insider : String?
var Interests : String?
var Gender : String?
var Location : String?
var ParentsStatus : String?
var School : String?
var FB_username: String?
var FB_score : String?
var IG_username : String?
var IG_score : String?
var TW_username : String?
var Tw_score : String?
var Email: String?
var Balance : String?
}
This is where I call it in another Class
#ObservedObject var networkManager = NetworkManager()
if self.networkManager.userData?.Status == "0"{
print("the account is not activated yet")
}
else{
print("successful")
}
Related
class CartViewModel: ObservableObject {
#Published var cartItems = [Cart]()
#Published var errorMessage = ""
private var db = Firestore.firestore()
func fetchData() {
db.collection("customers").document(Auth.auth().currentUser!.uid).collection("cart").addSnapshotListener{(querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("no documents")
return
}
self.cartItems = documents.compactMap { (queryDocumentSnapshot) -> Cart? in
return try? queryDocumentSnapshot.data(as: Cart.self)
}
if let error = error {
self.errorMessage = error.localizedDescription
return
}
}
}}
struct Cart: Identifiable, Codable {
let db = Firestore.firestore()
#DocumentID var id: String?
var name: String
var details: String
var image: String
var price: Float
var quantity: Int
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
case details = "details"
case image = "image"
case price = "price"
case quantity = "quantity"
}}
This is the code for the struct and viewmodel. I tried following https://www.youtube.com/watch?v=3-yQeAf3bLE and replacing the Book with Cart. I am getting the following error in my CartViewModel "Type of expression is ambiguous without more context". The editor highlights the = in the self.cartItems = documents.compactMap
class CartViewModel: ObservableObject {
#Published var products: [Product] = []
#Published var error = ""
var db = Firestore.firestore()
func fetchData(){
db.collection("customers").document(Auth.auth().currentUser!.uid).collection("cart").getDocuments { (snap, err) in
guard let productData = snap?.documents else{return}
self.products = productData.compactMap{ queryDocumentSnapshot -> Product? in
return try? queryDocumentSnapshot.data(as: Product.self)
}
}
}
}
struct Product: Codable, Identifiable {
#DocumentID var id: String?
var product_name: String
var product_details: String
var product_image: String
var product_ratings: String
var product_size: String
var product_quantity: Int
var product_price: Int
}
This seems to be working correctly. Conforming to Codable. I don't know why but appending the items to a cartitem struct was not syncing with the database.
I'm back on a learning course in SwiftUI using CoreData. I have three entities:
User // Has many Customers
Customer // Belongs to User and has many PartExchanges
PartExchange // Belongs to customer
When user first installs the app after they logged in, I fetch some initial data to be saved (the above: customers, part exchnages etc...):
struct AuthResponse: Decodable {
let error: String?
let token: String?
let userData: UserObject?
let customers: [Customers]?
struct UserObject: Decodable {
let FirstName: String?
let Surname: String?
let EmailAddress: String?
let UserID: String
}
struct Customers: Decodable {
let FirstName: String?
let Surname: String?
let EmailAddress: String?
let Customer_ID: String
let PartExchanges: [PartExchangeData]?
}
}
// In another file and not inside AuthResponse
struct PartExchangeData: Decodable {
let Registration: String?
let Customer_ID: String?
let PartExchange_ID: String?
let Variant: String?
let Colour: String?
}
AuthResponse is only used when user first logs in or reinstalls the app to get the initial data from our API:
// The exact data I have
import SwiftUI
class AuthController {
var emailUsername: String = ""
var password: String = ""
func login() -> Void {
guard let url = URL(string: "http://localhost:4000/api/auth") else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let body: [String: AnyHashable] = [
"emailUsername": emailUsername,
"password": password
]
request.httpBody = try? JSONSerialization.data(withJSONObject: body, options: .fragmentsAllowed)
// Make the request
URLSession.shared.dataTask(with: request) { data, response, error in
if let data = data {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
if let decodedResponse = try?
decoder.decode(AuthResponse.self, from: data) {
DispatchQueue.main.async {
if decodedResponse.error != nil {
// Tell user?
return
}
let userObject = UserModel()
userObject.createUser(authObject: decodedResponse)
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
Last, the UserModel:
class UserModel: ObservableObject {
private let fetchRequest: NSFetchRequest<User> = User.fetchRequest()
private let viewContext = PersistenceController.shared.container.viewContext
#Published var saved: Bool = false
var firstName: String = ""
var surname: String = ""
var emailAddress: String = ""
var token: String = ""
var userId: String = ""
init() {...}
public func createUser(authObject: AuthResponse) -> Void {
do {
// Create a user on first login
let user = User(context: viewContext)
let customer = Customer(context: viewContext)
let partExchange = PartExchange(context: viewContext)
//let userCustomers: [AuthResponse.Customers]
user.firstName = authObject.userData!.FirstName
user.surname = authObject.userData!.Surname
user.emailAddress = authObject.userData!.EmailAddress
user.token = authObject.token!
user.userId = authObject.userData!.UserID
// Save customers
for cus in authObject.customers! {
customer.firstName = cus.FirstName
customer.surname = cus.Surname
user.addToCustomers(customer)
// save part exchanges
for px in cus.PartExchanges! {
partExchange.registration = px.Registration
partExchange.partExchangeId = px.PartExchange_ID
partExchange.variant = px.Variant
customer.addToPartExchanges(partExchange)
}
}
try viewContext.save()
saved = true
print("ALL SAVED!!")
} catch {
let error = error as NSError
// If any issues, rollback? viewContext.rollback()
fatalError("Could not save user: \(error)")
}
}
public func logOut() {
// Only remove the token....
}
}
The issue I'm having with this approach is when saving; it's saving the last customer in the loop.
Xcode generated some extensions for User, Customer and PartExchnage and inside User, I see a function: #NSManaged public func addToCustomers(_ values: NSSet):
[..]
user.addToCustomers(<what-goes-here>)
My User entity saves correctly. Customer only has the last data from the api array. How to correctly save the user with many customers, where the each customer has many part exchanges?
You need to create a new object for each iteration in each of your loops since each object created will be stored as a separate item in Core Data
So change createUser like this
public func createUser(authObject: AuthResponse) -> Void {
do {
let user = User(context: viewContext)
user.firstName = authObject.userData!.FirstName
// more properties ...
for cus in authObject.customers! {
let customer = Customer(context: viewContext)
customer.firstName = cus.FirstName
customer.surname = cus.Surname
user.addToCustomers(customer)
for px in cus.PartExchanges! {
let partExchange = PartExchange(context: viewContext)
partExchange.registration = px.Registration
partExchange.partExchangeId = px.PartExchange_ID
partExchange.variant = px.Variant
customer.addToPartExchanges(partExchange)
}
}
try viewContext.save()
saved = true
print("ALL SAVED!!")
} catch let error = error as NSError {
//Either log the error and return some status or throw it
//FatalError is a bit to much in this situation
fatalError("Could not save user: \(error)")
}
}
Here is my code:
import Foundation
import SwiftyJSON
import Alamofire
struct data {
var users : User?
init(json : JSON?) {
if let value = json?["data"].dictionaryObject {
let new = User(json: JSON(value))
self.users = new
}
}
}
struct User {
var userinfo : UserInfo?
init(json : JSON?) {
if let value = json?["user"].dictionaryObject {
let new = UserInfo(json: JSON(value))
self.userinfo = new
}
}
}
struct UserInfo {
var id:String?
var firstname: String?
var username: String?
var profilepicture: String?
var biography: String?
var lastname: String?
var gender: String?
var email: String?
var phone: String?
init(json : JSON?) {
self.id = json?["id"].string
self.firstname = json?["firstname"].string
self.username = json?["username"].string
self.profilepicture = json?["profilepicture"].string
self.biography = json?["biography"].string
self.lastname = json?["lastname"].string
self.gender = json?["gender"].string
self.email = json?["email"].string
self.phone = json?["phone"].string
}
}
Alamofire.request(urlVal, method: .get, parameters:nil, encoding: URLEncoding.queryString, headers: headers).responseJSON { (resp) in
switch resp.result {
case .success(let value):
let response = JSON(value)
print("Response JSON: \(response)")
let firstName = response["firstname"]
let newUser = data(json: response)
self.userData.append(newUser)
print(self.userData)
case .failure(let error):
print(error)
break
}
I want decodedResult variable assigns to exchangeInformations variable:
self.exchangeInformations = decodedResult
I created an instance of exchangeInfo() class. I try print the value of the exchangeInformations variable but I got nil. Why?
import Cocoa
class exchangeInfo {
var exchangeInformations: exchangeInformation? //Exchange information
let exchangeInformationURL: String = "https://api.binance.com/api/v3/exchangeInfo"
struct exchangeInformation: Codable {
var timezone: String //UTC
var serverTime: Int //1565246363776
var rateLimits: [rateLimits] = []//Rate limits
var exchangeFilters: [exchangeFilters] = [] //Exchange filters
var symbols: [symbols] //Symbols
}
struct rateLimits: Codable { //Rate limits
var rateLimitType: String //REQUEST_WEIGHT, ORDERS, RAW_REQUESTS
var interval: String //interval
var intervalNum: UInt16 //1
var limit: UInt32 //1200
}
struct exchangeFilters: Codable {
var filterType: String
var minPrice, maxPrice, tickSize, multiplierUp, multiplierDown, minQty, maxQty, stepSize, minNotional, maxPosition: String?
var avgPriceMins, limit, maxNumOrders, maxNumAlgoOrders, maxNumIcebergOrders: UInt16?
var applyToMarket: Bool?
}
struct symbols: Codable { //Symbols
var symbol: String //ETHBTC
var status: String //TRADING
var baseAsset: String //ETH
var baseAssetPrecision: UInt16 //8
var quoteAsset: String //BTC
var quotePrecision: UInt16 //8
var quoteAssetPrecision: UInt16 //8
var baseCommissionPrecision: UInt16 //8
var quoteCommissionPrecision: UInt16 //8
var orderTypes: [String] //orderTypes
var icebergAllowed: Bool //true
var ocoAllowed: Bool //true
var quoteOrderQtyMarketAllowed: Bool //true
var isSpotTradingAllowed: Bool //true
var isMarginTradingAllowed: Bool //true
var filters: [exchangeFilters] = [] //Filters
var permissions: [String] //Permissions
}
init() {
guard let url = URL(string: exchangeInformationURL) else {
print("URL is not valid")
return
}
let request = URLRequest(url: url)
let configuration = URLSessionConfiguration.ephemeral
let session = URLSession(configuration: configuration)
session.dataTask(with: request) { data, response, error in
do {
if let adat = data {
if let decodedResult = try? JSONDecoder().decode(exchangeInformation.self, from: adat) {
DispatchQueue.main.async {
self.exchangeInformations = decodedResult
}
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}.resume()
}
}
var informacio = exchangeInfo()
if let serverTime = informacio.exchangeInformations?.serverTime {
print (serverTime)
}
I got nothing.
I have a UserModel:
class UserModel {
var uid: String?
var username : String?
var email: String?
var profileImageUrl: String?
var dateOfBirth: String?
var registrationDate: Int?
var isFollowing: Bool?
var accessLevel: Int?
var onlineStatus: Bool?
init(dictionary: [String : Any]) {
uid = dictionary["uid"] as? String
username = dictionary["username"] as? String
email = dictionary["email"] as? String
profileImageUrl = dictionary["profileImageUrl"] as? String
dateOfBirth = dictionary["dateOfBirth"] as? String
registrationDate = dictionary["userRegistrationDate"] as? Int
accessLevel = dictionary["accessLevel"] as? Int
onlineStatus = dictionary["onlineStatus"] as? Bool
}
}
And I also have a value like [12ih12isd89 : True]
I want to change the value "onlineStatus" for the user "12ih12isd89" to True and I thought the right way to do this is updateValue(:forKey:). But my class UserModel does not have updateValue(:forKey:).
How can I use this in my existing model?
Edit:
How I get the data:
func fetchAllUsers (completion: #escaping ([UserModel]) -> Void) {
let dispatchGroup = DispatchGroup()
var model = [UserModel]()
let db = Firestore.firestore()
let docRef = db.collection("users")
dispatchGroup.enter()
docRef.getDocuments { (querySnapshot, err) in
for document in querySnapshot!.documents {
let dic = document.data()
model.append(UserModel(dictionary: dic))
}
dispatchGroup.leave()
}
dispatchGroup.notify(queue: .main) {
completion(model)
}
}
To me it looks like you need to find the right object in the array and update the property
let dict = ["12ih12isd89" : true]
var model = [UserModel]()
if let user = model.first(where: {$0.uid == dict.keys.first!}) {
user.onlineStatus = dict.values.first!
}
Depending on what ["12ih12isd89" : true] really is you might want to change the access from dict.keys.first! that I used
If your value dictionary contains more than one user, you can use a for loop like this:
var model = [UserModel]()
//Some initalization ...
let values = ["12ih12isd89" : true]
for (k, v) in values {
model.filter({$0.uid == k}).first?.onlineStatus = v
}