Accessing MultiLevel Nested Children in Firebase Database Swift 3 - ios

I am using Firebase Realtime Database.
I have a customer id which corresponds to customer table. I need to fetch its respective apartment name. Then search the record having same apartment name in apartment table. Once found, I need to get all serviceType values from its respective segments. Also need to fetch its block from apartment table.
Table structure is as follows:
customer
-L1x2AKUL_KNTKXyza
apartment_name:"ABC Residency"
appartment
-L1Ohec4nW-ya_SkiG49
apartment_name:"ABC Residency"
block: "A Wing"
segments
-L1OhecGtEk_8xdNs67T
serviceType:"Mopping"
-L1OhecGtEk_8xdNs631
serviceType:"Cleaning"
I want to use only one firebase database reference object and achieve this multilevel access.
Any help would be greatly appreciated!

This should help you. I've created a class called CRUD to perform all of my firebase functions.
import Firebase
/**
CRUD Object to maintain Firebase DB
- MUST use crud.creatRef() before any save or read functions!!
*/
class CRUD: NSObject {
var ref: DatabaseReference!
var id: String!
/**Create FirebaseDB reference for use of CRUD Object to interact with DB*/
func createRef() {
id = PageDataSource.sharedInstance.myID!
ref = Database.database().reference(fromURL: "https://yourURL.firebaseio.com/")
}
}
When calling CRUD to save or load data within my app I have to do this:
let crud = CRUD()
crud.createRef()
crud.doSomething()
I've had great success with this. You'll need to design your model and implement the rest of the code accordingly. When you create your save functions they should go inside of the CRUD object.

Related

Best way to store a single instance of RLMObject

I'm currently developing an iOS app with Realm as the backend data storage. I have a class that is an RLMObject for the user profile. It stores their name, profile picture, stats, etc.
There should only always be one of these objects, however I know implementing a singleton pattern is usually a bad idea. Currently I have it implemented as
//AppDelegate.swift, applicationDidFinishLaunching
//If there's no user profiles yet (first time launch), create one
if UserProfile.allObjects().count == 0 {
let realm = RLMRealm.defaultRealm()
try! realm.transactionWithBlock() {
realm.addObject(UserProfile())
}
}
//ProfileViewController.swift
//Get the first UserProfile
var userProfile: UserProfile? {
get {
return UserProfile.allObjects().objectAtIndex(0) as? UserProfile
}
}
Is there a better way to keep track of a single instance of this class?
Your code sample uses a computed property, which will fetch the object from the Realm each time you access it.
Instead, try using a lazy var property:
lazy var userProfile: UserProfile? = {
return UserProfile.allObjects().objectAtIndex(0) as? UserProfile
}()
This type of property will load the object from the Realm only the first time it is accessed. All subsequent accesses will be directly to the object.
Note that, since UserProfile is a Realm object, its fields will automatically update in response to changes made to the underlying object in the Realm. Likewise, any changes you wish to make will need to be wrapped within a Realm write transaction.
In terms of your overall architecture, there is nothing wrong with storing a single instance of an object type in a Realm similar to what you are doing. You may want to give your UserProfile object a hardcoded constant primary key, then use the 'add or update' version of the update API (see https://realm.io/docs/swift/latest/#updating-objects). This will allow you to avoid having to explicitly create a new object.

Connecting remote search results with local database using CoreData

Assume we have simple data model with single entity User; simple tableView_friends with fetchedResultsController_friends for show users - friends.
Assume we have search bar for searching all (not only friends) users in service, and for every typed in it character we perform search request to server, which return to us somehow filtered by character User objects. Some of this objects can already be inside local database. By app logic we don't really must save all this results in local database forever (but ok, we can, we can clear local database time to time); on other hand, if we will perform any action on some searched user, we must store this user. We want to show list of searched user in other tableView_search with fetchedResultsController_search.
Question: should I use same context for fetchedResultsController_friends and fetchedResultsController_search? If no, how can I handle situation, when I wish to edit searched user, which already exists in database and probably already local edited? If yes, how can I setup predicate for fetchedResultsController_search (server perform its own logic for search by character, which can be changed) for show exactly same result as from server?
We recently implemented a search feature in our application and had a similar issue, We had local data in core data and also remote data from our API.
You have a few options that we explored:
Save your data into core data from the API as it is retreived and
then the fetched results controller will do the rest
Manage the merge of the data yourself, you can still use NSFetchedResults controller to an extent but need to do more work
We didn't want to save all of the information returned from the API unless it was needed (the user selected it), so we come up with a simple solution that worked for our app. This may not work directly for your app, you may need a completely different solution or change some of the things we done to suit.
Firstly, To explain what we are dealing with, we had a Article entity in core data which contains around 25 properties, the API returns article objects as JSON data with the same data.
What we decided to do was to create a class which represents a simple version of an article (just enough data to show in a list view and reference it later in the API or core data) which looked something like this:
class SearchResult: NSObject {
var id:String?
var title:String?
var imageUrl:String?
var url:String?
// core data entity
init(article:Article) {
self.id = content.contentId
self.title = content.title
self.featuredImageURL = content.absoluteImagePath()
self.urlAlias = content.urlAlias
self.publishedAt = content.publishedAt
}
init(articleDictionary:NSDictionary) {
self.id = articleDictionary.objectForKeyNotNull("id") as? String
self.title = articleDictionary.objectForKeyNotNull("title") as? String
self.url = articleDictionary.objectForKeyNotNull("url") as? String
if let imageUrl = articleDictionary.objectForKeyNotNull("imageUrl") as? String {
self.imageUrl = imageUrl
}
}
}
Now using this, we can create once of these from either the core data results or from the API results. Our tableview datasource is just an array
var dataSet = [SearchResult]()
We use the NSFectchResultsController delegate methods to add/remove/re-order core data elements from the dataSet after the initial load and when we get API data we'll do something like:
dataSet = Array(Set(apiResponseArray + dataSet))
This will take an array of SearchResult items from the API, merge them with the current result set and remove duplicates. casting to a set and then back to an array will give you an array of unique results as a Set is made of unique values only.
See this reference which should help with how the delegate methods would work

Update Realm relationship with primary ids

Is it possible to update a Realm object's relationships by passing only the ids of the relationships?
If I have a Library class that has a List<Books> relationship and I wanted to merge two Libraries together, I would presume that could be done like:
let bookIds = (firstLibrary.books.toArray() + secondLibrary.books.toArray).map { $0.id }
Then I use ObjectMapper & SugarRecord:
let changes = ["books": bookIds]
Mapper<T>().map(changes, toObject: secondLibrary)
let realm = self.realm
realm.add(secondLibrary, update: true)
But the list of books doesn't get updated.
I assume this is because ObjectMapper doesn't know anything about primary ids and therefore trying to map them into an object doesn't do anything.
Does Realm have the capability to update via primary id? If it does, I'd gladly rewrite my persistence stack.
For future reference: one workaround, which was discussed on this Github issue, is to modify the object creation code in the example as such:
let books = ["1", "2"].map { realm.objectForPrimaryKey(Book.self, key: $0) }
realm.create(Library.self, values: ["id": "4321", "books": books], update: true)
It's not possible to directly pass an list of primary key values as part of the value passed into the create() API to create a Library, and have Realm automatically create or update the Book objects associated with those primary keys.
Instead, this workaround first retrieves or creates the Book objects for each primary key value using objectForPrimaryKey(), and then creates the Library using those Book objects using create().

How to get all Created Class Names in Parse using swift?

Can anyone please tell me a way to Query the list of classes created in the Parse Core. I am using Swift 2.0 and XCode 7.
Basically, I have 4 different classes (example : Class1, Class2, ClassName3, ClassName4) holding the different data. I want to get only the class Names and display it in my app using PFQueryTableViewController.
override func queryForTable() -> PFQuery {
var query : PFQuery = PFQuery(className: "ClassName1")
query.orderByAscending("songName")
return query
}
Above code queries the data from ClassName1, however, I want to display the class names not the data within a particular class.
Thanks
Thanks a lot
It isn't available via the iOS SDK.
You can get it via the REST interface if you use the master key to fetch the app schema: https://api.parse.com/1/schemas
It may be easier for you to just add a Classes class with rows for each of the classes, which you manage manually and can simply query with PFQuery...

How to allow only one user to edit a Parse object?

I'm creating an app that allows co-workers to upload shifts (objects) to Parse that they need covered. I save all the shifts to a "Shifts" class (in the data browser). I want to restrict the ALC so that only the person who uploaded the shift/object can edit it (everyone can "read" it). Is there a way that I can create/upload that programmatically (rather than having to go into the Data Browser and add it manually)?
You can create a relationship between your Shifts and your existing _User table. If you add a shift, you can create a relationship to the _User table. Than, if you retrieve the info, you can check if the user who reads the record is also the one who created it by comparing the active PFUser.currentUser with the user from your shift.
//Initial safe
var user = PFUser.currentUser
var relation = user.relationForKey("shifts")
relation.addObject(yourShiftObject)
user.saveInBackground()
You can set the ACL on objects when you create them. The best would be to define a default ACL that allows the creating user to read and write the object while still allowing public read access.
Example taken from the documentation
var defaultACL = PFACL.ACL()
// Optionally enable public read access while disabling public write access.
defaultACL.setPublicReadAccess(true)
PFACL.setDefaultACL(defaultACL, withAccessForCurrentUser:true)
https://www.parse.com/docs/ios_guide#security-recommendations/iOS

Resources