iOS Azure Mobile Service Completion Method - ios

i try to get some data from Azure, all works fine.
My problem is to populate the Data in a table view.
Here is my Implementaion of the TableViewController (only the important):
#interface tableview ()
#property (strong, nonatomic) AzureService *ClientService;
#end
#implementation overview
#synthesize tableViewObject,tabledata,ClientService;
- (void)viewDidLoad {
[super viewDidLoad];
[self.tableViewObject setDelegate:self];
[self.tableViewObject setDataSource:self];
self.ClientService = [[AzureService alloc]init];
[self.ClientService DatamyWay:^
{
[self.tableViewObject reloadData];
NSLog(#"Reload Table after complete Request");
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [self.ClientService.loadedItems count];
}
#end
Here´s the Implementation of the method form the Service
- (void) DatamyWay:(completionBlock)completion
{
[self.table readWithCompletion:^(MSQueryResult *result, NSError *error) {
if(error) { // error is nil if no error occured
NSLog(#"ERROR %#", error);
} else {
[self.loadedItems addObjectsFromArray:result.items];
for(NSDictionary *item in result.items) { // items is NSArray of records that match query
NSLog(#"Location Name: %#", [item objectForKey:#"name"]);
}
}
}];
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
Here the debugger output
2015-06-17 22:24:02.848 type2[3041:301640] Reload Table after complete Request
2015-06-17 22:24:03.601 type2[3041:301640] Location Name: abc
2015-06-17 22:24:03.601 type2[3041:301640] Location Name: def
2015-06-17 22:24:03.602 type2[3041:301640] Location Name: ghj
2015-06-17 22:24:03.602 type2[3041:301640] Location Name: klm
so i´ve leraned the call is asynchron. i want to check when the Completion code is called. So i decided to write the "Reload" NS Log Message. As you can see in the Debugger output, the NSlog message from the Completion block writes before the NSlog message in the Service Method. so the numbersinRowSection can´t count and nothing will happen. I hope the problem is described clearly.
Regards

i know, i shouldn´t post my own answer. I hope someone can help this one.
Put the Completion block in the Request.
code that works in my Service Implementation
- (void) DatamyWay:(completionBlock)completion
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[self.table readWithCompletion:^(MSQueryResult *result, NSError *error) {
if(error) { // error is nil if no error occured
NSLog(#"ERROR %#", error);
} else {
[delegate.loadedItems addObjectsFromArray:result.items];
for(NSDictionary *item in result.items) { // items is NSArray of records that match query
NSLog(#"Location Name: %#", [item objectForKey:#"name"]);
}
completion();
}
}];
}

Related

Set text in Active Conversation in iMessage

I wrote some code to add text to the Messages.app input field in my iMessage extension.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"didSelect called");
NSLog(#"%d", 1);
[[self activeConversation] insertText:#"https://google.com" completionHandler:^(NSError * error) {
NSLog(#"Error happened");
NSLog(#"Error: %#", error);
}];
NSLog(#"%d", 2);
}
The strange part is that all of the normal logs are happening. The app will log "didSelect called", "1" and "2". However, the message - the Google url - isn't being inserted, and the error logs aren't being shown. So I don't really have a clue as to what's going wrong. Any idea's what I'm doing wrong?
Solution #1
Send correct reference from MessagesViewController to your view controller.
Check activeConversation value for nil:
if ([self activeConversation] != nil) {
[[self activeConversation] insertText:#"Some text" completionHandler:^(NSError * _Nullable error) {
NSLog(#"error: %#", error.localizedDescription);
}];
} else {
NSLog(#"Conversation is nil");
}
Solution #2
Create Singleton in iMessage extension name space.
In MessagesViewController in - (void)viewDidLoad setup reference to
your MSConversation: [[Conversation shared] activeConversation] = [self activeConversation];
Use [[Conversation shared] activeConversation] insertText: .... ];
for sending messages from any controllers.

Background fetch and refresh completed after viewDidLoad in iOS 10

I'm trying to implement background fetch as well as refresh in iOS 10.
I'm using XML parsing to parse the data and then storing it in a file in the document's directory. For parsing XML I'm using a custom class (XMLParser) that confirms the NSXMLParserDelegate protocol.
The background fetch works fine. But I'm having problems in displaying the refreshed data, both when I click on the refresh button as well as in viewDidLoad.
I'm calling the refreshData method in viewDidLoad.
Here's how far I've gotten.
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
//--Set background fetch--//
[application setMinimumBackgroundFetchInterval:UIApplicationBackgroundFetchIntervalMinimum];
}
...
#pragma mark Background data fetch methods
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
NSDate *fetchStart = [NSDate date];
ArtsViewController *artsViewController = (ArtsViewController *)self.window.rootViewController;
[artsViewController fetchNewDataWithCompletionHandler:^(UIBackgroundFetchResult result) {
completionHandler(result);
NSDate *fetchEnd = [NSDate date];
NSTimeInterval timeElapsed = [fetchEnd timeIntervalSinceDate:fetchStart];
NSLog(#"Background Fetch Duration: %f seconds", timeElapsed);
}];
}
ArtsViewController.h
#interface ArtsViewController : UIViewController <UIPageViewControllerDataSource>
#property BOOL newsAvailable;
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; // No problems here
#end
ArtsViewcontroller.m
#interface ArtsViewController ()
#property (nonatomic, strong) NSArray *arrNewsData;
-(void)refreshData;
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray;
#end
...
#implementation ArtsViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self refreshData];
//--Load the file that saves news--//
[self loadNews];
if (_newsAvailable == YES)
{
[self setupPageViewController];
}
else
{
[self showNoNewsMessage];
}
}
...
#pragma mark Data Fetch methods
-(void)refreshData{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
[self performNewFetchedDataActionsWithDataArray:dataArray];
}
else{
NSLog(#"%#", [error localizedDescription]);
}
}];
}
-(void)performNewFetchedDataActionsWithDataArray:(NSArray *)dataArray{
// 1. Initialize the arrNewsData array with the parsed data array.
if (self.arrNewsData != nil) {
self.arrNewsData = nil;
}
self.arrNewsData = [[NSArray alloc] initWithArray:dataArray];
// 2. Write the file and reload the view.
NSArray * paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString * docDirectory = [paths objectAtIndex:0];
NSString * newsFilePath = [NSString stringWithFormat:#"%#",[docDirectory stringByAppendingPathComponent:#"arts2"]]; // NewsFile
if (![self.arrNewsData writeToFile:newsFilePath atomically:YES]) {
_newsAvailable = NO;
NSLog(#"Couldn't save data.");
}
else
{
_newsAvailable = YES;
NSLog(#"Saved data.");
[self viewWillAppear:YES];
}
}
-(void)fetchNewDataWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
XMLParser *xmlParser = [[XMLParser alloc] initWithXMLURLString:ArtsNewsFeed];
[xmlParser startParsingWithCompletionHandler:^(BOOL success, NSArray *dataArray, NSError *error) {
if (success) {
NSDictionary *latestDataDict = [dataArray objectAtIndex:0];
NSString *latestTitle = [latestDataDict objectForKey:#"title"];
NSDictionary *existingDataDict = [self.arrNewsData objectAtIndex:0];
NSString *existingTitle = [existingDataDict objectForKey:#"title"];
if ([latestTitle isEqualToString:existingTitle]) {
completionHandler(UIBackgroundFetchResultNoData);
NSLog(#"No new data found.");
}
else{
[self performNewFetchedDataActionsWithDataArray:dataArray];
completionHandler(UIBackgroundFetchResultNewData);
NSLog(#"New data was fetched.");
}
}
else{
completionHandler(UIBackgroundFetchResultFailed);
NSLog(#"Failed to fetch new data.");
}
}];
}
...
#pragma mark IBActions
- (IBAction)reloadNews:(UIBarButtonItem *)sender
{
[self viewDidLoad];
}
I've debugged the application and found that after viewDidLoad
completes execution, the data file is written but the view isn't
updated. I've also tried calling the refreshData method in the main
thread, but there's no change.
after viewDidLoad is complete the showNoNewNews method is called.
I'm suspecting that my logic isn't wrong but implementation is. Threads at play here..
Any help would be appreciated.
Update:
Hope this helps those with similar problems...
I moved the logic of viewDidLoad to a different method, called the method for the first time in viewDidLoad and again in refreshData, after
[self performNewFetchedDataActionsWithDataArray:dataArray];

Places Autocomplete API stops updating predictions (iOS)

I've implemented the Places autocomplete API in my iOS app, using a UISearchBar and UITableView to house the autocomplete predictions.
The implementation works, but sometimes -[GMSAutocompleteTableDataSourceDelegate didUpdateAutocompletePredictionsForTableDataSource:] simply doesn't get called.
The -[GMSAutocompleteTableDataSourceDelegate didRequestAutocompletePredictionsForTableDataSource:] is always called when the GMSAutocompleteTableDataSource instance is passed the -[GMSAutocompleteTableDataSource sourceTextHasChanged:] method.
I would expect either the above update delegate method or the -[GMSAutocompleteTableDataSourceDelegate tableDataSource: didFailAutocompleteWithError:] to be called at some point in response to a request, but again, this doesn't seem to always be the case.
Every now and then I just simply stop receiving updates regardless of what gets typed in the search bar, and no delegate method other than the request one gets called. The implementations for the delegate methods:
#pragma mark - Autocomplete Table Data Source Delegate
- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
didAutocompleteWithPlace:(GMSPlace *)place {
NSString *state;
NSString *stateAbbreviation;
NSArray *addressComponents = place.addressComponents;
if (addressComponents) {
// Type of addressComponent is <GMSAddressComponent *> (absent from public header UPDATE: Is present in 1.12.)
for (GMSAddressComponent *addressComponent in addressComponents) {
if ([addressComponent.type isEqualToString:#"administrative_area_level_1"]) {
state = addressComponent.name;
break;
}
}
}
if (state && [[_stateAbbreviations allKeys] containsObject:state]) {
stateAbbreviation = _stateAbbreviations[state];
} else {
stateAbbreviation = #"";
}
NSLog(#"City: <%#> State: <%#> Abbr: <%#>", place.name, state, stateAbbreviation);
}
- (void)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
didFailAutocompleteWithError:(NSError *)error {
NSLog(#"Error: %#", [error description]);
}
- (BOOL)tableDataSource:(GMSAutocompleteTableDataSource *)tableDataSource
didSelectPrediction:(GMSAutocompletePrediction *)prediction {
NSLog(#"Selected Prediction: <%#>", prediction);
return YES;
}
- (void)didUpdateAutocompletePredictionsForTableDataSource:
(GMSAutocompleteTableDataSource *)tableDataSource {
NSLog(#"-[%# %#] called.", self.class, NSStringFromSelector(_cmd));
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
// Reload table data.
[_searchResultsTableView reloadData];
}
- (void)didRequestAutocompletePredictionsForTableDataSource:
(GMSAutocompleteTableDataSource *)tableDataSource {
NSLog(#"-[%# %#] called.", self.class, NSStringFromSelector(_cmd));
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
// Reload table data.
[_searchResultsTableView reloadData];
}

iOS- Saving data inside a block

I've been struggling on this for several days so any would be appreciated. I'm trying to save the players array below and display it in a UITableView. I'd like to save it so I can display the local player's friends. I've tried several different things but something that looks it's working for others is this.
__block NSArray *friends;
- (void) loadPlayerData: (NSArray *) identifiers {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (players != nil) {
friends = players;
NSLog(#"Inside: %#", friends); //Properly shows the array
}
}];
NSLog(#"Outside: %#", friends): //Doesn't work, shows nil
}
But friends is still nil/null afterwards. Am I doing something wrong? Is there any way to save players and use it in a UITableView? Thanks.
***EDIT***
So here's the solution I put together.
typedef void(^CallbackBlock)(id object);
+ (void) retrieveFriends: (CallbackBlock)callback {
GKLocalPlayer *lp = [GKLocalPlayer localPlayer];
if (lp.authenticated) {
[lp loadFriendsWithCompletionHandler:^(NSArray *friends, NSError *error) {
if (friends != nil) {
[self loadPlayerDataWithIdentifiers:friends callback:^(NSArray *playersInfo) {
if (callback) callback(playersInfo);
}];
}
}];
}
}
+ (void) loadPlayerDataWithIdentifiers: (NSArray *) identifiers callback:(CallbackBlock)callback {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (players != nil) {
if (callback) callback(players);
}
}];
}
The only thing is, my UITableView is in another class so I tried doing this and making the two methods above public. info isn't printing out anything. Any ideas?
[GameCenterHelper retrieveFriends:^(NSArray *info) {
NSLog(#"Friends Info: %#", info);
}];
Use callback blocks.
typedef void(^CallbackBlock)(id object);
- (void)loadPlayerDataWithIdentifiers:(NSArray *)identifiers callback:(CallbackBlock)callback {
[GKPlayer loadPlayersForIdentifiers:identifiers withCompletionHandler:^(NSArray *players, NSError *error) {
if (error != nil) {
// Handle the error.
}
if (callback) callback(players);
}];
}
You imply in your question that this is for a table view. If so, you need to reload your table after the data has been loaded.
[self loadPlayerDataWithIdentifiers:identifiers callback:^(NSArray *players) {
self.players = players;
[self.tableView reloadData];
}];
Crimson Chris is correct.
The other option is use GCD to wait for the response to comeback.
Or change this to synchronous call as you want to get the results immediately.

didSelectRowAtIndexPath not working with NSNotificationCenter

I have a MasterDetail app that I am working on that will be used to show players (as in sports) and detail stats. The list of players is called from a Postgres Database and parsed from JSON using the JSONModel. So far, I am able to get all of the data that I need from the Postgres DB, and display it perfectly in the MasterView. I am using the NSNotificationCenter to pass the data from the Master to the Detail view (I fetch the data using a function in the MasterView). I am able to pass the data accurately to the Detail view, but for some reason, my didSelectRowAtIndexPath is not working right. I obviously did something wrong, but I have no idea what it is. Here are the important parts of the code:
In the MasterViewController.m viewDidAppear:
-(void)viewDidAppear:(BOOL)animated
{
//fetch the feed from the Postgres Database
[JSONHTTPClient getJSONFromURLWithString:#"http://myurl" completion:^(NSDictionary *json, JSONModelError *err) {
NSError* error = nil;
_feed = [[PostgresFeed alloc]initWithDictionary:json error:&error];
//Print the data fethced to NSLog in JSON format
NSLog(#"Players: %#", _feed.players);
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil userInfo:json];
//reload the table view after data collected
[self.tableView reloadData];
}];
}
and I collect that info in my DetailViewController.m like so:
- (void)handleNotification:(NSNotification *) notification
{
NSLog(#"%#", notification.userInfo);
NSArray *playerData = [notification.userInfo objectForKey:#"player"];
NSDictionary *firstElement = [playerData objectAtIndex:0];
nameLabel.text = [firstElement objectForKey:#"name"];
}
and then my didSelectRowAtIndexPath in my MasterViewController
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
Player *selectedPlayer = [_players objectAtIndex:indexPath.row];
if (_delegate) {
[_delegate selectedPlayer:slectedPlayer];
}
}
There you have it. If you want me to post more or if you want code from the DetailViewController.m, MasterViewController.h, or PlayerSelectionDelegate.h, just let me know.
As a note, I originally created this based off of the Ray Wenderlich iPad SplitView app tutorial a while back. And yes, I am new to this all.
You need to place the NSNotification in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:YES];
//Here you have the NSDictionary from the json
[JSONHTTPClient getJSONFromURLWithString:#"http://myurl" completion:^(NSDictionary *json, JSONModelError *err) {
NSError* error = nil;
_feed = [[PostgresFeed alloc]initWithDictionary:json error:&error];
//Print the data fethced to NSLog in JSON format
NSLog(#"Players: %#", _feed.players);
[JSONHTTPClient getJSONFromURLWithString:#"http://myurl" completion:^(NSDictionary *json, JSONModelError *err) {
NSError* error = nil;
_feed = [[PostgresFeed alloc]initWithDictionary:json error:&error];
//Print the data fethced to NSLog in JSON format
NSLog(#"Players: %#", _feed.players);
//Assuming that you have the players in the same order as your list
[[NSNotificationCenter defaultCenter] postNotificationName:#"myNotification" object:nil userInfo:[[json objectForKey:#"players"]objectAtIndex:indexPath.row]];
}];
Player *selectedPlayer = [_players objectAtIndex:indexPath.row];
if (_delegate) {
[_delegate selectedPlayer:slectedPlayer];
}
}
And in your DetailViewController :
- (void)handleNotification:(NSNotification *) notification
{
NSLog(#"%#", notification.userInfo);
nameLabel.text = [notification.userInfo objectForKey:#"name"];
}

Resources