Detect if at least one property of object was modified - ios

I have class with many strings and mutable arrays. All this objects represent form data. So if I modify textfield or add new photo to array I keep this data in my properties of object.
How can I create validator that will check if some of that properties were changed.
For example I have add some photos to array. Then I save it. When I reopen this screen again I see saved photos, but right now I want to add new photos.
The initial state and state after I add new photos to array will be different and I want to track it.
The simplest way I see here save current state of all objects and then compare it with new stat.
Let's say I have next variables:
NSString *name;
NSString *lastName;
NSMutableArray *photos;
I can use for example KVC to detect if some object was modified, but it works for example for array. So if array was modified, we can suppose that the data was changed. But for example if I property name had string #"Alex" and then I put here string #"Alex" again from text field, I can compare it with previous state and current state and make a conclusion that state was changed if strings will be different. Is this right way?

You can try Method Swizzling, ex: http://nshipster.com/method-swizzling/. To hook whether your set method be called.
You can use KVC or Delegate to notify the changing.
You can use immutable object to make sure your object is not changed, then hook when it's created.

Related

RxSwift - Class property's binding

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.

If I am observing a collection via KVO, am I notified of changes to objects within the collection?

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:...]

Creating a copy of a PFObject

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.

Sending NSNotifications to all objects of a class

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.

Is there a way to mark a NSManagedObject as dirty?

In my NSManagedObject subclass I have an NSString ivar that splits up into an NSSet of entities. I'd like to be able to set the string and during a call to save, do the split, however, only setting the string will not trigger a dirty flag or a need to save.
You can implement the + (BOOL)contextShouldIgnoreUnmodeledPropertyChanges on you NSManagedObject subclass and return NO rather than the default (YES).
This should then cause the NSManagedObjectContext to be notified of changes properties even if they aren't represented by actual columns in the database.
I assume you mean "attribute" instead of "ivar". Your scheme of having a string being split into a set and then saving the set is perhaps debatable, but I guess that is not the issue here.
Why do you need to have the Managed Object marked as "dirty"? This is really not necessary. Just save it, dirty or not!
I do not know how you check the "dirtiness" of your managed object, but I assume you want this to trigger a save at a certain point. At that point you might just as well as check your own BOOL "dirtyFlag" which you can set as appropriate and keep available for checking.
It is always better to make these kinds of things explicit. Your code will become more readable and transparent.

Resources