Force asynchrounous Firebase query to execute synchronously? - objective-c

I'm designing an app that uses firebase to store user information. Below, I'm trying to write a method that queries the database, obtains the stored password, and checks it against the inputted password, returning a boolean based on whether or not they match.
-(BOOL) ValidateUser: (NSString*)username :(NSString*)password {
//initialize blockmutable true-false flag
__block bool loginFlag = true;
//initialize blockmutable password holder
__block NSString* passholder;
//check if user exists in database
//get ref to firebase
Firebase * ref = [[Firebase alloc] initWithUrl:kFirebaseURL];
//delve into users
Firebase * usersref = [ref childByAppendingPath:#"users"];
//search based on username
FQuery * queryRef = [[usersref queryOrderedByKey] queryEqualToValue: username];
//EXECUTION SKIPS THIS PORTION OF CODE
//get snapshot of things found
[queryRef observeEventType:FEventTypeChildAdded withBlock:^(FDataSnapshot *querySnapshot){
NSLog(#"%#", querySnapshot);
if no match found
if (querySnapshot.childrenCount == 0)
{
return false
loginFlag = false;
}
//otherwise store password of thing found
else
{
passholder = querySnapshot.value[#"hashed_password"];
NSLog(#"%#", passholder);
}
}];
//CODE SKIPS TO HERE
NSLog(#"%#", passholder);
//check inputted password against database password
if (![password isEqualToString:passholder])
{
//if they don't match, return false
loginFlag = false;
}
return loginFlag;
}
The problem, however, is that the method terminates and returns true before the firebase query runs. Essentially, the method executes, checks placeholder values against the inputted password, returns the value for the bool, AND ONLY THEN retrieves the password(within the block). I'm not sure how to force the block to run synchronously in order to actually return the correct boolean.
Is there any way to alter the flow of the method in order to actually have it return the correct boolean value? I'm at a loss.
Thanks so much
PS: I'm aware that Firebase supports a dedicated login functionailty (AuthUser and all that); however, this project is more of a proof of concept and so we're utilizing simple, unencrypted passwords stored in the main firebase.

To force your function to wait for some asynchronous action to complete, check out semaphores. Here's another StackOverflow question which tackles it: objective c - How do I wait for an asynchronously dispatched block to finish?

Related

ReactiveCocoa: Manually sending event

Ok, I have a signal that sends an event when a certain method of a protocol gets called in response to some data getting retrieved from the server:
self.dataReceivedSignal = [[self rac_signalForSelector:#selector(didReceiveData:) fromProtocol:#protocol(DataServiceDelegate)] mapReplace:#YES];
This signal is then used to fire another signal that formats and returns the data:
- (RACSignal *)dataSignal
{
return [RACSignal combineLatest:#[self.dataReceivedSignal] reduce:^id(NSNumber * received){
...
return my_data;
}];
}
This view controller just listens to this second signal to get the data.
This works fine.
The problem is, the second time I enter this view controller, I don't want to load the data again, so I save it locally and do this:
if (!self.alreadyHasData) {
self.dataService = [[DataService alloc] init];
self.dataService.delegate = self;
[self.dataService getData];
} else {
self.dataReceivedSignal = [RACSignal return:#YES];
}
In case I already have the data, I'm replacing the dataReceivedSignal with a new one that just sends #YES and completes.
This works too, but that if/else doesn't seem too functional to me. Is this the correct approach?
Thanks.
First of all you can exchange combineLatest to map.
If you want not reload data if it's already loaded, you can write something like this:
- (RACSignal *)dataSignal
{
if (!_dataSignal) {
RACMulticastConnection *dataConnection = [[self.dataReceivedSignal map:^id(NSNumber * received){
/// ...
return my_data;
}] multicast:[RACReplaySubject replaySubjectWithCapacity:1]];
// Only do all of the above after one subscriber has attached.
_dataSignal = [RACSignal defer:^{
[dataConnection connect];
return dataConnection.signal;
}];
}
return _dataSignal;
}
And don't matter how much subscribers signal will have, retrieve data block will be called only one time.
Simpler code = better code. I think you can solve task with simpler solution without RAC.

How to do multiple serial connection using nsurlconnection?

i am new to iPhone development so i don't know whether this is simple or complicated.
Here is my problem. I have a app which first connects to web service for authentication process. After authentication i need to get data of logged in user. So, how to connect to web services twice and serially using NSURLConnection. Serially means first login and then retrieve data. In my case, the request are sent in random order, means sometime it sends login request first and some time retrieve data request.
Can anyone please help this out.
Thanks.
Set the delegate of your NSURLConnection to self, and implement NSURLConnectionDelegate methods, there's a method named
- (void) connectionDidFinishLoading:(NSURLConnection*)connection
which tells you when the request was finished successfully so inside it you can call the second request.
Now your only problem is that you don't know which request to call second :) as you don't know which one was called first so what I usually do is simply, define some variable, simply an int or NSString as a tag, e.g.
Add these before your #implemenetation
#define TAG_LOGIN_REQUEST 1
#define TAG_DATA_REQUEST 2
now define this inside the class
int currentTag
now before you start the login request add this
currentTag = TAG_LOGIN_REQUEST;
and before you start the data request add this
currentTag = TAG_DATA_REQUEST;
whenever this delegate method gets called
- (void) connectionDidFinishLoading:(NSURLConnection*)connection
you simply check
if (tag == TAG_LOGIN_REQUEST) {
// send data request
}
else if (tag == TAG_DATA_REQUEST) {
// send login request
}
I hope this helps, if there's anything that's not clear, please tell me.
Just try to synchronise you calling. Here is a example:
request for login and wait for login response
get login response and call getData()
if login response if yes, request for data.
Hope this helps.. :)
EDIT:
maintain two global flag (variable). let loginReq and dataReq are those flag.
before calling getLogin(), make loginReq = true;
in your connectinDidFinishLoading check
if(loginReq == true){
loginReq = false;
dataReq = true;
getData();
}
else if(dataReq == true){
dataReq = false;
}

How do I return a structure?

I have this code that does not works since user is always null. It's weird since if I debug before returning data it's ok but after returning data is always null. How can I fix this? I really appreciate any comments, suggestions, or tips....
Notes: xcode 5 iOS 7
definition of data types
typedef struct {
char *locale;
} W_user;
.m test file
W_user *user;
-(void)test{
// user is always null
user = W_generateUserDefault();
}
methods
W_user *W_new() {
W_user *user = malloc(sizeof(W_user));
memset(user, 0x00, sizeof(W_user));
return user;
}
W_user *W_generateUserDefault() {
W_user *user = W_new();
user->locale = "es";
// but here user contains data
return user;
}
Try to change the test to this:
-(void)test{
// user is always null
W_user *user = W_generateUserDefault();
}
However, that's strange, because local variables hide global ones, not visa-versa.
Also, are you sure that you aren't debugging it in release mode? Try NSLog the user value to make sure it's nil.

Wait for something of variable time to complete before continuing method

I need to be able to halt a method while it waits for user input.
I tried using a while (true) loop to check to see if a boolean was set indicating that the operation had completed, but as I predicted, it made the application non-responsive.
How do you halt and resume a method after a variable has been set, without calling the method again, and without making the entire application non-responsive.
Here's basically what the program does
openFile method called
openFile method determines whether file has a password
if it does, display an alert to the user requesting the password
Here's the problem, halting the method until the password has been entered.
Any help appreciated.
Why do you want to halt the method? You could use a delegate. You present the alert view and register the delegate for the alert view. The delegate registers for the didDismissAlertViewWithButtonIndex method and acts according to the button. If the password was entered correctly and the OKAY button was tapped, you can continue with your process.
You can't. You need to split it up into two methods, one that does the initial check and then prompts for the password, then the second that uses the password to get the file.
You may need something like below.
class Program
{
static void Main(string[] args)
{
}
void YourFileReadMethod()
{
string password = string.Empty;
bool isPasswordProtected = CheckFileIsPasswordProtected();
if (isPasswordProtected)
{
Form passwordFrm = new Form();//This is your password dialogue
DialogResult hasEntered = DialogResult.No;
do
{
hasEntered = passwordFrm.ShowDialog();
password = (hasEntered == DialogResult.Yes) ?
passwordFrm.passwordTxt//password property/control value;
: string.Empty;
} while (hasEntered != DialogResult.Yes);
}
ReadFileMethod(password);
}
private void ReadFileMethod(string password)
{
//use the password value to open file if not String.Empty
}
bool CheckFileIsPasswordProtected()
{
//your logic which decides whether the file is password protected or not
//return true if password is required to open the file
return true;
}
}

How is it better to wait an asynchronous method to be finished in iPhone app?

everybody.
I want to understand, how i shoud procceed situations when an asynchronous method has "didFinish:#selector(SEL)" parameter.
My code example is:
//
// Authentication check
- ( void )authenticationSuccess: ( GDataServiceTicket* ) ticket
authenticatedWithError: ( NSError* ) error {
if ( error == nil )
{
NSLog( #"authentication success" );
}
else
{
NSLog( #"authentication error" );
}
}
//
- ( void ) fetchFeedOfSpreadsheets {
//create and authenticate to a google spreadsheet service
if ( !(mService) )
{
GDataServiceGoogleSpreadsheet *service = [self spreadsheetService];
[mService autorelease];
mService = [service retain];
}
// check autentication success ( invoke "authenticationSuccess" method for debug success & error )
[mService authenticateWithDelegate: self
didAuthenticateSelector:#selector(authenticationSuccess:
authenticatedWithError:) ];
// HERE I WANT TO MAKE A PAUSE AND WHAIT THE RESULT, EITHER I AUTHENTICATED OR NOT
// AND MAKE AN "IF" STATEMENT TO CONTINTUE WORKING ON SERVER, OR RETURN ERROR
//fetch retrieves the feed of spreadsheets entries
NSURL *feedURL = [ NSURL URLWithString: kGDataGoogleSpreadsheetsPrivateFullFeed ];
GDataServiceTicket *ticket;
ticket = [mService fetchFeedWithURL: feedURL
delegate: self
didFinishSelector: #selector(spreadsheetsTicket:finishedWithFeed:
error: ) ];
// HERE I WANT TO WAIT SECOND TIME. I WANT "spreadsheetsTicket:
// finishedWithFeed:error:" TO PROCCEED ERROR AND PUT A FEED IN SOME NSARRAY OBJECT
// AND AFTER THAT I WANT TO WORK WITH THAT NSARRAY RIGHT HERE
}
I's clear, that i can push the code i want into the end of "authenticationSuccess" method section, but it's also clear, that it's a wrong a way to solve the proble. There a number of situations like this, where i call an asynchronous method with a selector parameter, and i want to find a solution providing me a flexible code writing.
Thanks in advance.
It's a standard practice in Objective-C to put the code to be executed after the authentication in the authenticationSucess: method. You might not like it, but that is life.
Many people had the same complaint as you, so
on iOS 4 and later, there's something called blocks which allow you to write the code to be executed after the authentication in the method which initiates the authentication, as in
[mService authenticateAndExecute:^{
code to be executed when successfully authenticated ;
} whenError:^{
code to be executed when authentication failed;
} ];
But in this case you need to modify the API, which is possible by using categories. See this blog post by Mike Ash. He has many other posts on blocks on the same blog, which are also very instructive.
If you're going to use a library that works asynchronously (and therefore doesn't block your UI), you should have a good reason for trying to force it to work synchronously.
You should be checking for an authentication error at the end of your authenticationSuccess:authenticatedWithError: method, and calling the next request from there if there's a success. Similarly, in your spreadsheetsTicket:finishedWithFeed:error: check for an error, and continuing processing if there isn't one. It might be a better design to do that continued work in a separate method, but that's up to you.
Is there a specific reason you want to use the GData API in a synchronous fashion?