How to parse data from firebase and then save it within a model. Swift3 MVC format - swift

I have a user class within my firebaseDB. Its what you would expect a flat file DB would look like: users->UID->profile->key/values. I am trying to implement best practice within my code so I want the data to be handled by the User model in my Xcode project. How would I go about parsing the data and saving it as class variables within my model.
class User {
private var _username: String
private var _uid: String
var uid: String {
return _uid
}
var username: String {
return _username
}
init (uid: String, username:String) {
self._uid = uid
self._username = username
}
func getUserData() {
DataService.ds.REF_USERS.observeSingleEvent(of: .value, with: { (snapshot) in
if let users = snapshot.value as? Dictionary<String, Any>{
for (key, value) in users { // We use the for in loop to iterate through each key/value pair in the database which is stored as a dictionary.
if let dict = value as? Dictionary<String, Any> { // This gets us inside the user ID
if let profile = dict["profile"] as? Dictionary<String, Any> { // This gets us inside the profile
if let username = profile["username"] as? String {
self._uid = key // Stores the UID of the user as the key.
self._username = username
} else {
let username = profile["name"] as? String
self._uid = key
self._username = username!
}
}
}
}
}
This is what I am trying to do but I am lost as to how I would actually store the data values within the class. FYI: username is an attribute under profile.

You are currently looping through the entire list of users in the database and assigning the User properties repeatedly, overwriting the value from the previous loop cycle.
In order to get the correct user values assigned, you will need a way of knowing the correct path to the values. I would suggest saving the users using their assigned Firebase UID.
If you know the correct UID to look up you can then implement the following code:
func getUserData() {
DataService.ds.REF_USERS.child(_uid).child("profile").observeSingleEvent(of: .value, with: { (snapshot) in
guard let profileDict = snapshot.value as? [String: Any] else {
print("Error fetching user")
return
}
self._username = profileDict["username"] as? String ?? profileDict["name"] as! String
})
}
The way that "username" is force unwrapped in your original code doesn't appear safe but I'm assuming you are certain that it will work out. Otherwise the following line should be changed to safely unwrap the optional:
self._username = profileDict["username"] as? String ?? profileDict["name"] as! String

Related

How do I retrieve CloudKit Reference List?

I have setup my CloudKit record types the same as my local structs and models in my code but can't seem to find a way to retrieve the list of references.
For example, I have a UserData record type with a record field of friends which is a list of references to Friend record type.
Local models:
struct User {
let id: UUID
let friends: [Friend]
let username: String
}
struct Friend {
let id: UUID
let username: String
}
And this is the code I use to attempt to retrieve the friends
#State var friends = [Friend]()
if let userID = UserDefaults.standard.string(forKey: "userID") {
let publicDatabase = CKContainer.default().publicCloudDatabase
publicDatabase.fetch(withRecordID: CKRecord.ID(recordName: userID)) { (record, error) in
if let fetchedInfo = record {
friends = (fetchedInfo["friends"] as? [Friend])!
}
}
}
So I guess the main question is how do I retrieve a list record field from CloudKit as it does not seem to return the value as an array.

Firebase query returns empty when data is there

I have this RTDB I am trying to search my users in, from a path called users -> UID -> and then the user key/values. One of them being "username". I want to append these to an array to return in my table view but no matter what I do, I keep getting back nothing.
var userRef = Database.database().reference(withPath: "users")
func queryText(_ text: String, inField child: String) {
print(text)
userRef.queryOrdered(byChild: child)
.queryStarting(atValue: text)
.queryEnding(atValue: text+"\u{f8ff}")
.observeSingleEvent(of: .value) { [weak self] (snapshot) in
for case let item as DataSnapshot in snapshot.children {
//Don't show the current user in search results
if self?.currentUser?.uid == item.key {
continue
}
if var itemData = item.value as? [String:String] {
itemData["uid"] = item.key
self?.resultsArray.append(itemData)
print(self?.resultsArray)
}
}
self?.tableView.reloadData()
}
}
Edit: I have verified I am able to print out the snapshot, I am just not getting the usernames added to my resultsArray. Anyone have a clue why?
bio = " dfdf";
displayname = chattest4;
email = "test#test.com";
"first_name" = chattest4;
followers = 0;
following = 0;
"join_date" = "June 28, 2021";
languages = English;
"last_name" = test;
location = "United States";
profileImageURL = "hidjkfsf";
username = chattest4;
So I found out the issue. Some of the values in my database were not strings, but I had coded it to only look for Strings. Username was stored after these non-string values, so it never reached it. I just changed the array to [String:Any] and then it worked!
if var itemData = item.value as? [String:Any]

How to easily reference UserDefaults values [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 1 year ago.
Improve this question
Essentially my app relies on various JSON calls that rely on values initiated during login. Currently, I am using UserDefaults to save the following values at the time of login:
UserDefaults.standard.set(useremailValue, forKey: "useremail")
UserDefaults.standard.set(nameValue, forKey: "name")
UserDefaults.standard.set(userLastNameValue, forKey: "lastname")
In each view controller that I need to use these values I have been creating a variable to and assign the value of that user defaults:
var useremail = UserDefaults.standard.string(forKey: "facility")!
var name = UserDefaults.standard.string(forKey: "name")!
var lastname = UserDefaults.standard.string(forKey: "lastname")!
Although this works, doing so seems very redundant and unnecessary - is there an easier way to set and reuse user defaults?
I appreciate any suggestions.
I usually create a class to manage the user settings, something like this
class Settings {
static let shared = Settings()
let settings = UserDefaults.standard
private init() { }
var username: String {
get {
return settings.string(forKey: "useremail") ?? ""
}
set {
settings.set(newValue, forKey: "useremail")
}
}
// follow the same idea to the other fields
}
To manage it you only need to do the following
let username = Settings.shared.username // access it
Settings.shared.username = "some username" // set it
You can create sub classes to organize the variables, it all depends on your needs.
You can also follow the idea of #erictruong, here's a small tutorial Save custom objects into UserDefaults using Codable in Swift 5.1 (Protocol Oriented Approach)
UPDATE
In answer to a question in the comment, the user defaults can be cleared but they should be done manually
Calling the following function UserDefaults.resetStandardUserDefaults() this clears all UserDefaults you can also change to empty or if you set the vars as optional you can set them to nil
But they must be done on the App Lifecycle, either when the user clicks on logout or when the app shuts down.
You can also create a function to clear only those variables.
In Swift 5.1 property wrappers were introduced which are a perfect solution for this job.
Create an enum with static properties and a #propertyWrapper struct which controls the access to UserDefaults
enum Login {
#Wrapped(key: "useremail") static var useremail : String?
#Wrapped(key: "name") static var name : String?
#Wrapped(key: "lastname") static var lastname : String?
#propertyWrapper
struct Wrapped {
let key : String
var wrappedValue: String? {
get { return UserDefaults.standard.string(forKey: key) }
set { UserDefaults.standard.set(newValue, forKey: key) }
}
}
}
Then read the mail address with
let useremail = Login.useremail
and write it
Login.useremail = "john#doe.com"
Or create a struct and save it JSON encoded
struct User : Codable {
let email, name, lastname : String
}
enum Login {
#Wrapped(key: "user") static var user : User?
#propertyWrapper
struct Wrapped {
let key : String
var wrappedValue: User? {
get {
guard let data = UserDefaults.standard.data(forKey: key) else { return nil }
return try? JSONDecoder().decode(User.self, from: data)
}
set {
let data = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(data, forKey: key) }
}
}
}
And use it
let useremail = Login.user?.email
I would recommend to use the next extension of UserDefaults with auto generated keys which are based on custom properties names:
extension UserDefaults {
static func key(key: String = #function) -> String {
return "com.company.MyApp.\(key)"
}
var isAppInstalled: Bool {
get { bool(forKey: Self.key()) }
set { setValue(newValue, forKey: Self.key()) }
}
}
// How to use
UserDefaults.standard.isAppInstalled = true
print(UserDefaults.standard.isAppInstalled)
This solution eliminates definition and duplication of strings keys each time and make easy and convenient way to work with stored values.
i'm using DataMangers class
class DataManagers{
static var FireBaseToken: String{
set{
UserDefaults.standard.setValue(newValue, forKey: "Token") // this code save your value
}
get{
return UserDefaults.standard.value(forKey: "Token") as? String ?? kEmptyString // with this code we get our value
}
}
}
so when you want to save some value in userDefault then use it like this way
DataManagers.FireBaseToken = "Your Value"
if your want to get that value then use this code
let getValue = DataManagers.FireBaseToken
i hope this is what you want if there is something wrong in my answer then please edit my answer
Thank you

Firebase Swift 3 Fetching multiple values from a group

I want to be able to list users in a tableview by fetching their UID Value from a key stored in my database. As of now, the code only fetches the first value in the database rather than all of the values. Here is the code for fetching the applicants.
func loadApplicants() {
let usersRef = ref.child("users")
usersRef.observe(.value, with: { (users) in
var resultArray = [UserClass]()
for user in users.children {
let user = UserClass(snapshot: user as! DataSnapshot)
if user.uid == self.job.userID {
let appRef = self.ref.child("jobs").child(self.job.postID).child("applicants")
appRef.queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { (snapshot) in
let sValue = snapshot.value
resultArray.append(user)
})
}
}
}) { (error) in
print(error.localizedDescription)
}
}
This is what my database looks like where the User's UIDs are stored.
jobs
"Job ID"
applicants:
-KtLJaQnFMnyI-MDWpys:"8R6ZAojX0FNO7aSd2mm5aQXQFpk1"
-KtLLBFU_aVS_xfSpw1k:"GGqjtYvwSwQw9hQCVpF4lHN0kMI3"
If I was to run the app, it fetches UID: "8R6ZAojX0FNO7aSd2mm5aQXQFpk1"
How can I implement a for loop or an if statement to ensure that all of the values are taken and appended into the table view
I know that I need a for loop before the fetchApplicants is called from AuthService because it is only fetching one UID but I can't work out where it would go.
Thanks.
P.S. This is what I have tried
func loadApplicants() {
let jobID = job.postID
let appRef = ref.child("jobs").child(jobID!).child("applicants")
appRef.queryOrderedByKey().observeSingleEvent(of: .childAdded, with: { (snapshot) in
if let applicants = snapshot.value! as? [String:AnyObject] {
for (value) in applicants {
self.authService.fetchApplicants(applicantID: "\(value!)", completion: { (users) in
self.usersArray = users
self.tableView.reloadData()
})
}
}
})
}
but the output is:
(key: "-KtLLBFU_aVS_xfSpw1k", value: GGqjtYvwSwQw9hQCVpF4lHN0kMI3)
(key: "-KtLJaQnFMnyI-MDWpys", value: 8R6ZAojX0FNO7aSd2mm5aQXQFpk1)
Needed to use observe instead of observeSingleEvent
Answer:
appRef.queryOrderedByKey().observe(.childAdded, with: { (snapshot) in

How can I return an object that I create in my data service class through Firebase?

I am trying to adhere to MVC practices and keep all the network code inside the data service class that I am using in my app. On one screen I have the user's name and username that needs to be displayed. When updating this, I am calling this function:
func grabUserData() -> User {
REF_USERS.child(getCurrentUID()).observeSingleEvent(of: .value) {
(snapshot) in
if let userDict = snapshot.value as? Dictionary<String, String> {
let user = User(
first: userDict["firstName"]!,
last: userDict["lastName"]!,
username: userDict["username"]!
)
return user
}
}
}
But I am getting an error trying to return the user! It says:
Unexpected non-void return value in void function.
But the function clearly isn't void. So what do I do?
You are confusing your grabUserData function return value with the Firebase closure return value — the former is User but the latter is Void ;)
You are actually returning from this closure
— I'm now using a explicit return type to be clear:
{
(snapshot) -> Void in
if let userDict = snapshot.value as? Dictionary<String,String> {
let user = User(
first: userDict["firstName"]!,
last: userDict["lastName"]!,
username: userDict["username"]!
)
return user
}
}
which is passed as the last argument to the observeSingleEvent Firebase function. This is a very common mistake ;)
Completion handler. A standard pattern here is to return the desired User via a completion handler instead. This solution nicely models the asynchronous nature of network requests such as Firebase database calls. For instance:
func grabUserData(completion: #escaping (User?) -> Void) {
REF_USERS.child(getCurrentUID()).observeSingleEvent(of: .value) {
(snapshot) in
if let userDict = snapshot.value as? Dictionary<String, String> {
let user = User(
first: userDict["firstName"]!,
last: userDict["lastName"]!,
username: userDict["username"]!
)
completion(user) // Returns user!
} else {
completion(nil) // User not found!
}
}
}
Finally, in your data service client code, call it like this:
grabUserData() {
(user) in
if let user = user {
print("Grabbed user: \(user)")
} else {
print("User not found!")
}
}