ios - returning a value from asynchronous call using semaphores -


i need use nsurlsession make network calls. on basis of things, after receive response, need return nserror object.

i using semaphores make asynchronous call behave synchronously. problem is, err set inside call, semaphore ends (after

dispatch_semaphore_wait(semaphore, dispatch_time_forever);

), err becomes nil.

please help

code:

-(nserror*)loginwithemail:(nsstring*)email password:(nsstring*)password {     nserror __block *err = null;          // preparing url of login         nsurl *url              =       [nsurl urlwithstring:urlstring];          nsdata *postdata        =       [post datausingencoding:nsasciistringencoding allowlossyconversion:yes];          // preparing request object         nsmutableurlrequest *request = [[nsmutableurlrequest alloc] init];         [request seturl:url];         [request sethttpmethod:@"post"];         [request setvalue:postlength forhttpheaderfield:@"content-length"];         [request sethttpbody:postdata];          nsmutabledictionary __block *parseddata = null; // holds data after parsed          dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);          nsurlsessionconfiguration *config = [nsurlsessionconfiguration defaultsessionconfiguration];         config.tlsminimumsupportedprotocol = ktlsprotocol11;          nsurlsession *session = [nsurlsession sessionwithconfiguration:config delegate:nil delegatequeue:nil];          nsurlsessiondatatask *task = [session datataskwithrequest:request completionhandler:^(nsdata *data, nsurlresponse *response1, nserror *err){                 if(!data)                 {                     err = [nserror errorwithdomain:@"connection timeout" code:200 userinfo:nil];                 }                 else                 {                     nsstring *formatteddata = [[nsstring alloc] initwithdata:data encoding:nsutf8stringencoding];                      nslog(@"%@", formatteddata);                      if([formatteddata rangeofstring:@"<!doctype"].location != nsnotfound || [formatteddata rangeofstring:@"<html"].location != nsnotfound)                     {                         loginsuccessful = no;                         //*errorr = [nserror errorwithdomain:@"server issue" code:201 userinfo:nil];                         err = [nserror errorwithdomain:@"server issue" code:201 userinfo:nil];                     }                     else                     {                         parseddata = [nsjsonserialization jsonobjectwithdata:data options:nsjsonreadingallowfragments error:&err];                         nsmutabledictionary *dict = [parseddata objectforkey:@"user"];                          loginsuccessful = yes;                 }             dispatch_semaphore_signal(semaphore);         }];         [task resume];          // have thread wait until task done          dispatch_semaphore_wait(semaphore, dispatch_time_forever);      return err; } 

i suggest cutting gordian knot: should not use semaphores make asynchronous method behave synchronously. adopt asynchronous patterns, e.g. use completion handler:

- (void)loginwithemail:(nsstring *)email password:(nsstring*)password completionhandler:(void (^ __nonnull)(nsdictionary *userdictionary, nserror *error))completionhandler {     nsstring *post   = ...; // build `post` here, making sure percent-escape userid , password if x-www-form-urlencoded request      nsurl  *url      = [nsurl urlwithstring:urlstring];     nsdata *postdata = [post datausingencoding:nsasciistringencoding allowlossyconversion:yes];      nsmutableurlrequest *request = [nsmutableurlrequest requestwithurl:url];     [request sethttpmethod:@"post"];     // [request setvalue:postlength forhttpheaderfield:@"content-length"];                       // not needed set length ... done     [request setvalue:@"application/x-www-form-urlencoded" forhttpheaderfield:@"content-type"];  // best practice set `content-type`; use whatever `content-type` appropriate request     [request setvalue:@"text/json" forhttpheaderfield:@"accept"];                                // , it's best practice inform server of sort of response you'll accept     [request sethttpbody:postdata];      nsurlsessionconfiguration *config = [nsurlsessionconfiguration defaultsessionconfiguration];     config.tlsminimumsupportedprotocol = ktlsprotocol11;      nsurlsession *session = [nsurlsession sessionwithconfiguration:config delegate:nil delegatequeue:nil];      nsurlsessiondatatask *task = [session datataskwithrequest:request completionhandler:^(nsdata *data, nsurlresponse *response, nserror *err) {         if (!data) {             dispatch_async(dispatch_get_main_queue(), ^{                 completionhandler(nil, [nserror errorwithdomain:@"connection timeout" code:200 userinfo:nil]);             });         } else {             nserror *parseerror;             nsdictionary *parseddata = [nsjsonserialization jsonobjectwithdata:data options:nsjsonreadingallowfragments error:&parseerror];              dispatch_async(dispatch_get_main_queue(), ^{                 if (parseddata) {                     nsdictionary *dict = parseddata[@"user"];                     completionhandler(dict, nil);                 } else {                     completionhandler(nil, [nserror errorwithdomain:@"server issue" code:201 userinfo:nil]);                 }             });         }     }];     [task resume]; } 

and call so:

[self loginwithemail:userid password:password completionhandler:^(nsdictionary *userdictionary, nserror *error) {     if (error) {         // whatever want on error here     } else {         // successful, use `userdictionary` here     } }];  // don't reliant on successful login here; put inside block above 

note:

  1. i know you're going object restoring asynchronous method, it's bad idea make synchronous. first it's horrible ux (the app freeze , user won't know if it's doing or whether it's dead) , if you're on slow network can have sorts of problems (e.g. watchdog process can kill app if @ wrong time).

    so, keep asynchronous. ideally, show uiactivityindicatorview before starting asynchronous login, , turn off in completionhandler. completionhandler initiate next step in process (e.g. performseguewithidentifier).

  2. i don't bother testing html content; easier attempt parse json , see if succeeds or not. you'll capture broader array of errors way.

  3. personally, wouldn't return own error objects. i'd go ahead , return error objects os gave me. way, if caller had differentiate between different error codes (e.g. no connection vs server error), could.

    and if use own error codes, i'd suggest not varying domain. domain should cover whole category of errors (e.g. perhaps 1 custom domain of app's own internal errors), not vary 1 error another. it's not practice use domain field error messages. if want more descriptive in nserror object, put text of error message inside userinfo dictionary.

  4. i might suggest method/variable names conform cocoa naming conventions (e.g. classes start uppercase letter, variables , method names , parameters start lowercase letter).

  5. there's no need set content-length (that's done you), practice set content-type , accept (though not necessary).


Comments

Popular posts from this blog

javascript - Karma not able to start PhantomJS on Windows - Error: spawn UNKNOWN -

Nuget pack csproj using nuspec -

c# - Display ASPX Popup control in RowDeleteing Event (ASPX Gridview) -