Adding Objects to single Realm File - ios

Before I begin here is a bit a background for you all so your possible answers won't go beyond my comprehension level:
1. I'm only jolt starting to learn swift and is still learning the in and outs of both realm and Xcode.
2. My only OOP experience has been with java, and at that very low.
So here is my issue:
I'm trying to make a single realm file hold an entire list of "user profile" data(ie. name, age, email). I'm attempting to do this by allowing an IBAction button to cause an object to be saved to the realm file as shown below
#IBAction func signUpButton(_ sender: UIButton) {
let realm = try! Realm()
try! realm.write {
user.userName = userNameTextField.text!
user.passWord = passWordTextField.text!
user.email = emailTextField.text!
user.name = fullNameTextField.text!
user.age = ageTextField.text!
profile.person.append(user)
realm.add(profile)
}
}
The only problem here is that it's not adding object but instead updating the one that was created before, can anyone tell me how I can accomplish this using an IBAction Button?

If an Object has already been added to a Realm file, then you can change its properties by opening up a write transaction and simply modifying its properties in there.
let realm = try! Realm()
let newUser = User()
newUser.userName = userNameTextField.text!
// Add to Realm for the first time
try! realm.write {
realm.add(newUser)
}
// Update its property at a later time
try! realm.write {
newUser.userName = userNameTextField.text!
}
It's not necessary to call realm.add or profile.person.append(user) again if those objects were already previously added.
I'm not sure where user and profile are coming from in your example code there. Since there's no let user = User() inside that method, I'm assuming you're creating single copies of them elsewhere in the view controller class.
If profile has already been added to the Realm, you shouldn't be calling realm.add(profile) again, as that will add a second copy (And calling append each time probably won't break, but it's not recommended).
To work out if profile is already inside a Realm file, you can check by using profile.realm != nil. To check if user already belongs in profile, you can user Realm's inverse relationships feature.
class User: Object {
dynamic var userName = ""
dynamic var password = ""
dynamic var email = ""
dynamic var name = ""
dynamic var age = ""
let profile = LinkingObjects(fromType: Profile.self, property: "person")
}

If your object have primary key, you can only add new by fetch then delete it, then add new one, else you can only update it (primary key is used to prevent duplication)
If you want to have 2 object that have similar property value, then just simply remove the primary key from the object class

Related

issue when appending the RLMArray, update RLMArray in RLMObject and link it with object in swift realm 3.0

want some method like which do update the existing followers, and if not exists do add it to DB and link them to user something like
GETS CRASH OVER append in write block, due to duplicate primary key,
also, it works perfectly if no followers has been added in HKUser Table, once it comes to update it crashes
import UIKit
import RealmSwift
class HKUser: Object{
dynamic var full_name = ""
dynamic var email: String?
dynamic var user_id: String?
let followers = List<HKUser>()
override static func primaryKey() -> String? {
return "user_id"
}
}
I want to update the connection of a user in DB also, so I want to do some thing like
//1. updated the userFollower array with required data
let userFollowers:[HKUser] = []
//2. now need to link it with my user object and update it in db
if let user = realmWrapper.sharedInstance.getUser(forID: id) {
try! realm.write {
//want some method like which do update the existing followers,
//and if not exists do add it to db and link them to user something like
//realm.add(user, update: true)
user.followers.append(contentsOf: followers)
/**********
GETS CRASH OVER HERE,
due to duplicate primary key,
it works perfect if no followers has been added in HKUser Table,
once it comes to update it crashes
**********/
}
}
List<T>.append() method save to the Realm implicitly if the objects are unmanaged. That is why the duplicated primary key exception happens.
To avoid this, you can add or update the unmanaged objects before appending to the List. Then you append the objects to the List.
try! realm.write {
...
realm.add(followers, update: true) // Add or update the objects first
user.followers.append(contentsOf: followers)
...
}
Maybe it's not the case (not enough code in your question to tell) but it looks like you prepare the userFollowers array in step 1 to contain ALL the current followers for that specific user.
If this is the case then you'll end up re-adding all the existing followers not only the new ones, hence the duplicate keys.

Update model Realm Swift

I'm a new in Realm and ask you to help me.
I created the model:
class UserModel: Object {
dynamic var email = ""
dynamic var facebook_id = ""
dynamic var google_id = ""
dynamic var id = 0
dynamic var name = ""
dynamic var photo = ""
dynamic var someinfo = ""
dynamic var twitter_id = ""
}
When I login to app, I can see my info on UserProfileController. Also I have a EditProfileController where I can change some info about myself. So when I successfully change it, I want to update my Realm model, and try to do this:
let realm = try! Realm()
try! realm.write {
realm.create(UserModel.self, value: ["name": self.editEmail.text!, "email": self.editEmail.text!], update: true)
}
But unfortunately I see this message:
''UserModel' does not have a primary key and can not be updated'
What if I want to update a few property at the same time?
Can I do this with primaryKey? Or...
How it possible to update Realm model without primaryKey?
The problem lie in this update: true, it's used to automatically replace object that have the same primary key in your realm, if your object don't have primary key, set update to false then it will work and always create new object
You need to set a primary key for your UserModel.
https://realm.io/docs/swift/latest/#primary-keys

Firebase overwrite existing record

With the following code that is used to create a new user into Firebase BaaS, but when it is launched it overwrite the existing user.
let test = Firebase(url: "https://dr-freud.firebaseio.com/users")
ref.createUser(email.text, password: password.text,
withValueCompletionBlock: { error, result in
if error != nil {
print(error)
} else {
let uid = result["uid"] as? String
let nome = self.nome.text!
let cognome = self.cognome.text!
let utente = ["Nome": nome, "Cognome": cognome]
let users = ["\(self.nome.text!)": utente]
test.setValue(users)
}
})
You're calling setValue() on the same location, so you'll indeed overwrite the existing data at that location.
To prevent this, you'll need to call setValue() on a user-specific location:
let currentUser = test.childByAppendingPath(uid)
currentUser.setValue(users)
By calling childByAppendingPath() we end up with a reference to a database location that is specific to this user.
This and much more is covered in Firebase's programming guide for iOS. I highly recommend that you spend some time in that guide. A few hours there will save you many problems and questions down the line.
It's overwriting due to this line
test.setValue(users)
You need to create a separate child for each user that you write to the users node.
Since this is the users node, you should use the uid that the createUser provides. Frank's answer is correct so the below is more of a general case answer.
If it was some other node where you are not provided a natural key, using autoId will help you do this; here's an example.
let pizzaRef = ref.childByAppendingPath("pizzas")
let thisPizzaRef = pizzaRef.childByAutoId()
let pizzaData = ["crustType": "Thick"]
thisPizzaRef.setValue(pizzaData)
this will create a new node within the users node each time it's called
pizzas
-J1092uf8n0293i
crustType: "Thick"
-J989jijsoissds
crustType: "Thick"

Firebase + swift retrieving user data

I just tried to retrieve data from firebase for my project. For example, display the facebook user name in UILabel. I store the facebook user data like this
then retrieve the data using :
let ref = Firebase(url:"https://<app-name>.firebaseio.com/users/facebook:10207213459687665/name")
ref.observeEventType(.Value, withBlock: { snapshot in
self.facebookUserName.text = snapshot.value as! String
})
It works perfectly but it is pretty stupid by retrieving user name in a specific path because that could be different facebook user login.
I'm thinking like check the user is logged in and display their name or checking the currentUser or any smarter way to do this?
I am not sure how to do that.
There are 100 different ways to do this; here's a couple
users
user_id_0
facebook_id: facebook:10207213459687665
name: Nicholas
user_id_1
facebook_id: facebook:12346578912345689
name: Frank
in the above, you would query for the facebook_id you want, which will return the node and all of the child nodes (name, address, etc). The user_id_x is a Firebase auto-generated node name (guaranteed to be distinct)
ref.queryOrderedByChild("facebook_id").queryEqualToValue("facebook:12346578912345689")
.observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
print(snapshot.value) //prints the facebook_id and Frank
})
Another option is to use your same data structure and observe that node to load the data. Keep in mind that the facebook id is the KEY of the node, not the value - .value is key:value pairs.
let ref = Firebase(url:"https://<app-name>.firebaseio.com/users")
let thisUser = ref.childByAppendingPath("facebook:12346578912345689")
ref.observeSingleEventOfType(.ChildAdded, withBlock: { snapshot in
//snapshot will contain all of the child nodes
let userName = snapshot.value.objectForKey("name")
self.facebookUserName.text = userName
})
If you just care about the users name, you could simplify your structure by using the facebook id as the key and the value would be the user name:
users
fb_10207213459687665: Nicholas
fb_12346578912345689: Frank
and retrieve with the above observe code except you would again use the .value property as in your initial question.
In this case the .value property is a string as it's the only value (there are no child nodes as in the structure you posted, which could cause issues as it could be a series a key:value pairs which would crash)
Queries add some overhead so the observe is more efficient.
I have found an answer in http://www.appcoda.com/firebase/
When we are trying to fetch the data from current user add this code below to store the uid(when creating user account)
NSUserDefaults.standardUserDefaults().setValue(result ["uid"], forKey: "uid")
assign the variable:
var CURRENT_USER_REF: Firebase {
let userID = NSUserDefaults.standardUserDefaults().valueForKey("uid") as! String
let currentUser = Firebase(url: "\(BASE_REF)").childByAppendingPath("users").childByAppendingPath(userID)
return currentUser!
}
implement code for function:
<reference>.observeEventType(FEventType.Value, withBlock: { snapshot in
let currentUser = snapshot.value.objectForKey("username") as! String
<--your code-->
print("Username: \(currentUser)")
self.currentUsername = currentUser
}, withCancelBlock: { error in
print(error.description)
})
}
Then we could obsolete the ref in a direct path.
I had the same problem. To fix it, I created a variable that automatically references the user that's logged in:
class ViewController: UIViewController {
let UsersRef = Firebase(url:"https://<app-name>.firebaseio.com/users")
Then, under the #IBOutlet that you're trying to get the user data to show up in, write:
let LoggedInUser = UsersRef.childByAppendingPath("\(UsersRef.authData.uid)")
When you run the code, "UsersRef.authData.uid" is automatically replaced by the specific ID of the user currently logged in. This should create a direct path to the database info of the logged in user without having to manually enter the user's ID in the reference.
To retrieve data of a facebook user we can use the currentUser property of FirebaseAuth which returns a FIRUser which contains all the information about you user. After reading all the user data you can store it anywhere but for the sake of this example I have used UserDefaults.
import Firebase
func getUserInformation()
{
let user = Auth.auth().currentUser
if let user = user {
UserDefaults.standard.setValue(user.displayName!, forKey: "Username")
let uid = user.uid
UserDefaults.standard.set(uid, forKey: "user_ID")
let url = "http://graph.facebook.com/\(uid)/picture?type=square"
UserDefaults.standard.set(url, forKey: "ImageData")
}
}

Do I need to write all the child objects in a class in realm as well?

Following the example code as shown:
// Define your models like regular Swift classes
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
class Person: Object {
dynamic var name = ""
dynamic var picture: NSData? = nil // optionals supported
let dogs = List<Dog>()
}
// Use them like regular Swift objects
let myperson = Person()
let mydog = Dog()
mydog.name = "Rex"
myperson.dogs.append(mydog)
// Persist your data easily
let realm = try! Realm()
try! realm.write {
// do I need to add this statement??
realm.add(mydog)
realm.add(myperson)
}
Do I need to persist the mydog object as well, or that Realm is smart enough to know that it is a new child object of the myperson and it will persist it for me?
No you do not need to persist the actual dog object, if you already persist an object containing it.
For the users who are wondering about cascading delete, the answer is NO.
Realm supports cascading write but NOT delete. For delete you may have to query all the relations and delete one by one.

Resources