This is the third tutorial of Pico tutorial series, in first and second tutorials, I showed you how to use Pico with simple web serivces like StockQuote and CurrencyConverter. These services are quite simple, only support one or two calls, and the request/response structures are fairly simple, supporting these simple services only can’t show the full power of Pico, so in this and later tutorials, I will show you how to use Pico with industrial grade services, let’s start with eBay Finding service, please reivew its official site if you are not familiar with this service, bastially, it lets you search items on eBay. eBay Finding service supports SOAP 1.2, so I will also show you how to configure Pico to support SOAP 1.2 protocol, also I will show how to set service required HTTP headers on Pico client.
Depends on the network speed, you may need to wait a few moments to let the code generator download the wsdl and generate code, you may also download the wsdl and run the code generator with a local wsdl.
A few comments about the codegen options:
I have added a prefix Finding_ as codegen option, such that all interfaces/types generated will be prefixed. Adding prefix to Objective-C types is a recommended best practice to avoid possible type name conflict with code generated from other services, for example, eBay has a couple of SOA services, and they share a few commons types, without prefixing, you may get conflict if you use two or more eBay SOA services in one application.
I have added a special -ebaysoa codegen option, this is because eBay Finding service needs a per-call operation name HTTP header, I added this special flag in the code generator to let it generate the header for me, since I don’t want to add this header everytime I call an eBay Finding service, so this is just a special flag for eBay SOA services and for demo, or a hidden feature, not a generic codegen option.
Step 2 - Create New iOS Project, Add Pico Library and Generated Proxy into Your Project
Create a new simple iOS single view application named “HelloeBayFinding”, don’t choose ARC since Pico does not support ARC yet.
In this tutorial, we will reference Pico as a static library, suppose you have downloaded Pico source project from github site, then:
Drag the Pico xcodeproj into your project,
In the Build Phases of the target, add libPico.a and libxml2.dylib to “Link Binary With Libraries” section.
In the Build Setting of the target, add [your path to Pico source] to “User Header Search Paths” setting, choose “recursive” seach path.
Build the project to ensure that it can build successfully.
Now drag the proxy generated in step 1 into the project, choose “Copy items to destination group’s folder” and “add to targets” when prompted.
Build the project again to ensure that it can build successfully.
You may now review the eBay Finding service SOAP interface generated from wsdl, to learn what kinds of functions are provided by eBay Finding service, and what kinds of parameters are needed to call the serivce, the interface is posted below:
// Generated by wsdl compiler for ios/objective-c// DO NOT CHANGE!#import <Foundation/Foundation.h>#import "PicoSOAPClient.h"#import "Finding_GetVersionResponse.h"#import "Finding_FindItemsAdvancedRequest.h"#import "Finding_FindItemsAdvancedResponse.h"#import "Finding_FindItemsForFavoriteSearchResponse.h"#import "Finding_FindCompletedItemsRequest.h"#import "Finding_GetSearchKeywordsRecommendationResponse.h"#import "Finding_FindCompletedItemsResponse.h"#import "Finding_FindItemsByImageResponse.h"#import "Finding_FindItemsByProductRequest.h"#import "Finding_FindItemsByImageRequest.h"#import "Finding_FindItemsByKeywordsRequest.h"#import "Finding_FindItemsByProductResponse.h"#import "Finding_FindItemsIneBayStoresRequest.h"#import "Finding_FindItemsByCategoryRequest.h"#import "Finding_GetHistogramsRequest.h"#import "Finding_GetSearchKeywordsRecommendationRequest.h"#import "Finding_FindItemsIneBayStoresResponse.h"#import "Finding_GetVersionRequest.h"#import "Finding_FindItemsByCategoryResponse.h"#import "Finding_FindItemsByKeywordsResponse.h"#import "Finding_FindItemsForFavoriteSearchRequest.h"#import "Finding_GetHistogramsResponse.h"/** This class is the SOAP client to the FindingServicePortType Web Service.*/@interfaceFindingServicePortType_SOAPClient : PicoSOAPClient{}/** public method*/-(void)getSearchKeywordsRecommendation:(Finding_GetSearchKeywordsRecommendationRequest*)requestObjectsuccess:(void(^)(Finding_GetSearchKeywordsRecommendationResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsByKeywords:(Finding_FindItemsByKeywordsRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsByKeywordsResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsByCategory:(Finding_FindItemsByCategoryRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsByCategoryResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsAdvanced:(Finding_FindItemsAdvancedRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsAdvancedResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsByProduct:(Finding_FindItemsByProductRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsByProductResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsIneBayStores:(Finding_FindItemsIneBayStoresRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsIneBayStoresResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsByImage:(Finding_FindItemsByImageRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsByImageResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)getHistograms:(Finding_GetHistogramsRequest*)requestObjectsuccess:(void(^)(Finding_GetHistogramsResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findCompletedItems:(Finding_FindCompletedItemsRequest*)requestObjectsuccess:(void(^)(Finding_FindCompletedItemsResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)getVersion:(Finding_GetVersionRequest*)requestObjectsuccess:(void(^)(Finding_GetVersionResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;/** public method*/-(void)findItemsForFavoriteSearch:(Finding_FindItemsForFavoriteSearchRequest*)requestObjectsuccess:(void(^)(Finding_FindItemsForFavoriteSearchResponse*responseObject))successfailure:(void(^)(NSError*error,id<PicoBindable>soapFault))failure;@end
All the methods in the interface follow same calling paradigm - you call the service with required request object and register success callback(for success handling logic) and failure callback(for error handling logic) using Objective-C block.
Step 3 - Implement Appliction Logic and UI, Call Proxy to Invoke Web Service as Needed.
The shared client for eBay Finding service is a little more complex than the simple serivce client in tutorial 1 and 2, this is because that eBay Finding service needs a few HTTP headers set to work, let me give more comments:
eBay Finding service support SOAP12, so we set the client to use SOAP12 protocol, this is not necessary since eBay Finding service also supports SOAP11(which is Pico default), we use SOAP12 here just for demo and to show the capability of Pico.
eBay Finding service needs to set a few HTTP headers to work, for a list of required headers, please refer to doc here.
One mandatory header for eBay Finding service is eBayAppId, you need to register on eBay developer site as an eBay developer then get this id, before your can run this demo, you must fill in your own eBayAppId in the shared client.
Now the UI part, for this hello world like sample, we just need a UITextField for keyword input and UIButton to trigger eBay search by invoking method searchButtonPressed which will indirectly call eBay Finding service through the proxy, fairly simple, see definition in header file ViewController.h and instantiation in implementation file ViewController.m.
Now implement the searchButtonPressed method by invoking service as below:
#import "ViewController.h"#import "EBayFindingServiceClient.h"#import "Finding_CommonTypes.h"#import "SOAP12Fault.h"#import "SOAP12Faultreason.h"#import "SOAP12Detail.h"#import "SOAP12Reasontext.h"#import "Toast+UIView.h"-(void)searchButtonPressed:(id)sender{// Hide the keyboard.[_searchTextresignFirstResponder];if(_searchText.text.length>0){// start progress activity[self.viewmakeToastActivity];// Get shared service clientEBayFindingServiceClient*findingClient=[EBayFindingServiceClientsharedClient];findingClient.debug=YES;// enable request/response message logging// Build request objectFinding_FindItemsByKeywordsRequest*request=[[[Finding_FindItemsByKeywordsRequestalloc]init]autorelease];request.keywords=_searchText.text;// only need one item for demoFinding_PaginationInput*pagination=[[Finding_PaginationInputalloc]init];pagination.pageNumber=[NSNumbernumberWithInt:1];pagination.entriesPerPage=[NSNumbernumberWithInt:1];request.paginationInput=pagination;[paginationrelease];// make API call and register callbacks[findingClientfindItemsByKeywords:requestsuccess:^(Finding_FindItemsByKeywordsResponse*responseObject){// stop progress activity[self.viewhideToastActivity];if([Finding_AckValue_SUCCESSisEqualToString:responseObject.ack]){if(responseObject.searchResult.count>0){// show the title of the first found itemFinding_SearchItem*item=[responseObject.searchResult.itemobjectAtIndex:0];// start image downloading progress activity[self.viewmakeToastActivity];// get gallery imageNSURL*imageURL=[NSURLURLWithString:item.galleryURL];NSData*imageData=[NSDatadataWithContentsOfURL:imageURL];// stop progress activity[self.viewhideToastActivity];UIImage*image=[UIImageimageWithData:imageData];[self.viewmakeToast:item.titleduration:3.0position:@"center"title:@"Success"image:image];}else{// no result[self.viewmakeToast:@"No result"duration:3.0position:@"center"];}}else{// response residenet errorFinding_ErrorData*errorData=[responseObject.errorMessage.errorobjectAtIndex:0];[self.viewmakeToast:errorData.messageduration:3.0position:@"center"title:@"Error"];}}failure:^(NSError*error,id<PicoBindable>soapFault){// stop progress activity[self.viewhideToastActivity];if(error){[self.viewmakeToast:[errorlocalizedDescription]duration:3.0position:@"center"title:@"Error"];}elseif(soapFault){SOAP12Fault*soap12Fault=(SOAP12Fault*)soapFault;SOAP12Reasontext*reasonText=[soap12Fault.reason.textobjectAtIndex:0];[self.viewmakeToast:reasonText.valueduration:3.0position:@"center"title:@"SOAP Fault"];}}];}}
More comments to the serivce call code:
I’ve added comments in the code so the whole service call flow should be easy to understand.
We used the eBay Finding FindItemsByKeywords call, which takes a keyword as input, and will return a list of matched items, for demo, we just need one item to display, so we set entries per page to 1.
In the success handling logic, we show the title and the image of the returned item, for demo, the image is downloaded synchronously, but in practice, you should download image asynchronously in order not to block main UI.
Since we set the client to use SOAP12 protocol, in the error handling logic, we need to cast the soapFault variable to type SOAP12Fault and handle it accordingly.
eBay Finding service support response resident error(RRE), so even we get a success response, we still need to check the response for resident error and handle it accordingly.
We used a thrid party library called “Toast” for producing toast like message, this is just for the convenience of demo, not necessary in your real project.
Regarding type hint:
The pageNumber property of Finding_PaginationInput is of type NSNumber, you may get confused what kind of number should be put in PageNumber, short, int or long? Please just consult the source of Finding_PaginationInput.h, it provides type hint as code comments, see sample below. Indeed, every type generated from wsdl has sufficient type hint to assist your development. Xsd annotations in wsdl/schema are also generated into corresponding interfaces/types, further facilitating your development
/**Specifies which subset of data (or "page") to return in the callresponse. The number of data pages is determined by the total number ofitems matching the request search criteria (returned inpaginationOutput.totalEntries) divided by the number of entries todisplay in each response (entriesPerPage). You can return up to the first100 pages of the result set by issuing multiple requests and specifying,in sequence, the pages to return.type : NSNumber, wrapper for primitive int*/@property(nonatomic,retain)NSNumber*pageNumber;
In response handling, the item property of Finding_SearchResult is of type NSMutableArray, you may get confused what is the actual entry type? please just consult the source of Finding_SearchResult.h for type hint, see sample below:
/** Container for the data of a single item that matches the search criteria. entry type : class Finding_SearchItem*/@property(nonatomic,retain)NSMutableArray*item;
Xsd enumeration is mapped to Objective-C NSString in Pico framework, for example, the ack property of Finding_FindItemsByKeywordsResponse is of type NSString, in wsdl, it’s an xsd enumeration of type AckValue, the type hint in Finding_BaseServiceResponse.h(from which Finding_FindItemsByKeywordsResponse.h extends) will tell you where to find the enum constants allowed by ack property, in this case, the allowed enum constants are in Finding_AckValue.h, see sample below :
/** Indicates whether or not errors or warnings were generated during the processing of the request. type: string constant in Finding_AckValue.h*/@property(nonatomic,retain)NSString*ack;
At last, please don’t forget to include the shared client and SOAP12 related header files, it’s a best practice to include the generated Finding_CommonTypes.h file which can free you from writing many import statements required by request building and response handling.
Final Step - Run the Demo
This time let’s run the demo on a real iPod Touch 4 device with iOS 4.3.3, see a screen shot below:
let’s also try a soap fault case, for example, if you forget to fill in your eBayAppId in the shared client, then you will get:
This is just a bare minimum eBay Finding service based application, for a demo with more functions, please see the eBayDemoApp sample in the Examples folder of Pico source, eBayDemoApp is a composite app which calls two eBay serivces behind, this app searches eBay by calling eBay Fining service, showes a list of matched items on UI, when an item is clicked, it will show item details by calling eBay Shopping service, see a screen shot below.
Now it’s your turn to create iOS applications based on eBay Finding web service, see your next great service based app.
Update 1
The eBay Finding Service Proxy has been extracted as a standalone project, hosted here, and the corresponding appledoc is hosted here, the appledoc is a useful programming reference. By the way, the doc annotations in wsdl are not only generted into the proxy code, but into the appledoc, assisting your development.