Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data - Watch for changes and register local notifications

I'm relatively new to Core Data and KVC, but I would like some pointers on registering listeners for changes in Core Data objects. Here's the situation:

I have one NSManagedObject called Patient and another called Medication. A Patient may have many Medications, and a Medication has a startOn and endOn dates.

I'd like to somehow listen for changes to the endOn property of all Medication objects. When a change occurs, I would like to schedule a local notification on the iOS device. I've worked with local notifications before, but don't know where to put the code for it in this context.

Do I create the scheduling code in the App Delegate and somehow register the App Delegate to listen for changes in Medication objects? Does this need to be attached to the NSManagedObjectContext?

How is this done? Pointers would be much appreciated!

Thanks!

like image 565
Bill Shiff Avatar asked Jan 28 '11 22:01

Bill Shiff


People also ask

What is local notification in iOS?

Local notifications reach users whether your app is running in the foreground or the background and require no external infrastructure to send, which is why they are aptly termed “local.” These notifications simply require that a user's device is on in order to be received.

What is local notification?

Local notifications are scheduled by an app and delivered on the same device. They are suited for apps with time-based behaviors, such as calendar events. When you run your app on a device with Android OS 8.0 or above, Kony uses default channels that are mentioned in the localnotificationconfig.

How do I get local notification in Swift?

The swift file (say ViewController. swift ) in which you want to create local notification should contain below code: //MARK: - Button functions func buttonIsPressed(sender: UIButton) { println("buttonIsPressed function called \(UIButton. description())") var localNotification = UILocalNotification() localNotification.

How do I use notifications in Swiftui?

If you run your app now, press the first button to request notification permission, then press the second to add an actual notification.


2 Answers

With Key Value Observing, you need some instance to do the observing. Sometimes that can be the same object that calls -setEndOn: on Medication; sometimes it might have to be something else. Let's assume that your app has a MedicationManager class - of which one instance is created. And, further assume that MedicationManager has an instance method -createMedicationWithName:startOn:endOn: like this:

- (Medication*) createMedicationWithName:(NSString*)medName startOn:(NSDate*)startDate endOn:(NSDate*)endDate
    {
    //  Create and configure a new instance of the Compound entity 
    Medication *newMedication = (Medication *)[NSEntityDescription insertNewObjectForEntityForName:@"Medication"
                                                inManagedObjectContext:[self managedObjectContext]];
    [newMedication setName:medName];
    [newMedication setStartOn:startDate];
    [newMedication setEndOn:endDate];

    //  Set up KVO
    [newMedication addObserver:self
                    forKeyPath:@"endOn"
                    options:NSKeyValueObservingOptionNew
                    context:nil];

    return newCompound;
    }


- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                                                    change:(NSDictionary *)change 
                                                    context:(void *)context
    {
    if ([keyPath isEqualToString:@"endOn"])
        {
        //  ... schedule local notification on the iOS device for (Medication*)object.
        return;
        }
    }

Or something like that.

Note, when you delete a Medication, then you would want to removeObserver... Also, when launching your app, you would need to establish MedicationManager as observer for existing Medications. I think that this could be as simple as iterating over all Medications and calling addObserver for each. If you have many Medications, then you might want to do this in a more 'lazy' manner (i.e., in -awakeFromFetch).

like image 163
westsider Avatar answered Sep 24 '22 06:09

westsider


You will have to register observers when objects are fetched from the store the first time as well as when they are created. Instead of having to loop through all of the entries when you first fetch (which is error prone especially on the iPhone when unmodified fetched objects can be faulted when not retained) just use the awakeFromFetch and awakeFromInsert messages.

Also, in the example code below you can also keep up with the Patient's aggregate information like the soonest startOn and the soonest endOn by making transient properties on the Patient which store this information. The following code observes changes to the endOn in the Medication and offers you the ability to update the Patient's aforementioned transient "soonest endOn" or "soonest startOn"

- (void)addMyObservers
{
    registeredObservers_ = YES;
    [self addObserver:self forKeyPath:@"endOn" options:NSKeyValueObservingOptionNew context:nil]; 
}

- (void)awakeFromInsert 
{
    // called when you create this object
    [super awakeFromInsert];
    [self addMyObservers];
}

- (void)awakeFromFetch
{
    // called when you fetch this old object from the store
    [super awakeFromFetch];
    [self addMyObservers];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if([keyPath isEqualToString:@"endOn"]) 
    {
        id newValue = [change objectForKey:NSKeyValueChangeNewKey];
        // someone changed endOn so do something with this "newValue"
        // check to see if the Patient needs the transient property for the soonest medication updated
        // update any local notification schedule 
    }
}

// this is only required if you want to update the Patient's transient property for the soonest endOn or 
- (void)setPatient:(Patient *)patient
{
    [self willChangeValueForKey:@"patient"];
    [self setPrimitivePatient:patient];
    [self didChangeValueForKey:@"patient"];

    // check to see if the Patient needs the transient property for the soonest medication updated
}
like image 28
Brent Priddy Avatar answered Sep 20 '22 06:09

Brent Priddy