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?
Related
I have a situation where I will be getting more than 25000 records from web service, it is sending using pagination technique.
so the problem is I just want to store the data so for that I am thinking to run it in a loop but in future records may vary (i.e 30000,50000 etc)
from backend I am getting on each page 10000 records,but i dont know how many times i have run the loop so how do I handle this problem?
-(void)vendorsListCalling:(NSInteger)pageIndex{
[[ServicesHandler new] callVendorDetailsServiceWithParams:#{#"pageno":#(pageIndex)} CompletionBLock:^(NSDictionary *response, NSError *error) {
if (error) {
NSLog(#"error log %#",error.localizedDescription);
}else{
NSDictionary *dict = response[#"params"][#"data"];
[vendorDictionay addEntriesFromDictionary:dict];
pageCount++;
[[NSUserDefaults standardUserDefaults] setObject:vendorDictionay forKey:#"vendorsDict"];
}
}];
}
above block is where i stuck .
Any suggestions would be more appreciated.
You can store data into sqlite database. And for recursive calling for service, you can modify the same method as,
-(void)vendorsListCalling:(NSInteger)pageIndex {
if (!loader) {
//Write code to Show your loader here
}
[[ServicesHandler new] callVendorDetailsServiceWithParams:#{#"pageno":#(pageIndex)} CompletionBLock:^(NSDictionary *response, NSError *error) {
if (error) {
NSLog(#"error log %#",error.localizedDescription);
//If it fails you need to call the service again with the same Index
[self vendorsListCalling:pageCount];
} else {
if (!response[#"params"][#"data"]) {
//Stop loader since you didn't received any data
} else {
NSDictionary *dict = response[#"params"][#"data"];
[vendorDictionay addEntriesFromDictionary:dict];
pageCount++;
// Store Data in database here //
//Call service with incremented Index
[self vendorsListCalling:pageCount];
}
}
}];
}
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
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);
}
}];
}
I am now working on an app that works with BLE, Backend server and location. I am facing a problem which I am not sure how to get out of which is what people call "Callback hell". The entire CoreBluetooth framework in iOS is based on a delegate pattern, which until you can use the CBPeripheral has to go to at least 3 callbacks:
DidConnectToPeripheral
DidDiscoverServices
DidDiscoverCharacteristics
But in fact there could be many more, and every action you take with the device will come back as a callback to one of those functions. Now when I want to "Rent" this ble product, I must connect to it, after connecting send a requests to the server and get the user's current location, after that all happens I have to write a value in the bluetooth device and get confirmation. This would not be so difficult, but unfortunately each and every one of those stages is failable, so error handling needs to be added. Not to mention implementing timeout.
I am sure I am not the only one to approach such issues so I looked around and I found 2 things that might help:
the Advanced NSOperations talk in the wwdc 2015, but after trying for 4 days to make it work, it seems like the code is too buggy.
Promisekit but I couldn't find a way to wrap CoreBluetooth.
How are people with even more complicated apps deal with this? in swift or objc.
Some sample problematic code:
-(void)startRentalSessionWithLock:(DORLock *)lock timeOut:(NSTimeInterval)timeout forSuccess:(void (^)(DORRentalSession * session))successBlock failure:(failureBlock_t)failureBlock{
//we set the block to determine what happens
NSAssert(lock.peripheral, #"lock has to have peripheral to connect to");
if (!self.rentalSession) {
self.rentalSession = [[DORRentalSession alloc] initWithLock:nil andSessionDict:#{} active:NO];
}
self.rentalSession.lock = lock;
[self connectToLock:self.rentalSession.lock.peripheral timeOut:timeout completionBlock:^(CBPeripheral *peripheral, NSError *error) {
self.BTConnectionCompleted = nil;
if (!error) {
[[INTULocationManager sharedInstance] requestLocationWithDesiredAccuracy:INTULocationAccuracyHouse timeout:1 delayUntilAuthorized:YES block:^(CLLocation *currentLocation, INTULocationAccuracy achievedAccuracy, INTULocationStatus status) {
if (status == INTULocationStatusSuccess || status == INTULocationStatusTimedOut) {
[self startServerRentalForSessionLockWithUserLocation:currentLocation.coordinate forSuccess:^(DORRentalSession *session) {
if (self.rentalSession.lock.peripheral && self.rentalSession.lock.peripheral.state == CBPeripheralStateConnected) {
[self.rentalSession.lock.peripheral setNotifyValue:YES forCharacteristic:self.rentalSession.lock.charectaristics.sensorCharacteristic];
}else{
//shouldnt come here
}
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
if (self.rentalSession.lock.peripheral.state == CBPeripheralStateConnected) {
!self.rentalSession.lock.open ? [self sendUnlockBLECommandToSessionLock] : nil;
if (successBlock) {
successBlock(session);
}
}else{
[self endCurrentRentalSessionWithLocation:self.rentalSession.lock.latLng andPositionAcc:#(1) Success:^(DORRentalSession *session) {
if (failureBlock) {
failureBlock([[NSError alloc] initWithDomain:DonkeyErrorDomain code:46 userInfo:#{NSLocalizedDescriptionKey:#"Could't connect to lock"}],200);
}
} failure:^(NSError *error, NSInteger httpCode) {
if (failureBlock) {
failureBlock([[NSError alloc] initWithDomain:DonkeyErrorDomain code:45 userInfo:#{NSLocalizedDescriptionKey:#"fatal error"}],200);
}
}];
}
});
} failure:^(NSError *error, NSInteger httpCode) {
if (failureBlock) {
failureBlock(error,httpCode);
}
}];
}else{
NSError *gpsError = [self donkeyGPSErrorWithINTULocationStatus:status];
if (failureBlock) {
failureBlock(gpsError,200);
}
}
}];
}else{
if (failureBlock) {
failureBlock(error,200);
}
}
}];
}
To get rid of this nested calls you can use GCD group + serial execution queue:
dispatch_queue_t queue = ddispatch_queue_create("com.example.queue", NULL);
dispatch_group_t group = dispatch_group_create();
// Add a task to the group
dispatch_group_async(group, queue, ^{
// Some asynchronous work
});
// Make dispatch_group_async and dispatch_group_sync calls here
// Callback to be executed when all scheduled tasks are completed.
dispatch_group_notify(serviceGroup,dispatch_get_main_queue(),^{
// Do smth when everything has finished
});
// wait for all tasks to complete
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
The other solution based on GCD groups is described here
I'm implementing v4.1 of the SDK for iOS and when I try to call for publishPermissions:, I get no callback.
For some reason everything works perfectly when I run logInWithReadPermissions:, but when I run logInWithPublishPermissions: it never hits my response handler. Nothing happens.
To test things out, I reset my loginManager before running logInWithPublishPermissions:, and to my surprise it worked then (aka NSLog(#"RESULT") is called).
Am I missing something about how the loginManager works? Shouldn't I be able to use it without resetting it?
// FacebookController.m
#implementation FacebookController
FBSDKLoginManager *loginManager;
static FacebookController *_shared = nil;
- (id)init {
self = [super init];
if (self != nil) {
userData = [[NSMutableDictionary alloc] init];
loginManager = [[FBSDKLoginManager alloc] init];
}
return self;
}
+ (id)getInstance {
if (!_shared) {
_shared = [[self alloc] init];
}
return _shared;
}
- (bool)hasPublishPermissions {
FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken];
if(accessToken != NULL){
NSSet *permissions = [accessToken permissions];
if([permissions containsObject:#"publish_actions"]){
return TRUE;
}
}
return FALSE;
}
- (void)requestPublishPermissionsWithDelegate:(id)aDelegate {
if(![self hasPublishPermissions]){
// FOR SOME REASON IT WORKS IF I RESET LOGIN MANAGER AS FOLLOWS
// loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithPublishPermissions:#[#"publish_actions"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
NSLog(#"RESULT: %#", result);
}];
}
}
- (void)connectToFacebookWithDelegate:(id)aDelegate {
FBSDKAccessToken *accessToken = [FBSDKAccessToken currentAccessToken];
if(accessToken != nil){
[aDelegate performSelector:#selector(facebookSignedIn)];
} else {
[loginManager logInWithReadPermissions:#[#"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) {
if (error) {
// Process error
NSLog(#"ERROR");
} else if (result.isCancelled) {
// Handle cancellations
NSLog(#"CANCELLED");
} else {
NSLog(#"SUCCESS");
[aDelegate performSelector:#selector(facebookSignedIn)];
}
}];
}
}
#end
Edit #1:
Including videos of it working and not working for the given scenarios:
Not working (loginManager reinitialization commented out):
https://dl.dropboxusercontent.com/u/14277258/not-working.mov
Working (loginManager reinitialized):
https://dl.dropboxusercontent.com/u/14277258/working.mov
Your video stack trace indicates you're calling the request for publish permissions inside the handler for your initial login. This should be avoided:
You're causing another login after the user has already granted you some permissions - it's not very good for the user to have to see another login dialog immediately after completing one.
You're asking for publish permissions when you don't need it - this may violate Facebook developer policies and again is not the best user experience. Instead you should asking for publish only when you need it (i.e., at the time of sharing).
If you really insist, you can dispatch your second login call asynchronously so that the first request finishes entirely but I wouldn't recommend it. We can probably update the SDK to detect this and log though so it's not as confusing.