I have an object that can be selected by a user click. With the current requirements of the app, at any time, there is no more than one of these items selected at any point during app execution.
I implemented a mechanism to enforce this, as follows:
Each of these objects has a unique identifier as a property.
When each object is created, it subscribes to the NSNotificationCenter listening for the MY_OBJECT_SELECTED notification.
When each object is selected, it posts the MY_OBJECT_SELECTED notification, with its unique Id as part of the userInfo dictionary.
Then, when each object receives the notification, it checks to see if its id is the same as the one in the userInfo. If it is, it does nothing, but if it isn't, it sets itself to unselected.
Is this a decent approach to the problem? If not, how would you do it?
It is a decent way of doing it, although it is not very efficient. The more objects you have, the more time you spend comparing IDs. The easiest way is to store your object pointers and IDs in a map table (or similar) and remember the last selected object. Whenever you select a new object, you clear the selection flag of the last selected object, then look up the new object and set its selection flag. It requires you to keep a collection of your objects, though.
The time required to update selections with this approach is independent of the number of objects you have.
If the object is spread all over the app,i.e. if it is a member in various classes. You can have a global object of same type and assign it to only that object which has been touched. In steps it will be like:
Have a global variable of object type.
At any object touch assign globalObject = currentObject;
do all operations on globalObject throughout app like calling methods and modifying object members(have a check for nil to ensure safety).
Reassign to different object with new touch.
Related
I have an question about binding:
I have an array of objects of my custom class: Array. Every object can be updated (change his properties value) in bg.
Also I have separated Controller, which take and store one object from list as variable and can update it (object still the same, so in list it will be updated too)
Is there any way to bind all object.property -> UILabels on Controller in way, when property changes automatically call label update?
Of course, there are multiple ways how to do it, but from your description I would use some kind of subject (because u said there will be changes in background so you will probably need hot observable )....For example Variable or PublishSubject. So you can crate
let myArrayStream: Variable<[MyObject]> = Variable([])
you can pass this variable as dependency to wherever you want, on one side you can subscribe to it, on the other side you can update it's value.
I assume not, but always like to double check.
Let's say I have an NSMutableArray I'm KVObserving. It holds a bunch of Employee object. If I fully assign the array, then no doubt I'll observe the change. However, what if I change one of the Employee objects references/value/objects in the array, such as employee.salary.
I assume there is no KVO notification, here correct?
And if you ever wanted something like this, how would it be achieved?
One quick nitpick: you can't be key-value observing an NSMutableArray. That's not what KVO does. What you're doing is key-value observing some property (say "employees") of some object. That property may be typed as an NSMutableArray (although that's a terrible idea) or merely backed by an NSMutableArray. But you're not observing the array. You are observing the object for changes in its employees property.
Key-value observing a collection property does not observe the properties of the objects in the collection. In general, there's no way to observe all properties, wholesale, of any object.
If you want to observe some specific property or properties of the objects in a collection, you should:
Consolidate all mutations of that collection to separate methods. You should already have done this to make your employees property KVO-compliant. In particular, I recommend that you implement the indexed collection mutating accessors.
Within those methods, use -addObserver:toObjectsAtIndexes:forKeyPath:options:context: and -removeObserver:fromObjectsAtIndexes:forKeyPath:context: to start and stop observing some key path(s) of the elements that are being added to or removed from the collection. You need to do this in the setter (-setEmployees:) for the employees property, too (stop observing all of the elements of the old array, replace it with the new array, start observing all of the elements of the new array).
Don't forget to stop observing the elements before the array is released, for example in -dealloc.
Try this :
[[employee mutableArrayValueForKey:#"salary"] addObject:...]
I am in a situation where I allow the user to download a PFObject and modify it locally, and they can then either cancel the changes or hit Done, which will dismiss the editing interface but NOT upload the changes to Parse yet. They need to hit Save on the previous screen to write all changes to the database at once.
The problem is once the PFObject is modified, you cannot revert it to its prior state without refetching from the database. But I cannot always refetch the data from the database every time they hit Cancel because the prior state may not be uploaded to Parse yet (and that's a bad UX making them wait to discard changes that are only stored locally).
For example, imagine the user taps to edit the PFObject, they make changes then hit Done, then tap on it again and further edit the object, then hit Cancel. In this case, the object needs to be reverted to its prior state, but that state has not been uploaded to Parse yet. So I cannot refetch the data from the database to revert changes otherwise it would overwrite the changes they made the first time.
To solve this problem, I would simply fetch the PFObject and store a copy of it. I'd call that the transient object. I would have another property that stores the real object. The user would modify the transient object, and when they hit Cancel I would simply set that to nil, if they instead hit Done I would set the real object equal to the transient object, and once they finally hit Save I would save the real object to the database. That way I can be sure changes aren't being made to the real object until the user commits the changes. The problem is, PFObject does not adopt the NSCopying protocol (not sure why), therefore I cannot create a copy of the PFObject. Any change I make to it affects the real object.
How can this be resolved, without modifying the app's design that allows control over when the data is committed and later saved? Is there a way to extend PFObject and adopt NSCopying, has it been done before?
I did consider storing the attributes of the object in a dictionary and allow the user to edit that instead, then upon commit set each of those attributes on the PFObject. The problem with this solution arises with complex structures. In this app, I allow the user to modify multiple arrays that contain multiple PFObjects. It's just infeasible to try to recreate and later merge changes with complex structures like this beyond a single simple PFObject.
I ran into this same problem. I did not make any changes directly to the PFObject, but rather, saved the updates in an NSDictionary. When the user clicks the done button, I then update the PFObject and saveInBackground. I don't think there is a "discard local changes" option for PFObject. If you don't do this, the only option is to throw out the existing PFObject and fetch again.
Regarding the NSDictionary comment, perhaps NSArray would be better. The implementation really depends on your specific program, but I'll give a quick example. The NSArray we'll call instructionArray. Imagine there are 3 sections in a tableView. Also assume that the data source for each section is an NSArray of PFObjects. Now say you want to set the age property of each PFObject in Section 2 to 35.
Add an NSArray object (corresponding to an instruction to carry out) to the instructionArray. This instruction to carry out could have the form
Section to update
Property to update
Value to update to
So the object you'll add is #[#(2),#"age",#(35)];
Given that the user is probably carrying out a finite amount of instructions, it might not be that performance heavy to loop through the instructionArray in cellForRowAtIndexPath so when a cell uses its corresponding PFObject to figure out what to display, it can loop through the instructions after and change what is displayed as if the PFObject was updated.
When the save button is touched, loop through the instructions and actually edit the PFObjects themselves.
If you need the instructions to handle specific objects rather than sections, then you just have to update the structure of the instructionArray. Maybe you could include an identifier to indicate what type of instruction it is.
I'm losing my mind around this question.
So I have a Core Data setup in my iOS app done this way:
http://www.cocoanetics.com/2012/07/multi-context-coredata/
I then insert an object by creating a temporary MOC (as explained in the blog post) and perform saves on all 3 contexts in performBlock: methods.
In a view controller I have an NSFetchedResultsController and it gets notified that I did indeed insert a new object. The problem is that the NSFetchedResultsChangeInsert is fired twice and each time the object that is passed trough has a different objectID (it also is a different object instance in memory). What happens is that I then have 2 rows inserted in my table view but un the SQL database there is only one new. It then of course crashes when I scroll to the bottom of the table view.
If I also perform some updates on the object I get NSFetchedResultsChangeUpdate called only once and with the objectID that was passed in the second NSFetchedResultsChangeInsert call.
The first ID looks like this:
<x-coredata:///ReceivedMessage/t605BB9A7-A04E-4B89-B568-65B12E8C259A2>
The second (and all consequent ones) like this:
<x-coredata://02A917C5-850F-4C67-B8E4-1C5790CF3919/ReceivedMessage/p28>
What could this be? Am I missing out something obvious?
PS: I also checked if the notification comes from the same context, thread, etc. It does.
The two IDs you are seeing may very well represent one object. The difference between them is just that the first one is a temporary object ID, assigned to the object on creation, and the second one is the permanent object ID, assigned to the object when it gets stored to the managed object store (see NSManagedObjectID's isTemporaryID).
To work around this issue you could call NSManagedObjectContext's obtainPermanentIDsForObjects:error: just before you save the temporary MOC. This way the inserted object will have just one ID during the save propagation and the NSFetchedResultsControllerDelegate methods should get called just once.
I have a managed object which acts as a playlist, it has a to-many relationship with the playlist items. There can be multiple playlists, but only one "active" playlist. The active playlist is indicated by a boolean attribute on the managed object.
I have the number of items in the active playlist displayed as the badge on a tab bar item. The view controller that the tab bar item represents is listening for a specific notification which is fired when the contents of the active playlist are updated.
I have implemented this in what I feel is a clumsy way and would like to do it better. It does work at the moment but I'm not happy with it.
Currently, each playlist object, on awakeFromFetch, checks to see if it is the active one, and if so registers itself (using key value observing) as an observer for the key path which is the key for the relationship. When it observes a change, it fires the notification, which causes the tab bar item to update itself.
If the playlist loses or gains active status it stops or starts observing itself appropriately, so notifications are only fired from the active playlist.
I would like to drop all of the self-observing KVO code as I am concerned about the various entry and exit points and when to add and remove observers appropriately. It seems too dirty.
I would like to just override didChangeValueForKey:, check and send my notification there if necessary, then call the super implementation. But this is expressly forbidden in the documentation:
didChangeValueForKey:
Invoked to inform the receiver that the value of a given property has changed.
-(void)didChangeValueForKey:(NSString *)key
Parameters
key
The name of the property that changed. Discussion For more details, see
Key-Value Observing Programming Guide.
You must not override this method.
So, what can I do?
I've just read the same documentation, but if you look at the top of the NSManagedObject documentation, it actually says "You are strongly discouraged..."
I guess it all depends on your implementation details. For example, I do the following on a data model that I can modify locally, and sync with a server:
- (void)didChangeValueForKey:(NSString *)key
{
[super didChangeValueForKey:key]; // MUST CALL THIS!
if ([key isEqualToString:NSStringFromSelector(#selector(name))] ||
[key isEqualToString:NSStringFromSelector(#selector(text))] ||
[key isEqualToString:NSStringFromSelector(#selector(filename))]
)
{
self.lastModified = [NSDate date];
}
}
I'm not sure why this would be considered bad. It's just saying "Do what you normally do. In addition, I'd like to set another property that depends on that change."
What you want is Key Value Observing. You should be able to register for that specific key, and get notified when it changes. Check this out too: Using KVO to observe changes to a property on an object inside a collection in Objective-C
I have solved this by making a separate object (a singleton I am using to manage my core data stack) the observer instead. All of the self-observing complexity has now gone and I do not need to worry about adding or removing observers on awakeFromFetch and so on.