I have a query regarding concurrency in Azure mobile client SDK.
for windows I found Conflict link for handling concurrency but I could not found the same for iOS client SDK.
Can anyone please suggest or help how to handle concurrency in iOS client SDK.
Here is the old Mobile Services page for handling conflicts with the iOS SDK. The setup for offline sync with the iOS SDK hasn't changed since then.
1) Set up an MSSyncContextDelegate and pass it to the MSSyncContext constructor when you create it.
2) Implement tableOperation:(MSTableOperation *)operation onComplete:(MSSyncItemBlock)completion in your delegate. After executing the operation, check for an MSErrorPreconditionFailed error code and decide what to do from there based on your app's needs.
- (void)tableOperation:(MSTableOperation *)operation onComplete:(MSSyncItemBlock)completion
{
[operation executeWithCompletion:^(NSDictionary *item, NSError *error) {
NSDictionary *serverItem = [error.userInfo objectForKey:MSErrorServerItemKey];
if (error.code == MSErrorPreconditionFailed) {
QSUIAlertViewWithBlock *alert = [[QSUIAlertViewWithBlock alloc] initWithCallback:^(NSInteger buttonIndex) {
if (buttonIndex == 1) { // Client
NSMutableDictionary *adjustedItem = [operation.item mutableCopy];
[adjustedItem setValue:[serverItem objectForKey:MSSystemColumnVersion] forKey:MSSystemColumnVersion];
operation.item = adjustedItem;
[self doOperation:operation complete:completion];
return;
} else if (buttonIndex == 2) { // Server
NSDictionary *serverItem = [error.userInfo objectForKey:MSErrorServerItemKey];
completion(serverItem, nil);
} else { // Cancel
[operation cancelPush];
completion(nil, error);
}
}];
NSString *message = [NSString stringWithFormat:#"Client value: %#\nServer value: %#", operation.item[#"text"], serverItem[#"text"]];
[alert showAlertWithTitle:#"Server Conflict"
message:message
cancelButtonTitle:#"Cancel"
otherButtonTitles:[NSArray arrayWithObjects:#"Use Client", #"Use Server", nil]];
} else {
completion(item, error);
}
}];
}
Related
As Spotify framework is upgraded and new classes like SPTRemoteApp has
been introduced, I got totally frustrated to integrate it in old app
as they have changed almost every thing. So I need help if someone can
tell me these following points how it works. Somehow I feel I can do
login part but about track list and its playback part seems not
properly understandable from the Spotify developer docs for iOS.
How to get login Url from new Spotify framework or how to login by clicking button without checking login URL content.
How to get playlist like previously we get playlist by calling this method
[SPTRequest playlistsForUserInSession:session callback:^(NSError *error, SPTListPage *object) {
playListCount = [object.items count] - 1;
if (object.items) {
[object.items enumerateObjectsUsingBlock:^(SPTPartialPlaylist *obj, NSUInteger idx, BOOL *stop) {
[playlistURI addObject:obj.uri];
if (idx == object.items.count - 1) {
[weakSelf requestsTracks:playlistURI withSession:session];
}
spotifySynching = NO;
}];
}else{
spotifySynching = NO;
[self stopAnimatingTotalSpinner];
[self updateProgressDisplays];
}
}];
How to maintain and renew session, nd when we should renew it.
What is the replacement of these methods
(SPTListPage *)object;
[object requestNextPageWithSession:session callback:^(NSError *error, SPTListPage *object) {
[tracksURI addObjectsFromArray:object.items];
if ([object hasNextPage]) {
[self hasNextTrack:object withSession:session withNewObject:newObject];
}else{
[self requestsTracks:newObject withSession:session];
}
}];
[SPTRequest requestItemAtURI:obj withSession:session callback:^(NSError *error, SPTPlaylistSnapshot *object) {
if (error != nil) {
NSLog(#"*** Auth error: %#", error);
return;
}
[tracksURI addObjectsFromArray:object.firstTrackPage.items];
if ([object.firstTrackPage hasNextPage]) {
[self hasNextTrack:object.firstTrackPage withSession:session withNewObject:newObject];
}else{
[self requestsTracks:newObject withSession:session];
}
}];
Please check and let me know.
Thanks
A part of code (I didn't write) is shown as deprecated in my Xcode project, here's the code block:
#pragma mark - Report Achievement Progress
static int reportAchievement(struct lua_State *state) {
[gameCenterAddOnInstance reportAchievementAction:[NSString stringWithCString:lua_tostring(state, 1) encoding:NSUTF8StringEncoding] percentComplete:(int)lua_tointeger(state, 2)];
return 1;
}
- (void) reportAchievementAction: (NSString*) identifier percentComplete: (float) percent
{
GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier: identifier];
if (achievement)
{
achievement.percentComplete = percent;
[achievement reportAchievementWithCompletionHandler:^(NSError *error)
{
if (error != nil)
{
NSLog(#"Error in reporting achievements: %#", error);
}
}];
}
}
According to Xcode, the depreciated part is:
reportAchievementWithCompletionHandler
Xcode suggests to use:
reportAchievements:WithCompletionHandler:
Instead. But, not being familiar with objective C, I wouldn't know where to start.
How to implement to new function?
Try this
[GKAchievement reportAchievements:#[achievement] withCompletionHandler:^(NSError *error)
{
if (error != nil)
{
NSLog(#"Error in reporting achievements: %#", error);
}
}];
Apple replaced the instance method - reportAchievementWithCompletionHandler: with the class method + reportAchievements:withCompletionHandler:. This allows you to report multiple achievements at once without having to call the instance method on every achievement object.
#[achievement] is shorthand for [NSArray arrayWithObjects:achievement, nil].
I am creating an iOS application in which I am integrating Office-365-SDK-for-iOS for import contacts from outlook365.I am successfully able to authenticate with Microsoft Azure. But when I am fetching user and user's contacts then following error is coming-
Error Domain=Error in the Request Code=401 "The operation couldn’t be completed. (Error in the Request error 401.)
Here is my code for authentication and get client
//Acquire access and refresh tokens from Azure AD for the user.
-(void)acquireAuthTokenWithResourceId:(NSString *)resourceId completionHandler:(void (^)(BOOL authenticated))completionBlock
{
ADAuthenticationError *error;
self.authContext = [ADAuthenticationContext authenticationContextWithAuthority:OutlookAuthority error:&error];
[self.authContext acquireTokenWithResource:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]
completionBlock:^(ADAuthenticationResult *result)
{
if (AD_SUCCEEDED != result.status)
{
completionBlock(NO);
}
else
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:result.tokenCacheStoreItem.userInformation.userId
forKey:#"LogInUser"];
[userDefaults synchronize];
self.dependencyResolver = [[ADALDependencyResolver alloc] initWithContext:self.authContext
resourceId:OutlookRsourceId
clientId:OutlookClientId
redirectUri:[NSURL URLWithString:OutlookRedirectUrl]];
completionBlock(YES);
}
}];
}
- (void) getClient:(void (^) (MSOutlookServicesClient *))callback
{
OutlookAuthManager* authenticationController = [OutlookAuthManager sharedInstance];
[authenticationController acquireAuthTokenWithResourceId:OutlookRsourceId completionHandler:^(BOOL authenticated)
{
if (authenticated)
{
callback([[MSOutlookServicesClient alloc] initWithUrl:#"https://outlook.office365.com/api/v1.0" dependencyResolver:[authenticationController dependencyResolver]]);
}
else
{
NSLog(#"Error in authentication");
}
}];
}
And following I am getting user-
[[OutlookAuthManager sharedInstance] getClient:^(MSOutlookServicesClient *client)
{
NSURLSessionTask* task = [[client getMe] readWithCallback:^(MSOutlookServicesUser *user, NSError *error)
{
if(error == nil)
{
dispatch_async(dispatch_get_main_queue(),
^{
NSLog(#"------>%#",user.DisplayName);
NSLog(#"------>%#",user.Alias);
NSLog(#"------>%#",user.Id);
NSLog(#"------>%#",user.MailboxGuid);
});
}
else
{
[client.resolver.logger logMessage:error.description withLevel:LOG_LEVEL_ERROR];
}
}];
[task resume];
}];
But That error is coming here.
Please help me
Thanks
Start by validating your access token. Since it's a 401 it's likely a problem there.
Similar to this question. Is it possible to cache the auth token to speed up login in Firebase with iOS?
I'm authenticating with FirebaseSimpleLogin. Here is my authentication code
- (void)checkAuthStatus{
Firebase *ref = [[Firebase alloc] initWithUrl:rootURL];
FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:ref];
[authClient checkAuthStatusWithBlock:^(NSError *error, FAUser *user)
{
if (error == nil)
{
if (user != nil)
{
if(user.thirdPartyUserData == nil){
//Email Login
}else{
//Facebook、Twitter Login
}
}else [self actionLogin];
}
else
{
NSString *message = [error.userInfo valueForKey:#"NSLocalizedDescription"];
[self performSelectorOnMainThread:#selector(showError:) withObject:message waitUntilDone:NO];
}
}];
}
I also this method with async blocks
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self checkAuthStatus];
});
This takes around 5 - 8 seconds until showing tableview data source.
I tried to run iFirefeed app but it takes almost same time.
I want to know more faster way if there is.
Anyone have idea or experience to be more faster for authentication?
I'm developing an iOS application with Simple Notification Service (SNS) from Amazon Web Services. At this point the app registers the device to a Topic and can receive push notifications, which are published to the Topic. It is possible to subscribe a device to many Topics.
Now I'm trying to unsubscribe a device from a specific Topic, but the SNSUnsubscribeRequest needs a SubscriptionARN. I've tried to use the EndpointARN from the device, but it seems I've to use an extra SubscriptionARN for the combination of EndpointARN and TopicARN. How do I get this ARN?
In this post: How do you get the arn of a subscription? they ask for the whole list of subscribers and compare each EndpointARN with the EndpointARN of the device. This cant be the right way i think.
Subscribe to Topic
// Check if endpoint exist
if (endpointARN == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self universalAlertsWithTitle:#"endpointARN not found!" andMessage:#"Please create an endpoint for this device before subscribe to topic"] show];
});
return NO;
}
// Create topic if not exist
NSString *topicARN = [self findTopicARNFor:topic];
if (!topicARN) {
[self createTopic:topic];
topicARN = [self findTopicARNFor:topic];
}
// Subscribe to topic if exist
if (topicARN) {
SNSSubscribeRequest *subscribeRequest = [[SNSSubscribeRequest alloc] initWithTopicArn:topicARN andProtocol:#"application" andEndpoint:endpointARN];
SNSSubscribeResponse *subscribeResponse = [snsClient subscribe:subscribeRequest];
if (subscribeResponse.error != nil) {
NSLog(#"Error: %#", subscribeResponse.error);
dispatch_async(dispatch_get_main_queue(), ^{
[[self universalAlertsWithTitle:#"Subscription Error" andMessage:subscribeResponse.error.userInfo.description] show];
});
return NO;
}
}
return YES;
The method findTopicARNForTopic already iterates over the list of Topics and compare the suffix with the topic name. I really don't know if this is the best practice.
Unsubscribe from Topic
NSString *topicARN = [self findTopicARNFor:topic];
if (topicARN) {
SNSUnsubscribeRequest *unsubscribeRequest = [[SNSUnsubscribeRequest alloc] initWithSubscriptionArn:topicARN];
SNSUnsubscribeResponse *unsubscribeResponse = [snsClient unsubscribe:unsubscribeRequest];
if (unsubscribeResponse.error) {
NSLog(#"Error: %#", unsubscribeResponse.error);
}
}
For now I ask for the whole subscriber list and compare the EndpointARN with the EndpointARN of the Device. With the following method i get the subscription arn:
- (NSString *)findSubscriptionARNForTopicARN:(NSString *)topicARN
{
// Iterate over each subscription arn list for a topic arn
NSString *nextToken = nil;
do {
SNSListSubscriptionsByTopicRequest *listSubscriptionRequest = [[SNSListSubscriptionsByTopicRequest alloc] initWithTopicArn:topicARN];
SNSListSubscriptionsByTopicResponse *response = [snsClient listSubscriptionsByTopic:listSubscriptionRequest];
if (response.error) {
NSLog(#"Error: %#", response.error);
return nil;
}
// Compare endpoint arn of subscription arn with endpoint arn of this device
for (SNSSubscription *subscription in response.subscriptions) {
if ([subscription.endpoint isEqualToString:endpointARN]) {
return subscription.subscriptionArn;
}
}
nextToken = response.nextToken;
} while (nextToken != nil);
return nil;
}
and with this method i remove the device from a topic:
- (void)unsubscribeDeviceFromTopic:(NSString *)topic
{
NSString *subscriptionARN = [self findSubscriptionARNForTopic:topic];
if (subscriptionARN) {
SNSUnsubscribeRequest *unsubscribeRequest = [[SNSUnsubscribeRequest alloc] initWithSubscriptionArn:subscriptionARN];
SNSUnsubscribeResponse *unsubscribeResponse = [snsClient unsubscribe:unsubscribeRequest];
if (unsubscribeResponse.error) {
NSLog(#"Error: %#", unsubscribeResponse.error);
}
}
}
You could also store the SubscriptionArn in the SubscribeResponse and use this value in the UnSubscribeRequest.