Reputation: 1535
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?
Upvotes: 0
Views: 1020
Reputation: 1681
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
Upvotes: 1