I wonder if it is possible to use the same notificationblock on a resultset but change the filter? For example: I have two queries, one with isDelivered = true and one where isDelivered = false. I would like to have one Resultset with different filters and then switch the resultset depending on if I want to see delivered or undelivered items. Is that possible or do I need to create two notificationblock for this?
It's not possible to retroactively change the predicate query used to create a Realm Results object. But it is possible to attach the same notification block to two separate Results instances in order to share the handling logic.
let notificationBlock: ((RealmCollectionChange) -> Void) = { changes in
// Perform common update logic in here
}
let deliveredObjects = realm.objects(MyObject.self).filter("isDelivered = true")
let delieveredNotificationToken = deliveredObjects.addNotificationBlock(notificationBlock)
let undeliveredObjects = realm.objects(MyObject.self).filter("isDelivered = false")
let undelieveredNotificationToken = undeliveredObjects.addNotificationBlock(notificationBlock)
Related
I replaced all the instances of UISwitch in my app with a custom class - let's call it MySwitch - and as a result the XCUIElementTypeQueryProvider.switches query doesn't contain any results. How can I create a query that does match all descendants that are instances of MySwitch?
Ideally what I want is to be able to call someElement.switches and have it return all instances of UISwitch and MySwitch, or call someElement.mySwitches and have it return all instances of MySwitch.
You’d need a way to uniquely identify your switches since presumably they are now coming back as otherElement and a query for all of those would return a lot of junk (depending on your app). Add an accessibility ID of mySwitch (or anything you'd like) to your switch and you should be able to get them all back with myElement.otherElements[“mySwitch”].
If you need a query that returns both your custom switches (found by an identifier) AND standard switches you're going to have to come up with a compound predicate. I've made a guess at what the second predicate should be (not tested / never written an elementType predicate so you might have to play around a bit), but it'd be something like this:
let mySwitchPredicate = NSPredicate(format: "identifier = %#", "mySwitch")
let standardSwitchPredicate = NSPredicate(format: "elementType = %#", ".switch")
let predicate = NSCompoundPredicate(orPredicateWithSubpredicates: [mySwitchPredicate, standardSwitchPredicate])
I have relatively expensive operation so I am willing to perform that operation once and create 2 Observables from it.
Here is how it looks:
let outputObservable1: Observable<Bool>
let outputObservable2: Observable<Bool>
(outputObservable1, outputObservable2) = inputObservable1.zip(inputObservable2).map { booleanCondition1, booleanCondition2 in
// different condition combinations create different outputObservables
}
I am guessing map is not the right operator here as it will only yield one observable. How can I mix and match the conditions and return 2 Observables at once?
Based on my understanding, you just need to use map
let inputs = Observable.zip(inputObservable1, inputObservable2)
.share() // you only need this if one of your inputs is making a network request.
let outputObservable1 = inputs
.map { first, second in
return true // or false depending on the values of first & second.
}
let outputObservable2 = inputs
.map { first, second in
return true // or false depending on the values of first & second.
}
I have written code like this to listen for changes in Post object.
notification = Post.allObjects(in: RLMRealm.encryptedRealm()! as! RLMRealm).addNotificationBlock({ (results, changes, error) in
let pred = NSPredicate(format: "tag == %#", self.postTag)
self.posts = CommonResult.objects(with: pred).sortedResults(usingKeyPath: "id", ascending: true)
if let _ = changes {
if (changes!.insertions.count > 0 || changes!.deletions.count > 0 || changes!.modifications.count > 0) {
self.tblListing.reloadData()
}
}
})
In my Post object, there are 2 property. One is 'rowHeight' and another is 'isLikeByMyself'.
I want to reload tableview only if 'isLikeByMyself' is changed. How shall I do? Is it possible?
You have at least two options.
If you don't have many Post objects, you may want to consider registering object notifications on each of them. Object notifications tell you which properties were changed and how, so you can use that information to reload the table view. However, you will need to register a separate notification on each Post object, which may not be practical if you have a large number of them.
Here is an alternate idea. Add an ignored boolean property to Post called something like isLikeWasChanged, and add a Swift didSet block that will set isLikeWasChanged to true any time you modify isLikeByMyself. Then, in your existing collection observation block, reload the table view only if at least one isLikeWasChanged is true, remembering to set them all back to false before you leave the block.
I am new to Firebase and not sure how to best explain this but I will try.
I am trying to have my app create an entry for each user. Then each user entry has multiple (0 through n) sub-entries where each sub-entry is a simple string. Basically there is a user-id (the main entry) and their tasks are the sub-entries.
Now my problem is whenever I push data (the sub-entries) to the main entries, all of the previous sub-entries are deleted and only the most recent one is pushed. I have been looking through the documentation and Googling like crazy but nothing seems to work.
I have tried this:
#IBAction func testWrite(sender: AnyObject) {
let def = NSUserDefaults.standardUserDefaults()
let uid = def.valueForKey("uid")
let root = Firebase(url: getFirebaseURL())
let text = self.tempText.text!
let dataRef = root.childByAppendingPath(uid as! String)
let data = ["test": String(text)]
dataRef.setValue(data)
}
Which appends to the user-id entry fine, with a key of "test" and a value of the 'text'
So then I kill the app and change it to:
#IBAction func testWrite(sender: AnyObject) {
let def = NSUserDefaults.standardUserDefaults()
let uid = def.valueForKey("uid")
let root = Firebase(url: getFirebaseURL())
let text = self.tempText.text!
let dataRef = root.childByAppendingPath(uid as! String)
let data = ["CHANGED": String(text)]
dataRef.setValue(data)
}
And it pushes fine, but then the previous entry was just deleted and the only entry left is this one.
What I am trying to do is maybe incrementally (having a numbered key possibly?) add items one by one without having other entries deleted.
I hope this makes sense :P and any help is greatly appreciated!
What is happening here is, you are setting the entire branch (Users/UserID##), to a value, and that value is a single node Changed:<somestring>
Conceptually, it may help to think of the key you want to set as being just another branch e.g (Users/UserID##/TaskID##)
So conceptually, instead of approaching it like this:
Users/UserID = Key:Value
Approach it like this:
Users/UserID/Key = Value
Note: the branch Users/UserID/Key does not have to exist prior to you assigning it a value.
e.g you could change your reference to point at the subkey you want to add or change:
let dataRef = root.childByAppendingPath(uid as! String + "/Task001")
dataref.setValue(String(text))
I concur that what you are doing is a great way to start learning Firebase, and how it works. But once you get going, instead of generating and using your own key for your list of subtasks, do look into childByAutoId, it will automatically create the subkeys for you, plus much more, and is much easier to manage and code.
Documentation Here
Edit: Suggest referring to Frank's better answer below.
An alternative to #MtlDev's answer would be to use updateChildValues():
let data = ["CHANGED": String(text)]
dataRef.updateChildValues(data)
While setValue() replaces the current data with the new value, updateChildValues() updates it in place.
See the Firebase documentation on updating saved data.
The code is from a book. In terms of overall app architecture (MVC), it's part of the Model. The model has two main components:
An array of tags called tags
A dictionary of tag - query called searches
The app saves these pieces of data in the NSUserDefaults (iOS defaults system) and on iCloud. The following method is called when a change in iCloud is signaled. The parameter is an instance of NSNotification.userInfo
// add, update, or delete searches based on iCloud changes
func performUpdates(userInfo: [NSObject: AnyObject?]) {
// get changed keys NSArray; convert to [String]
let changedKeysObject = userInfo[NSUbiquitousKeyValueStoreChangedKeysKey]
let changedKeys = changedKeysObject as! [String]
// get NSUbiquitousKeyValueStore for updating
let keyValueStore = NSUbiquitousKeyValueStore.defaultStore()
// update searches based on iCloud changes
for key in changedKeys {
if let query = keyValueStore.stringForKey(key) {
saveQuery(query, forTag: key, saveToCloud: false)
} else {
searches.removeValueForKey(key)
tags = tags.filter{$0 != key}
updateUserDefaults(updateTags: true, updateSearches: true)
}
delegate.modelDataChanged() // update the view
}
}
My question is on the if - else inside the for loop. The for loop iterates over keys that where changed; either the user adds a new search, updates an existing search, or deletes a search. But, I don't understand the logic behind the if-else. Some clarifying thoughts would be appreciated. I've read it over and over but it doesn't tick with me.
if let query = keyValueStore.stringForKey(key)
means that if keyValueStore contains a string corresponding to key, then this string will be assigned to the constant query.
This is called "safe unwrapping":
inside the if let ... condition, the query is safely saved with saveQuery because using if let ... guarantees that the value of keyValueStore.stringForKey(key) won't be nil.
If the value is nil, then in the else branch, the filter method is used to update the tags array without the key we just processed: tags.filter{$0 != key} means "return all items in tags that are different from key" (the $0 represents the current item from the array processed by filter).