简体   繁体   中英

iOS 8 widget, sharing data between app groups forward and backward

I've a messages app and I started to create a widget. Updating the core data with the new messages happens when user open the app. My wish is when:

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler

called I will get the UIViewController and call the my get messages thread. Linking the UIViewController against my widget target gave me an error:

'sharedApplication' is unavailable....

So I canceled it.

What I'm trying to achieve: 1. widgetPerformUpdateWithCompletionHandler is being called 2. Application start the get messages thread/method 3. when it finish, it send back data to the widget using NSUserDefaults

My code:

1:

- (void)widgetPerformUpdateWithCompletionHandler:(void (^)(NCUpdateResult))completionHandler
{
    // Perform any setup necessary in order to update the view.

    [self startGetMessages];

    // If an error is encountered, use NCUpdateResultFailed
    // If there's no update required, use NCUpdateResultNoData
    // If there's an update, use NCUpdateResultNewData

    completionHandler(NCUpdateResultNewData);
}

2:

- (void)startGetMessages
{
    NSLog(@"%s", __PRETTY_FUNCTION__);

    NSBundle *deviceBundle = [NSBundle mainBundle];
    UIStoryboard *storyboard = [UIStoryboard storyboardWithName:@"Main" bundle:deviceBundle];
    id MainController = [storyboard instantiateViewControllerWithIdentifier:@"MainTableViewController"];
    SEL getMessagesSelector = NSSelectorFromString(@"startGetMessages:");

    if (MainController)
    {
        NSThread *startGetMessagesThread = [[NSThread alloc] initWithTarget:MainController
                                                                   selector:getMessagesSelector
                                                                     object:StringForInt(HRTableDataSourceKindUpdate)];
        [startGetMessagesThread start];
    }
}

3:

- (void)notifyWidgetForChanges
{

    __block NSMutableDictionary *newMessages = [NSMutableDictionary new];

    NSArray *results = [CoreDataPhotoRecord MR_findAllSortedBy:@"message.originalDate"
                                                     ascending:NO
                                                 withPredicate:[NSPredicate predicateWithFormat:@"(message.delete_message == %@) AND (message.type.integerValue == %d) AND (message.originalDate >= %@)",
                                                                @NO, NORMAL_MESSAGE, _notiftWidgetDate]];

    NSLog(@"%s, _notiftWidgetDate: %@, newMessages.count: %d", __PRETTY_FUNCTION__, _notiftWidgetDate, newMessages.count);

    [results enumerateObjectsUsingBlock:^(CoreDataPhotoRecord *photoDetails, NSUInteger idx, BOOL *stop)
    {
        if (photoDetails != nil && photoDetails.message != nil)
        {
            NSString *cleanMobile = [[ABAddressBook sharedAddressBook] getCleanMobile:photoDetails.message.mobile];
            Contact *person = [[ABAddressBook sharedAddressBook] findContactWithPhoneNumber:cleanMobile];
            ContactWidget *contact = [[ContactWidget alloc] init];
            contact.name = (person != nil && person.name != nil && person.name.length > 0) ? person.name : cleanMobile;

            [newMessages setObject:contact forKey:cleanMobile];
        }
    }];

    [SharedUtilities archiveObject:newMessages.copy forKey:MESSAGES_KEY_NEW widget:true];

    [DEFAULTS_WIDGET setObject:@"111" forKey:@"111"];
    [DEFAULTS_WIDGET synchronize];

    newMessages = nil;
    results = nil;
}

widgetDefaults = [[NSUserDefaults alloc] initWithSuiteName:WIDGET_GROUP_NAME];

Nothing is happen since the MainController in step 2 is nil. What can I do?

The nil problem occurs because you try to access application's storyboard from widget. It's not straightforward, since the containing app and widget extension are being kept in a separate bundles. So the [NSBundle mainBundle] in step 2) is not the same bundle as the one in your app.

Possible solutions include:

  • including the app's Main.storyboard in extensions bundle either via adding it to Copy Bundle resources list at widget's target Build Phases tab or just adding widget target to Main.storyboard list of Target Membership

  • moving the code responsible for getting the messages from MainController startGetMessages: into a shared framework that will be accessible both from the app and the widget, preferably into a dedicated object.

The second one is way better. As a rule of thumb it's best to follow SOLID principles when doing the object-oriented programming, where S stands for single responsibility . It should not be a responsibility of view controller to provide the messages fetching system-wide. Creating a dedicated object that will have only one job - to get messages - and sharing it across the targets is a way to go.

Please consult the docs for the detailed explanation on how to create the shared framework : https://developer.apple.com/library/prerelease/ios/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM