Realm returning wrong data - swift

I'm new to realm. I have two objects, Restaurant and Address.
class Address: Object {
#objc dynamic var identifier: Int = 0
#objc dynamic var name: String!
#objc dynamic var restaurant: Restaurant!
}
notice that Address has relationship with restaurant.
class Restaurant: Object {
#objc dynamic var identifier: Int = 0
#objc dynamic var name: String!
#objc dynamic var location: String!
#objc dynamic var images: String!
}
now on my api side I will request for addresses and it will return list of addresses with restaurant but the restaurant has an identifier value only and others are nil to save loading time. I will save it to my realm.
Now in order to get the other restaurant details like name and location etc. I will request again list of restaurants to the api and save it to realm.
Right now I got 2 list of records save on my database. Address with incomplete restaurant information and Restaurants with complete information.
Now I want to get the Restaurant with complete information using the identifier of the Address.restaurant.identifier. so the code is like this.
let realm = try! Realm()
let predicate = NSPredicate(format: "identifier = %i", address.restaurant.identifier)
let restaurant = realm.objects(Restaurant.self).filter(predicate).first!
My expected output is the restaurant with complete details because I'm filtering the Restaurant class, instead I got the restaurant inside the Address object with incomplete restaurant details.
Could someone help me query the restaurant with complete details? Thank you.

As you said you make 2 requests, one for short addresses and another for restaurant details, in first request you make a relation between address and restaurant, but when you are getting restaurant details you save it like new object.
I think in your case you need to provide primaryKey for each class that you want to save in DB like:
override static func primaryKey() -> String? {
return "identifier"
}
After that data from restaurant details request will update your restaurant with the same id in DB and there will be only one unique object.
Also, you can have 2 tables, one for restaurants and another for addresses. Address will have restaurants identifiers, restaurant will have only one address identifier.
Hope it help you

From Realm Swift documentation for relationships: "For example, rex.owner?.address.country will traverse the object graph and automatically fetch each object from Realm as needed."
So realm would fetch Restaurant object for Address object only when it is accessed. So I would not try to optimize fetches by having 2 Restaurants in the Realm database.

Do you have to do a query?
Instead of doing:
let realm = try! Realm()
let predicate = NSPredicate(format: "identifier = %i", address.restaurant.identifier)
let restaurant = realm.objects(Restaurant.self).filter(predicate).first!
Couldn't you just do (given that you have the Address object):
let restaurant = address.restaurant

Related

How to match 2 users if they both pressed 'Heart' button

My app is very similar to Tinder, and only got the "matching" part left for it to be finished. When the user touches the 'Heart' button, a card with their profile is displayed on the Notifications View to the other user, for them to accept it or not.
In case of yes, the will both 'match' as in Tinder. My problem is in how to make that happen.
Person is presented to the User, and the home view displays the people, that UserManager holds.
struct Person: Identifiable, Hashable {
var username: String
var age: Int
}
struct User {
let username: String // These are let because they're being saved on UserManager
let age: Int
}
class UserManager: ObservableObject {
#Published var userInfo: UserInfo?
#Published var people: [Person] = [] // All People
#Published var matches: [Person] = [] // Matched people
Now, here in UserManager, I fetchAllUsers from Firebase and basically init the people's data by the documentSnapshot.
ref.getDocuments { documentsSnapshot, error in
if let error = error { }
documentsSnapshot?.documents.forEach({ snapshot in
let data = snapshot.data()
self.people.append(.init(data: data))
})
}
And finally, in HomeView, the user taps the button and appends the other person in the matches array, which doesn't make sense cause the other person hasn't even accepted them yet.
struct HomeView: View {
var body: some View {
CircleButtonView(type: .heart) {
if let person = userMng.people.last {
userMng.swipe(person, _direction: .like)
userMng.matches.append(person)
// Should change this to another array?
}
}
}
}
Tried saving it to Firebase and then retrieving the data from the users once matched but I can't especify what person the user has liked, for me to make that network call.
How can I append the liked person to another array and then append it to 'matches' once confirmed that they both like each other?
You need to make a new table in firebase with the name of firendRequest, where table child id will be userID and below it, we will have ids of all users who has sent him the friends request. Please have a look the the schema in below image which will make your concept more clear.
The way, you are getting all the user will add up more computation when number of users start increasing and its not recommend to do such large computation on mobile devices. One more thing, Firesbase is NO-SQL database so you need to duplicate your data to avoid computations. Making duplicate date does not mean you are not implementing the thing right :)

Prepopulate Realm database with many to many relationships?

I'm brand new to this, so forgive me if I'm missing something obvious or not asking the right question. I plan to make an app that have a few sets of data that require many-to-many relationships. For example, if I have a model for Food items and a model for CookingMethods to cook that food item. So, each Food can have multiple CookingMethods, and each CookingMethod applies to multiple types of Food.
I think this is the right way to set up the realm data:
class Food: Object {
#objc dynamic var title: String = ""
#objc dynamic var id: Int = 0
var cookingMethods = List<CookingMethod>()
}
class CookingMethod: Object {
#objc dynamic var id: Int = 0
#objc dynamic var title: String = ""
let foods = LinkingObjects(fromType: Food.self, property: "cookingMethods")
}
Is it now possible to import a set of data (probably a csv file?) using either Realm Studio or programmatically that allows me to link this relationship? The Food would have a list of CookingMethods, and the CookingMethods would link back to multiple different Foods?
If I'm going about this all wrong please let me know, there is a spreadsheet of data that I'd like to add to my app's database as a one time thing.

Realm Swift: Querying over backlinks is disabled but backlinks were found in the inverse relationship

I am getting this error and can't seem to find any answer anywhere online. It is Swift 5.1 and Realm 3.19.1
Querying over backlinks is disabled but backlinks were found in the inverse relationship
I have two simple Realm Model objects, Todo and User.
#objcMembers class User: Object {
dynamic var uid: String! = UUID().uuidString
dynamic var username: String?
dynamic var added: Date = Date() // Date when user is added
let todos = List<Todo>()
}
And:
#objcMembers class Todo: Object {
dynamic var title: String!
dynamic var added = Date()
let user = LinkingObjects(fromType: User.self, property: "todos")
}
And here I am calling the Todo objects:
var todos = realm.objects(Todo.self).filter("ANY user.uid CONTAINS [c]%#", uid)
Is there something I am doing wrong? Or somehow backlinks need to be activated somewhere before I can use them?
Realm documentation is of absolutely no help whatsoever.
What I am trying to do is to query all todo items for a given user by user id. Each User object has a list of Todo objects in it.

Where's the best place to call methods that interact with my database?

I'm creating an app that interacts with a Firestore database. As of now I have a singleton class, DatabaseManager that has all the methods relating to the Firestore database (i.e. get/post methods).
I have a User model called User that has properties such as name, email, photoURL, and some app-specific properties. Any user can edit their profile to update information from a view controller called EditProfileViewController.
Now my question is: is it best to call the DatabaseManager.shared.updateInfo(forUser: user) (where user is a User instance) from EditProfileViewController, User, or some other place?
Sorry if this is an obvious question, but there's going to be a lot of points in the app where I'll need similar logic so I wanted to know what's the best design. Also I'm sure this question has more to with MVC than it does Firebase/Swift.
A couple of thoughts:
Rather than accessing the singleton directly with, DatabaseManager.shared.update(for:), I might instead have a property for the database manager, initialize/inject it with the DatabaseManager.shared, and have whatever needs to interact with the database use that reference, e.g., dataManager.update(for:). The goal would be to allow your unit tests to mock a database manager if and when necessary.
I would not be inclined to have a view controller interact directly with the DatabaseManager. Many of us consider the view controller, which interacts directly with UIKit/AppKit objects, as part of the broader “V” of MVC/MVP/MVVM/whatever. We’d often extricate business logic (including interaction with the database manager) out of the view controller.
I personally wouldn’t bury it under the User object, either. I’d put it in an extension of the database manager, and called from the view model, the presenter, or whatever you personally want to call that object with the business logic.
Is there a reason you're using a singleton to contain all the Firestore logic? User model should contain the method updateInfo.
Here's an example i've used with Firestore:
class Group {
// can read the var anywhere, but an only set value in this class
private(set) var groupName: String!
private(set) var guestsInGroup: Int!
private(set) var joinedGroup: Bool!
private(set) var timeStampGroupCreated: Date!
private(set) var documentId: String!
init(groupName: String, guestsInGroup: Int, joinedGroup: Bool, timeStampGroupCreated: Date, documentId: String) {
self.groupName = groupName
self.guestsInGroup = guestsInGroup
self.joinedGroup = joinedGroup
self.timeStampGroupCreated = timeStampGroupCreated
self.documentId = documentId
}
// method to parse Firestore data to array, that table view will display
class func parseData(snapshot: QuerySnapshot?) -> [Group]{
var groups = [Group]()
guard let snap = snapshot else { return groups }
for document in snap.documents {
let data = document.data()
let groupName = data[GROUP_NAME] as? String ?? "No Group Name"
let guestsInGroup = data[GUESTS_IN_GROUP] as? Int ?? 0
let joinedGroup = data[JOINED_GROUP] as? Bool ?? false
let timeStampGroupCreated = data[TIMESTAMP_GROUP_CREATED] as? Date ?? Date()
let documentId = document.documentID
// add objects with fetched data into thoughts array
let newGroup = Group(groupName: groupName, guestsInGroup: guestsInGroup, joinedGroup: joinedGroup, timeStampGroupCreated: timeStampGroupCreated, documentId: documentId)
groups.append(newGroup)
}
return groups
}
}

Query where list has item/object in Realm

Using Realm in Swift (I am still using the version just before v1.x):
I have this class
class Event: Object {
dynamic var id: String = ""
dynamic var title: String? = nil
dynamic var creator: User?
let members = List<User>()
}
How can I find all events with the member "User A" (I have the id of the User A).
I tried something like this but doesn't really work:
let predicate = NSPredicate(format: "ANY members.id == %#", userA.id)
eventsWithUserA = realm.objects(Event).filter(predicate)
If you're not using the latest version, hopefully you're at least using version 0.100 or higher. If so, you can use Realm's inverse relationships feature to do this:
class User: Object {
let events = LinkingObjects(fromType: Event.self, property: "members")
}
After implementing this, user.events will return a List of every Event object in which the user object is in its members property.
let eventsWithUserA = userA.events
Hopefully this should eliminate the entire need to manually perform a query for what you're trying to achieve here.
Let me know if that doesn't work for you!