4

I've done lots of reading and researching on webservice and consuming them from a iOS app side. But many website are very brief and didn't go indepth about consuming and parsing the data. Currently I'm reading a tutorial by raywenderlich http://www.raywenderlich.com/2965/how-to-write-an-ios-app-that-uses-a-web-service and read a SO thread http://stackoverflow.com/questions/7364326/recommendations-for-net-web-service-format-protocol-for-android-iphone-etc-i but I am still unclear as to how I should approach it.

From what I know so far, the few methods to consume webservice are REST and SOAP protocol. (I have my app in xcode and webservice done in VB (.asmx file))

I'm the kind of guy who learn more and faster through hands on by tutorial rather than reading article and such. Is there any tutorial that work on consuming .asmx(webservice built with VS) rather than php. Personally I would prefer REST protocol which parse JSON since many people say it's easier to use and that Apple already have a lib/framework for it.

Will be great if anyone could point me in the right direction as I feel that learning how to let an iOS app consume web service is very important in future app development.

EDIT: After reading through the php webservice, there are a few things which I do not have in mine.

function redeem from http://www.raywenderlich.com/2941/how-to-write-a-simple-phpmysql-web-service-for-an-ios-app

// Put parameters into local variables
        $rw_app_id = $_POST["rw_app_id"];
        $code = $_POST["code"];
        $device_id = $_POST["device_id"]

If I'm using asmx file, do I also have to declare something like this in every of my web method?

E.G of one of my webMethod (basic CRUD)

[WebMethod]

        public string insertUser(string User, string Password, string Gender)
        {

               try
                {

                    string connectionString = ConfigurationManager.ConnectionStrings["mysql"].ToString();
                    MySqlCommand dCmd = new MySqlCommand();
                    using (MySqlConnection mysqlCon = new MySqlConnection(connectionString))
                    {

                        if (do_check_record(User) == false)
                            {

                                mysqlCon.Open();
                                dCmd.CommandText = "INSERT into tbl_login (username,password,gender) values (?username,?password,?gender)";
                                dCmd.CommandType = CommandType.Text;
                                dCmd.Parameters.Add(new MySqlParameter("?username", User));
                                dCmd.Parameters.Add(new MySqlParameter("?password", Password));
                                dCmd.Parameters.Add(new MySqlParameter("?gender", Gender));
                                dCmd.Connection = mysqlCon;
                                dCmd.ExecuteNonQuery();
                                mysqlCon.Close();

                            }
                            else

                            {
                                return string.Format( "User exists in data_base");
                            }

                    }

                    return string.Format("data_base insert");

                }
                catch (Exception ex)
                {
                    return string.Format(ex.Message);
                }

}

Under consuming webservice from app side

the example given in the tutorial was

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    NSLog(@"Want to redeem: %@", textField.text);

    // Get device unique ID
    UIDevice *device = [UIDevice currentDevice];
    NSString *uniqueIdentifier = [device uniqueIdentifier];

    // Start request
    NSString *code = textField.text;
    NSURL *url = [NSURL URLWithString:@"http://www.wildfables.com/promos/"];
    ASIFormDataRequest *request = [ASIFormDataRequest requestWithURL:url];
    [request setPostValue:@"1" forKey:@"rw_app_id"];
    [request setPostValue:code forKey:@"code"];
    [request setPostValue:uniqueIdentifier forKey:@"device_id"];
    [request setDelegate:self];
    [request startAsynchronous];

    // Hide keyword
    [textField resignFirstResponder];

    // Clear text field
    textView.text = @"";    

    return TRUE;
}

But shouldn't it be a IBAction button to call instead of a BOOL?

- (void)requestFinished:(ASIHTTPRequest *)request
{    

    if (request.responseStatusCode == 400) {
        textView.text = @"Invalid code";        
    } else if (request.responseStatusCode == 403) {
        textView.text = @"Code already used";
    } else if (request.responseStatusCode == 200) {
        NSString *responseString = [request responseString];
        NSDictionary *responseDict = [responseString JSONValue];
        NSString *unlockCode = [responseDict objectForKey:@"unlock_code"];

        if ([unlockCode compare:@"com.razeware.test.unlock.cake"] == NSOrderedSame) {
            textView.text = @"The cake is a lie!";
        } else {        
            textView.text = [NSString stringWithFormat:@"Received unexpected unlock code: %@", unlockCode];
        }

    } else {
        textView.text = @"Unexpected error";
    }

}

- (void)requestFailed:(ASIHTTPRequest *)request
{    
    NSError *error = [request error];
    textView.text = error.localizedDescription;
}

Correct me if I'm wrong, but this method - (void)requestFinished:(ASIHTTPRequest *)request used in the tutorial is the one that is parsing JSON, right? If so, for every webmethod that I call, I have to make a new request and a new - (void)requestFinished:(ASIHTTPRequest *)request

EDIT: any help would really be appreciated... I'm a beginner at this and hope that someone with experience could guide me through.

Yang Jie Domodomo
  • 685
  • 1
  • 8
  • 25

2 Answers2

2

do I also have to declare something like this in every of my web method?

The code you have posted above is extracting data from the POST. The asmx file should already do this for you. The parameters to you pass in the POST will be turned into the parameters passed to MethodName (see: Accessing XML Web Services).


Update

So if I understand this correctly, your web service endpoint is http://xxx.yyy/zzz.asmx/insertUser. You would then sent a post body using the variable username, password, and gender:

[request setPostValue:username forKey:@"User"];
[request setPostValue:password forKey:@"Password"];
[request setPostValue:gender forKey:@"Gender"];

But shouldn't it be a IBAction button to call instead of a BOOL?

No. -textFieldShouldReturn: is a delegate method for UITextField. It is not an IBAction.

When a user touches a text field, the keyboard becomes active. When the return button is pressed on the keyboard, the text field's delegate is sent the -textFieldShouldReturn: message.

The upshot is that this method is called when return or done is pressed on the keyboard.


Added more information for part that I missed

Correct me if I'm wrong, but this method -requestFinished: used in the tutorial is the one that is parsing JSON, right? If so, for every webmethod that I call, I have to make a new request and a new -requestFinished:

No, there will only be one -requestFinished: per class. You can either break-up your client into several classes: one per web service URL, or you need to distinguish the different URLs in the one -requestFinished:.

- (void)requestFinished:(ASIHTTPRequest *)request
{
    if ([request.originalURL.absoluteString isEqualToString:@"http://xxx.yyy/zzz.asmx/methodName"]) {
        // handle methodName endpoint.
    }
}

You can also use -setCompletionBlock: and -setFailedBlock: instead of -setDelegate:, if you don't like how the delegate works. Better yet, pick something like AFNetworking like @Stew suggested.

Jeffery Thomas
  • 42,202
  • 8
  • 92
  • 117
  • Edited the question. the code is extracted from he's webMethod redeemFunction, where he first built it. I've never tried PHP before, so the way they structured it and their syntax are foreign to me. I've also put a sample of my webMethod up there. – Yang Jie Domodomo Jul 16 '12 at 03:46
  • @YangJieDomodomo I added a bit more about your inserUser endpoint. – Jeffery Thomas Jul 16 '12 at 04:09
  • Sorry about the confusion, what I meant was on the web service side. the `[request setPostValue:username forKey:@"User"];` will be done in the app side isn't it? the php section ` $rw_app_id = $_POST["rw_app_id"];` is it equivalent to my `dCmd.Parameters.Add(new MySqlParameter("?username", User));` ? – Yang Jie Domodomo Jul 16 '12 at 04:14
  • @YangJieDomodomo No. `(string User, string Password, string Gender)` on the end of `public string insertUser(string User, string Password, string Gender)` is the equivalent of the php section. The asmx method automatically extracts the POST values from the request, you don't have to do it manually. – Jeffery Thomas Jul 16 '12 at 04:21
  • @YangJieDomodomo As an added note: `dCmd.Parameters.Add(new MySqlParameter("?username", User))` is database command. This is the web service communicating with a back end database. – Jeffery Thomas Jul 16 '12 at 04:22
  • I see. Thanks for clearing my misconception. Hard to get reference from it especially when I don't understand php very well. So I can just deploy my webservice(with the current method without changing), i just have to use `ASIHTTPRequest`/`ASIFormDataRequest` to consume webservice and finish up with `-requestFinished` . Am I right to say it this way? That my webmethod is fine and don't require any changes – Yang Jie Domodomo Jul 16 '12 at 04:30
  • @YangJieDomodomo I don't know the answer to that. `ASIFormDataRequest` will send the data as a POST using simple web content types like `application/x-www-form-urlencoded`. I'm not sure your web service is designed to handle receiving that format. You need to make sure the data you send is something your web service can receive. – Jeffery Thomas Jul 16 '12 at 05:16
  • I see, thanks so much. I will go read up more on `ASIFormDataRequest` and `ASIHTTPRequest` to find out the diff and type of usage. – Yang Jie Domodomo Jul 16 '12 at 05:26
1

I would suggest using the AFNetworking library (https://github.com/AFNetworking/AFNetworking) and using the built-in AFJSONRequestOperation: https://github.com/AFNetworking/AFNetworking/blob/master/AFNetworking/AFJSONRequestOperation.h

Rather than implementing a 'request finished' callback, it calls the success block when the request is completed, and passes in the JSON object as an argument. You can parse that object into whatever you want in the success block.

There is an example of how to use it in this answer https://stackoverflow.com/a/8920596/223558

Community
  • 1
  • 1
Stew
  • 1,901
  • 10
  • 9