Genivia Home Documentation
The iOS Plugin

updated Wed Aug 17 2016
 
The iOS Plugin

Table of Contents

The iOS Plugin

By Bethany Sanders, Robert van Engelen, Ning Xie, and Wei Zhang

Overview

Consuming Web services on iOS (iPhone and iPad) platforms is increasingly in demand today. Xcode does not have any built-in tools that make consuming XML Web services easy. It typically requires knowledge of XML processing techniques to send soap requests to the Web services and then parse the returning XML results. The gSOAP toolkit provides an automated XML data binding toolkit for C and C++ can be used to develop XML Web service client applications to consuming Web services on iOS platforms such as iPhone and iPad. Moreover, the plugin takes advantage of network connection offered by iOS SDK and supports 3G/4G/LTE, wifi and so on.

To use the iOS plugin for development of client applications on iOS platforms, register the plugin with the gSOAP engine context as follows:

1 #import "gsoapios.h"
2 
3 struct soap *soap = soap_new(); // new engine context
4 soap_register_plugin(soap, soap_ios); // register the iOS plugin
5 
6 ...
7 
8 soap_destroy(soap); // clean up deserialized data
9 soap_end(soap); // clean up temporaries
10 soap_free(soap); // free the context

Or when using a C++ proxy class generate with soapcpp2 option -j:

1 #import "gsoapios.h"
2 
3 Proxy proxy;
4 soap_register_plugin(proxy.soap, soap_ios);
5 
6 ...
7 
8 proxy.destroy();

There are no other plugin API calls necessary to make an XML Web service client application work with iOS.

Getting Started

To start building Web services client applications for iPhone and/or iPad with gSOAP, you will need:

Developing Web services client applications on iOS is no different than developing these applications on another OS when you use this iOS Plugin.

The steps to create a client application on iOS:

All of the source code files need to be of type Objective C++ source. Mixing some C files with some C++ files will cause errors.

Specifying the Cache Policy

The interaction between the client and the Web service server can be controlled by specifying the cache policy. To specify the cache policy, use the API function soap_ios_setcachepolicy(struct soap *soap, unsigned int policy). This API function cannot be called before the plugin is registered.

1 #import "gsoapios.h"
2 
3 struct soap *soap = soap_new();
4 soap_register_plugin(soap, soap_ios);
5 
6 // Specify the cache policy for the request
7 soap_ios_setchacepolicy(soap, NSURLRequestReturnCacheDataElseLoad);
8 
9 ...
10 
11 soap_destroy(soap); // clean up deserialized data
12 soap_end(soap); // clean up temporaries
13 soap_free(soap); // free the context

The available cache policies that can be specified are:

1 enum {
2  NSURLRequestUseProtocolCachePolicy = 0,
3  NSURLRequestReloadIgnoringLocalCacheData = 1,
4  NSURLRequestReloadIgnoringCacheData = NSURLRequestReloadIgnoringLocalCacheData,
5  NSURLRequestReturnCacheDataElseLoad = 2,
6  NSURLRequestReturnCacheDataDontLoad = 3,
7  NSURLRequestReloadIgnoringLocalAndRemoteCacheData =4,
8  NSURLRequestReloadRevalidatingCacheData = 5
9 }

The default cache policy is NSURLRequestUseProtocolCachePolicy.

Specifying a Timeout Interval

The timeout of a network connection can be specified using the API function soap_ios_settimeoutinterval(struct soap *soap, double seconds):

1 #import "gsoapios.h"
2 
3 struct soap *soap = soap_new();
4 soap_register_plugin(soap, soap_ios);
5 
6 // Specify the timout interval as 30 seconds
7 soap_ios_settimeoutinterval(soap, 30.0);
8 
9 ...
10 
11 soap_destroy(soap); // clean up deserialized data
12 soap_end(soap); // clean up temporaries
13 soap_free(soap); // free the context

The default timeout is 60 seconds.

HTTP Authentication

To support authentication when access is denied (HTTP 401 error) as the client tries to connect. enable HTTP authentication as follows.

Basic authentication is simply enabled at the client-side by setting the soap.userid and soap.passwd strings to a username and password, respectively:

1 struct soap *soap = soap_new();
2 soap->userid = "someone";
3 soap->passwd = "somepass";

Examples

This section introduces four examples to demonstrate the development of client applications consuming Web services on iOS platforms such as iPhone and iPad using the gSOAP tools and the iOS plugin.

The first example Simple Calculator Example (C++) is a basic calculator example client. The second example GeoIPService Example (C++) is a web service that locates the country of a certain IP Adress. The third example Weather Example (C++) returns weather results for well known US cities, and the fourth example Air Example (C++) shows information on every airport within a given country.

We assume you already have had the experience in development of applications for iPhone and iPad using Xcode with iOS SDK installed.

Directions:

Simple Calculator Example (C++)

This example shows you how to develop a client application in C++ using gSOAP and the ios plugin, which consumes a simple calculator service on iOS using gSOAP. The simple calculator service was developed and deployed as a demo to get started with gSOAP. The gSOAP Calculator Service provides several operations such as add, sub, mul, div etc. In this example, we use the operation add as a demo. Other operations are applied in a similar way. The wsdl file for this service can be obtained at the following link:

http://www.genivia.com/calc.wsdl

The Xcode project for this example can be found in samples/ios/calc.

Step 1: Generating stubs for C++ API

To generate codes for the calculator Web service, we first run the wsdl2h tool from the command line on the URL of the WSDL and use option -o to specify the output file (Alternatively, you can download the calc.wsdl and use the local file instead of the URL):

wsdl2h -o calc.h http://www.genivia.com/calc.wsdl

This generates the calc.h service definition header file with service operation definitions and types for the operation's data. By default, gSOAP assumes you will use C++ with STL.

We have not yet generated the stubs for the API. To do so, run the soapcpp2 compiler:

soapcpp2 -CL -I$GSOAP_HOME/import calc.h

Option -CL indicates client-side only files (soapcpp2 generates both client and server stubs and skeletons by default). This generates a number of source files for client application development. We do not have to generate a proxy class in this example because it does not use a Proxy class.

Step 2: Creating Xcode project for iPhone

Launch Xcode, create a Single View-based Application project and name it Calc. Open the ViewController.xib (main storyboard) file in the Interface Builder. Double-click on the View item and populate it with the views listed below and shown in Figure 1:

calc-view.png
Figure 1. View of the Calculator Web Service Client App

In Xcode, edit the file ViewController.h to make it look like the following:

1 // File: ViewController.h
2 
3 #import <UIKit/UIKit.h>
4 
5 @interface ViewController : UIViewController {
6  UITextField *op1; // operand1
7  UITextField *op2; // operand2
8 }
9 @property (nonatomic, retain) IBOutlet UITextField *op1; // this code is generated when you link the text fields
10 @property (nonatomic, retain) IBOutlet UITextField *op2; // this code is generated when you link the text fields
11 
12 - (IBAction) buttonPressed;
13 @end

Link the op1 and op2 to the two Text Fields and delegate the button action to method buttonPressed.

In Xcode, edit the file info.plist to make it look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleExecutable</key>
    <string>$(EXECUTABLE_NAME)</string>
    <key>CFBundleIdentifier</key>
    <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>$(PRODUCT_NAME)</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0</string>
    <key>CFBundleSignature</key>
    <string>????</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>LSRequiresIPhoneOS</key>
    <true/>
    <key>UILaunchStoryboardName</key>
    <string>LaunchScreen</string>
    <key>UIMainStoryboardFile</key>
    <string>Main</string>
    <key>UIRequiredDeviceCapabilities</key>
    <array>
      <string>armv7</string>
    </array>
    <key>UISupportedInterfaceOrientations</key>
    <array>
      <string>UIInterfaceOrientationPortrait</string>
      <string>UIInterfaceOrientationLandscapeLeft</string>
      <string>UIInterfaceOrientationLandscapeRight</string>
    </array>
  </dict>
</plist>

These changes ensure that you can access the web services despite the added layer of protection (App Transport Security).

Step 3: Adding generated source stubs to the Xcode project

Add the source files soapC.cpp, soapClient.cpp, soapH.h, and soapStub.h generated in Step 1 of this tutorial to the project. Also add files stdsoap2.h and stdsoap2.cpp to the project from gSOAP package as well as the iOS plugin files gsoapios.h and sgsoapios.mm.

Once all of your files have been added to the project, ensure they are all of type "Objective C++ Source". This ensures that there will be no issues with mixing Objective C and C++ code.

Step 4: Implementing the Logic by calling the soap service

Firstly, edit file main.mm to import the file calc.nsmap. Link errors may arise without importing this namespace mapping.

1 // File: main.mm
2 
3 #import <UIKit/UIKit.h>
4 #import "../../calc.nsmap"
5 
6 int main(int argc, char * argv[]) {
7  @autoreleasepool {
8  return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
9  }
10 }

Then, implement the source file ViewController.mm as the following:

1 #import "ViewController.h"
2 #import "soapStub.h"
3 #import "gsoapios.h"
4 
5 @implementation ViewController
6 
7 @synthesize op1; // Set or get operand1
8 @synthesize op2; // Set or get operand2
9 
10 - (IBAction) buttonPressed {
11  double x = [op1.text doubleValue];
12  double y = [op2.text doubleValue];
13 
14  struct soap *soap = soap_new1(SOAP_IO_DEFAULT|SOAP_IO_KEEPALIVE|SOAP_XML_INDENT|SOAP_XML_STRICT);
15 
16  if (soap_register_plugin(soap, soap_ios) == SOAP_OK)
17  {
18  // Specify the timeout interval (optional) to 45 seconds instead of
19  // the default 60 seconds
20  soap_ios_settimeoutinterval(soap, 45.0);
21 
22  double result;
23 
24  // Call Web service operation add
25  int status = soap_call_ns2__add(soap, NULL, NULL,x, y, result);
26 
27  soap_free_temp(soap); // Cleanup temporary resources
28 
29  // Check soap response status
30  if (status == SOAP_OK)
31  {
32  NSString *resultString;
33  NSString *titleString;
34 
35  resultString = [NSString stringWithFormat:@"%f",result];
36  titleString = [NSString stringWithFormat:@"%f + %f =",x, y];
37 
38  // Show the result in an alert
39  UIAlertController * alert = [UIAlertController
40  alertControllerWithTitle:titleString
41  message:resultString
42  preferredStyle:UIAlertControllerStyleAlert];
43  UIAlertAction* cancelButton = [UIAlertAction
44  actionWithTitle:@"OK"
45  style:UIAlertActionStyleDefault
46  handler:^(UIAlertAction * action){}];
47 
48  [alert addAction: cancelButton];
49  [self presentViewController:alert animated:YES completion:nil];
50  }
51  else
52  soap_print_fault(soap,stderr); // Print soap error in console
53  }
54  soap_destroy(soap);
55  soap_end(soap);
56  soap_free(soap);
57 }
58 @end

A screen snapshot of the client is shown in Figure 2.

calc-result.png
Figure 2: Snapshot of the CalcViewService result

GeoIPService Example (C++)

GeoIPService is a live SOAP Web service that enables you to look up countries by IP address or by Context.

This example shows you how to develop a client application in C++ using gSOAP and the ios plugin, which consumes the GeoIPSerive on iOS using gSOAP. The WSDL file for this service can be downloaded at the following link:

http://www.webservicex.net/geoipservice.asmx?WSDL

It is crucial to follow these directions in order for your app to work:

The Xcode project for this example can be found in gsoap/ios_plugin/examples/GeoIPService.

Step 1: Generating stubs for C++ Proxy

To generate codes for the GeoIPService Web service, we first run the wsdl2h tool from the command line on the URL of the WSDL and use option -o to specify the output file (Alternatively, you can download the GeoIPService.wsdl file and use the local file instead of the URL):

wsdl2h -o GeoIPService.h 'http://www.webservicex.net/geoipservice.asmx?WSDL'

This generates the GeoIPService.h service definition header file with service operation definitions and types for the operation's data. By default, gSOAP assumes you will use C++ with STL.

To generate the stubs for the C++ proxy classes, run the soapcpp2:

soapcpp2 -j -CL -I$GSOAP_HOME/import GeoIPService.h

Option -j tells the compiler to generate the C++ proxy class and option -CL indicates client-side only files (soapcpp2 generates both client and server stubs and skeletons by default). Option -I is needed to import the stlvector.h file from the import directory in the gSOAP package to support serialization of STL vectors.

Step 2: Creating an Xcode project for iPhone

Create a Single View-based Application project and name it GeoIPService.Open the ViewController.xib (main storyboard) file in the Interface Builder. Double-click on the View item and populate it with the views listed below and shown in Figure 3:

geoip-view.png
Figure 3. View of the GeoIPService Client

In Xcode, edit the file ViewController.h to make it look like the following:

1 // File: ViewController.h
2 
3 #import <UIKit/UIKit.h>
4 
5 @interface ViewController : UIViewController {
6  UITextField* IPAddress;
7 }
8 
9 @property (strong, nonatomic) IBOutlet UITextField *IPAddress; // this code generated when you link the text
10 //field to IPAddress
11 
12 - (IBAction) buttonPressed;
13 @end

Set the ipAddress outlet and the buttonFindCountry:(id)sender method to delegate action of the button.

In Xcode, edit the file info.plist to make it look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>APPL</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>LSRequiresIPhoneOS</key>
      <true/>
      <key>UILaunchStoryboardName</key>
      <string>LaunchScreen</string>
      <key>UIMainStoryboardFile</key>
      <string>Main</string>
      <key>UIRequiredDeviceCapabilities</key>
      <array>
        <string>armv7</string>
      </array>
      <key>UISupportedInterfaceOrientations</key>
      <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
      </array>
  </dict>
</plist>

These changes ensure that you can access the web services despite the added layer of protection of App Transport Security).

Step 3: Adding generated source stubs to the Xcode project

Add the source files soapC.cpp, GeoIPServiceSoap12Proxy.cpp, GeoIPServiceSoap12Proxy.h, soapH.h, and soapStub.h generated in Step 1 of this tutorial to the project. Also add files stdsoap2.h and stdsoap2.cpp to the project from the gSOAP package and the iOS plugin files gsoapios.h and gsoapios.mm.

Once all of your files have been added to the project, ensure they are all of type "Objective C++ Source". This ensures that there will be no issues with mixing Objective C and C++ code.

Step 4: Implementing the Logic by calling the soap service

Firstly, edit file main.mm to import the file GeoIPService.nsmap. Linking errors would arise without importing this namespace mapping.

1 // File: main.mm
2 
3 #import <UIKit/UIKit.h>
4 #import "AppDelegate.h"
5 #include "../../GeoIPServiceSoap.nsmap"
6 
7 int main(int argc, char * argv[]) {
8  @autoreleasepool {
9  return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
10  }
11 }

Then, implement the source file ViewController.mm as the following:

1 #import "ViewController.h"
2 #include "soapGeoIPServiceSoapProxy.h"
3 #import "soapStub.h"
4 #import "gsoapios.h"
5 using namespace std;
6 
7 typedef struct _ns1__GetGeoIP RequestStruct;
8 typedef struct _ns1__GetGeoIPResponse ResponseStruct;
9 
10 @implementation ViewController
11 
12 @synthesize IPAddress;
13 
14 - (IBAction)buttonPressed:(id)sender {
15  RequestStruct ip;
16  ResponseStruct response;
17 
18  //creates proxy
19  GeoIPServiceSoapProxy service(SOAP_IO_DEFAULT|SOAP_IO_KEEPALIVE|SOAP_XML_INDENT|SOAP_XML_STRICT);
20  soap_init(service.soap);
21 
22  //sets IPAddress from input
23  std::string ipAdd = std::string((char *)[IPAddress.text UTF8String]);
24  ip.IPAddress = &ipAdd;
25 
26  // ----- register plugin for callbacks ------
27  soap_register_plugin(service.soap, soap_ios);
28 
29  // Optional: timeout internal, the default is 60.0 seconds
30  soap_ios_settimeoutinterval(service.soap, 30.0);
31 
32  //call web service
33  int status = service.GetGeoIP(&ip, response);
34 
35  string* result;
36  string err_msg = "Invalid IP address";
37 
38  if (status == SOAP_OK)
39  {
40  NSString *soapResult;
41  NSString *titleMsg;
42 
43  if (response.GetGeoIPResult && response.GetGeoIPResult->CountryName)
44  result = response.GetGeoIPResult->CountryName;
45  else
46  result = &err_msg;
47 
48  soapResult = [NSString stringWithUTF8String:result->c_str()];
49  titleMsg = [NSString stringWithFormat: @"%@", @"Country Found"];
50 
51  //show result as an alert
52  UIAlertController * alert = [UIAlertController
53  alertControllerWithTitle:titleMsg
54  message:soapResult
55  preferredStyle:UIAlertControllerStyleAlert];
56  UIAlertAction* cancelButton = [UIAlertAction
57  actionWithTitle:@"OK"
58  style:UIAlertActionStyleDefault
59  handler:^(UIAlertAction * action){}];
60 
61  [alert addAction: cancelButton];
62  [self presentViewController:alert animated:YES completion:nil];
63 
64  }
65  else
66  service.soap_stream_fault(std::cerr);
67  service.destroy();
68 }
69 
70 // Override to allow orientations other than the default portrait orientation.
71 - (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
72  // Return YES for supported orientations
73  return (interfaceOrientation == UIInterfaceOrientationPortrait ||
74  interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown ||
75  interfaceOrientation == UIInterfaceOrientationLandscapeLeft ||
76  interfaceOrientation == UIInterfaceOrientationLandscapeRight);
77 }
78 
79 - (void)didReceiveMemoryWarning {
80  // Releases the view if it doesn't have a superview.
81  [super didReceiveMemoryWarning];
82 
83  // Release any cached data, images, etc that aren't in use.
84 }
85 
86 - (void)viewDidUnload {
87  // Release any retained subviews of the main view.
88  // e.g. self.myOutlet = nil;
89 }
90 
91 - (void)dealloc {
92  // [ipAddress release];
93  // [super dealloc];
94 }
95 @end

A screen snapshot of the client is shown in Figure 4.

geoip-result.png
Figure 4: Snapshot of the GeoIPServiceViewService result

Weather Example (C++)

GlobalWeather is a live SOAP Web service that enables you to look up the Weather in popular cities around the world. For simplicity, this example is limited to cities in the United States. For some reason, some very popular cities are not supported by this web service. For example, no results for New York City will be returned. There is an error message within the app that shows whenever one of these cities is entered. Know that the error message is not an issue with the app you just built, but something that the web service itself does not provide.

This example shows how to develop a client application in C++ using gSOAP and the ios plugin, which consumes the Weather service on iOS using gSOAP. The WSDL file for this service can be downloaded at the following link:

http://www.webservicex.net/globalweather.asmx?WSDL

It is crucial to follow these directions in order for your app to work:

The Xcode project for this example can be found in gsoap/ios_plugin/examples/Weather.

Step 1: Generating stubs for C++ Proxy

To generate codes for the GeoIPService Web service, we first run the wsdl2h tool from the command line on the URL of the WSDL and use option -o to specify the output file (Alternatively, you can download the GlobalWeather.wsdl file and use the local file instead of the URL):

wsdl2h -o GeoIPService.h 'http://www.webservicex.net/globalweather.asmx?WSDL'

This generates the weather.h service definition header file with service operation definitions and types for the operation's data. By default, gSOAP assumes you will use C++ with STL.

To generate the stubs for the C++ proxy classes, run the soapcpp2. compiler:

soapcpp2 -j -CL -I$GSOAP_HOME/import weather.h

Option -j tells the compiler to generate the C++ proxy class and option -CL indicates client-side only files (soapcpp2 generates both client and server stubs and skeletons by default). Option -I is needed to import the stlvector.h file from the import directory in the gSOAP package to support serialization of STL vectors.

Step 2: Creating an Xcode project for iPhone

Create a Single View-based Application project and name it Weather. Open the ViewController.xib(main storyboard) file in the Interface Builder.Double-click on the View item and populate it with the views listed below and shown in Figure 3:

In Xcode, edit the file ViewController.h to make it look like the following:

1 #import <UIKit/UIKit.h>
2 
3 @interface ViewController : UIViewController {
4  UITextField* inputcity;
5 }
6 @property (strong, nonatomic) IBOutlet UITextField *inputcity;
7 
8 - (IBAction) buttonPressed;
9 @end

Set the inputcity outlet and the buttonFindCountry:(id)sender method to delegate action of the button.

In Xcode, edit the file info.plist to make it look like the following:

 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
   <dict>
     <key>NSAppTransportSecurity</key>
     <dict>
       <key>NSAllowsArbitraryLoads</key>
       <true/>
     </dict>
       <key>CFBundleDevelopmentRegion</key>
       <string>en</string>
       <key>CFBundleExecutable</key>
       <string>$(EXECUTABLE_NAME)</string>
       <key>CFBundleIdentifier</key>
       <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
       <key>CFBundleInfoDictionaryVersion</key>
       <string>6.0</string>
       <key>CFBundleName</key>
       <string>$(PRODUCT_NAME)</string>
       <key>CFBundlePackageType</key>
       <string>APPL</string>
       <key>CFBundleShortVersionString</key>
       <string>1.0</string>
       <key>CFBundleSignature</key>
       <string>????</string>
       <key>CFBundleVersion</key>
       <string>1</string>
       <key>LSRequiresIPhoneOS</key>
       <true/>
       <key>UILaunchStoryboardName</key>
       <string>LaunchScreen</string>
       <key>UIMainStoryboardFile</key>
       <string>Main</string>
       <key>UIRequiredDeviceCapabilities</key>
       <array>
         <string>armv7</string>
       </array>
       <key>UISupportedInterfaceOrientations</key>
       <array>
         <string>UIInterfaceOrientationPortrait</string>
         <string>UIInterfaceOrientationLandscapeLeft</string>
         <string>UIInterfaceOrientationLandscapeRight</string>
       </array>
  </dict>
</plist>

These changes ensure that you can access the web services despite the added layer of protection of App Transport Security).

Step 3: Adding generated source stubs to the Xcode project

Add the source files soapC.cpp, soapGloabalWeatherSoapProxy.cpp, soapGloabalWeatherSoapProxy.h, soapH.h, and soapStub.h generated in Step 1 of this tutorial to the project. Also add files stdsoap2.h and stdsoap2.cpp to the project from gsoap package and the iOS plugin files gsoapios.h and gsoapios.mm.

Once all of your files have been added to the project, ensure they are all of type "Objective C++ Source". This ensures that there will be no issues with mixing Objective C and C++ code.

Step 4: Implementing the Logic by calling the soap service

Firstly, edit file main.mm to import the file GlobalWeatherSoap.nsmap. Linking errors would arise without importing this namespace mapping.

1 // File: main.mm
2 
3 #import <UIKit/UIKit.h>
4 #import "AppDelegate.h"
5 #include "../../GlobalWeatherSoap.nsmap"
6 
7 int main(int argc, char * argv[]) {
8  @autoreleasepool {
9  return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
10  }
11 }

Then, implement the source file ViewController.mm as the following, where we used the gSOAP domcpp tool with option -i on an example weather XML data file to produce DOM code to extract the XML data (see further below):

1 #import "ViewController.h"
2 #include "soapGlobalWeatherSoapProxy.h"
3 #import "soapStub.h"
4 #import "gsoapios.h"
5 #include <fstream>
6 
7 typedef struct _ns__GetWeather RequestStruct;
8 typedef struct _ns__GetWeatherResponse ResponseStruct;
9 
10 @implementation ViewController
11 @synthesize inputcity;
12 
13 - (IBAction)buttonPressed:(id)sender {
14  RequestStruct sending;
15  ResponseStruct receiving;
16  std::string result = "";
17 
18  GlobalWeatherSoapProxy service;
19  soap_init(service.soap);
20 
21  // ----- register plugin for callbacks ------
22  soap_register_plugin(service.soap, soap_ios);
23 
24  std::string cname= std::string((char *)[inputcity.text UTF8String]);
25  sending.CityName = &cname;
26  std::string USA = "United States";
27  sending.CountryName = &USA;
28 
29  if (service.GetWeather(&sending, receiving) == SOAP_OK)
30  {
31  std::ofstream xmlwrite("/Users/bethanysanders/Documents/work/Weather/Weather/message.txt");
32  std::ifstream xmlread("/Users/bethanysanders/Documents/work/Weather/Weather/message.txt");
33 
34  if(xmlwrite.is_open())
35  {
36  xmlwrite << *(receiving.GetWeatherResult);
37  xmlwrite.close();
38 
39  struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_DOM_TREE);
40  ctx->double_format = "%lG";
41 
42  xsd__anyType dom(ctx);
43  if (xmlread.is_open())
44  xmlread >> dom;
45  if (dom.soap->error)
46  result = "Error: Try a different city";
47 
48  xsd__anyAttribute *att;
49  xsd__anyType *elt;
50 
51  if ((elt = dom.elt_get("Wind")))
52  {
53  xsd__anyType& dom_Wind = *elt;
54  if (dom_Wind.get_text())
55  {
56  result += "Wind = ";
57  result += dom_Wind.get_text();
58  }
59  }
60  if ((elt = dom.elt_get("Visibility")))
61  {
62  xsd__anyType& dom_Visibility = *elt;
63  if(dom_Visibility.get_text())
64  {
65  result += "\nVisibility = ";
66  result += dom_Visibility.get_text();
67  }
68 
69  }
70  if ((elt = dom.elt_get("SkyConditions")))
71  {
72  xsd__anyType& dom_SkyConditions = *elt;
73  if (dom_SkyConditions.get_text())
74  {
75  result += "\nSky Conditions = ";
76  result += dom_SkyConditions.get_text();
77  }
78  }
79  if ((elt = dom.elt_get("Temperature")))
80  {
81  xsd__anyType& dom_Temperature = *elt;
82  if (dom_Temperature.get_text())
83  {
84  result += "\nTemperature = ";
85  result += dom_Temperature.get_text();
86  }
87  }
88  if ((elt = dom.elt_get("DewPoint")))
89  {
90  xsd__anyType& dom_DewPoint = *elt;
91  if (dom_DewPoint.get_text())
92  {
93  result += "\nDew Point = ";
94  result += dom_DewPoint.get_text();
95  }
96  }
97  if ((elt = dom.elt_get("RelativeHumidity")))
98  {
99  xsd__anyType& dom_RelativeHumidity = *elt;
100  if (dom_RelativeHumidity.get_text())
101  {
102  result += "\nRelative Humidity = ";
103  result += dom_RelativeHumidity.get_text();
104  }
105  }
106  if ((elt = dom.elt_get("Pressure")))
107  {
108  xsd__anyType& dom_Pressure = *elt;
109  if (dom_Pressure.get_text())
110  {
111  result += "\nPressure = ";
112  result += dom_Pressure.get_text();
113  }
114  }
115 
116  xmlread.close();
117 
118  soap_destroy(ctx); // delete objects
119  soap_end(ctx); // delete DOM data
120  soap_free(ctx); // free context
121  }
122  }
123 
124  NSString *titleMsg;
125  NSString *Weather;
126  titleMsg = [NSString stringWithFormat: @"%@", @"Weather Results"];
127  Weather = [NSString stringWithUTF8String:result.c_str()];
128 
129  //show result as an alert
130  UIAlertController * alert = [UIAlertController
131  alertControllerWithTitle:titleMsg
132  message:Weather
133  preferredStyle:UIAlertControllerStyleAlert];
134  UIAlertAction* cancelButton = [UIAlertAction
135  actionWithTitle:@"OK"
136  style:UIAlertActionStyleDefault
137  handler:^(UIAlertAction * action){}];
138 
139  [alert addAction: cancelButton];
140  [self presentViewController:alert animated:YES completion:nil];
141 
142  service.destroy();
143 }
144 
145 - (void)viewDidLoad {
146  [super viewDidLoad];
147  // Do any additional setup after loading the view, typically from a nib.
148 }
149 
150 - (void)didReceiveMemoryWarning {
151  [super didReceiveMemoryWarning];
152  // Dispose of any resources that can be recreated.
153 }
154 
155 @end

You will notice there is much more code in this example's ViewController.mm. This is because this web service stores the whole XML response within a string instead of appropriate variables. The dom parser can fix this situation so that you can still access your results without having to parse the XML yourself. The dom code in this example was generated via command line in UNIX. To do so, once you have dom executable in your working directory, just execute the command

./domcpp -i weather.xml

where weather.xml is a file that stores an example xml response. The option -i is what tells the dom tool to generate the code you need to parse your result. To obtain an example XML response, test the web service on http://www.webservicex.net/New/Home/ServiceDetail/56.

Air Example (C++)

Airport Information Web Service is a live SOAP Web service that enables you to look up Airport Information of countries around the world. For some reason, some very well known countries are not supported by this web service. For example, no results for Russia will be returned. There is an error message within the app that shows whenever one of these countries is entered. Know that the error message is not an issue with the app you just built, but information that the web service itself does not provide.

This example shows how to develop a client application in C++ using gSOAP and the ios plugin, which consumes the Weather service on iOS using gSOAP. The WSDL file for this service can be downloaded at the following link: http://www.webservicex.net/airport.asmx?WSDL

It is crucial to follow these directions in order for your app to work:

The Xcode project for this example can be found in gsoap/ios_plugin/examples/Air.

Step 1: Generating stubs for C++ Proxy

To generate codes for the GeoIPService Web service, we first run the wsdl2h tool from the command line on the URL of the WSDL and use option -o to specify the output file (Alternatively, you can download the airport.wsdl file and use the local file instead of the URL):

wsdl2h -o airport.h 'http://www.webservicex.net/airport.asmx?WSDL'

This generates the airport.h service definition header file with service operation definitions and types for the operation's data. By default, gSOAP assumes you will use C++ with STL.

To generate the stubs for the C++ proxy classes, run the soapcpp2. compiler:

soapcpp2 -j -CL -I$GSOAP_HOME/import airport.h

Option -j tells the compiler to generate the C++ proxy class and option -CL indicates client-side only files (soapcpp2 generates both client and server stubs and skeletons by default). Option -I is needed to import the stlvector.h file from the import directory in the gSOAP package to support serialization of STL vectors.

Step 2: Creating an Xcode project for iPhone

Create a Single View-based Application project and name it Air. Open the ViewController.xib(main storyboard) file in the Interface Builder.Double-click on the View item and populate it with the views listed below and shown in Figure 3:

In Xcode, edit the file ViewController.h to make it look like the following:

1 #import <UIKit/UIKit.h>
2 
3 @interface ViewController : UIViewController {
4  UITextField* country_name;
5  UITextView* showResults;
6 }
7 @property (strong, nonatomic) IBOutlet UITextField *country_name;
8 @property (strong, nonatomic) IBOutlet UITextView *showResults;
9 
10 
11 -(IBAction) buttonPressed;
12 
13 @end

Set the country_name and showResults outlets and the buttonFindCountry:(id)sender method to delegate action of the button.

In Xcode, edit the file info.plist to make it look like the following:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>NSAppTransportSecurity</key>
    <dict>
      <key>NSAllowsArbitraryLoads</key>
      <true/>
    </dict>
      <key>CFBundleDevelopmentRegion</key>
      <string>en</string>
      <key>CFBundleExecutable</key>
      <string>$(EXECUTABLE_NAME)</string>
      <key>CFBundleIdentifier</key>
      <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
      <key>CFBundleInfoDictionaryVersion</key>
      <string>6.0</string>
      <key>CFBundleName</key>
      <string>$(PRODUCT_NAME)</string>
      <key>CFBundlePackageType</key>
      <string>APPL</string>
      <key>CFBundleShortVersionString</key>
      <string>1.0</string>
      <key>CFBundleSignature</key>
      <string>????</string>
      <key>CFBundleVersion</key>
      <string>1</string>
      <key>LSRequiresIPhoneOS</key>
      <true/>
      <key>UILaunchStoryboardName</key>
      <string>LaunchScreen</string>
      <key>UIMainStoryboardFile</key>
      <string>Main</string>
      <key>UIRequiredDeviceCapabilities</key>
      <array>
        <string>armv7</string>
      </array>
      <key>UISupportedInterfaceOrientations</key>
      <array>
        <string>UIInterfaceOrientationPortrait</string>
        <string>UIInterfaceOrientationLandscapeLeft</string>
        <string>UIInterfaceOrientationLandscapeRight</string>
      </array>
  </dict>
</plist>

These changes ensure that you can access the web services despite the added layer of protection of App Transport Security).

Step 3: Adding generated source stubs to the Xcode project

Add the source files soapC.cpp, soapairportSoapProxy.cpp, soapairportSoapProxy.h, soapH.h, and soapStub.h generated in Step 1 of this tutorial to the project. Also add files stdsoap2.h and stdsoap2.cpp to the project from the gSOAP package and the iOS plugin files gsoapios.h and gsoapios.mm.

Once all of your files have been added to the project, ensure they are all of type "Objective C++ Source". This ensures that there will be no issues with mixing Objective C and C++ code.

Step 4: Implementing the Logic by calling the soap service

Firstly, edit file main.mm to import the file airportSoap.nsmap. Linking errors would arise without importing this namespace mapping.

1 // File: main.mm
2 
3 #import <UIKit/UIKit.h>
4 #import "AppDelegate.h"
5 #include "../../airportSoap.nsmap"
6 
7 int main(int argc, char * argv[]) {
8  @autoreleasepool {
9  return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
10  }
11 }

Then, we implement the source file ViewController.mm as the following, where we used the gSOAP domcpp tool with option -i on an example weather XML data file to produce DOM code to extract the XML data (see further below)

1 #import "ViewController.h"
2 #include "soapairportSoapProxy.h"
3 #import "soapStub.h"
4 #import "gsoapios.h"
5 #include <fstream>
6 
7 @interface ViewController ()
8 
9 @end
10 
11 @implementation ViewController
12 @synthesize country_name;
13 @synthesize showResults;
14 
15 - (IBAction)buttonPressed:(id)sender {
16  _ns1__GetAirportInformationByCountry sending;
17  _ns1__GetAirportInformationByCountryResponse receiving;
18  std::string result = ""; //stores resulting data
19  bool webserviceresult = true; //keeps track of if the web service returns a readable result
20 
21  airportSoapProxy service;
22  soap_init(service.soap);
23 
24  // ----- register plugin for callbacks ------
25  soap_register_plugin(service.soap, soap_ios);
26 
27  std::string countryname = std::string((char*)[country_name.text UTF8String]);
28  sending.country = &countryname;
29 
30  std::ofstream out("/Users/bethanysanders/Documents/work/Air/Air/xmlmessage.txt");
31  std::ifstream in("/Users/bethanysanders/Documents/work/Air/Air/xmlmessage.txt");
32 
33  if (service.GetAirportInformationByCountry(&sending,receiving) == SOAP_OK)
34  if (out.is_open())
35  out << *(receiving.GetAirportInformationByCountryResult);
36 
37  out.close();
38 
39  struct soap *ctx = soap_new1(SOAP_C_UTFSTRING | SOAP_XML_INDENT | SOAP_DOM_TREE);
40  ctx->double_format = "%lG";
41 
42  xsd__anyType dom(ctx);
43  if (in.is_open())
44  in >> dom;
45  if (dom.soap->error)
46  webserviceresult = false;
47  xsd__anyType *elt;
48 
49  for (xsd__anyType *it = dom.elt_get("Table"); it; it = it->get_next())
50  {
51  xsd__anyType& dom_Table = *it;
52  if ((elt = dom_Table.elt_get("AirportCode")))
53  {
54  xsd__anyType& dom_Table_AirportCode = *elt;
55  if (dom_Table_AirportCode.get_text())
56  {
57  result += "\n\nAirport Code = ";
58  result += dom_Table_AirportCode.get_text();
59  }
60 
61  }
62  if ((elt = dom_Table.elt_get("CityOrAirportName")))
63  {
64  xsd__anyType& dom_Table_CityOrAirportName = *elt;
65  if (dom_Table_CityOrAirportName.get_text())
66  {
67  result += "\nCity or Airport Name = ";
68  result += dom_Table_CityOrAirportName.get_text();
69  }
70  }
71  if ((elt = dom_Table.elt_get("Country")))
72  {
73  xsd__anyType& dom_Table_Country = *elt;
74  if (dom_Table_Country.get_text())
75  {
76  result += "\nCountry = ";
77  result += dom_Table_Country.get_text();
78  }
79  }
80  if ((elt = dom_Table.elt_get("CountryAbbrviation")))
81  {
82  xsd__anyType& dom_Table_CountryAbbrviation = *elt;
83  if (dom_Table_CountryAbbrviation.get_text())
84  {
85  result += "\nCountry Abbreviation = ";
86  result += dom_Table_CountryAbbrviation.get_text();
87  }
88  }
89  if ((elt = dom_Table.elt_get("CountryCode")))
90  {
91  xsd__anyType& dom_Table_CountryCode = *elt;
92  if (dom_Table_CountryCode.get_text())
93  {
94  result += "\nCountry Code = ";
95  result += dom_Table_CountryCode.get_text();
96  }
97  }
98  if ((elt = dom_Table.elt_get("GMTOffset")))
99  {
100  xsd__anyType& dom_Table_GMTOffset = *elt;
101  if (dom_Table_GMTOffset.get_text())
102  {
103  result += "\nGMT Offset = ";
104  result += dom_Table_GMTOffset.get_text();
105  }
106  }
107  if ((elt = dom_Table.elt_get("RunwayLengthFeet")))
108  {
109  xsd__anyType& dom_Table_RunwayLengthFeet = *elt;
110  if (dom_Table_RunwayLengthFeet.get_text())
111  {
112  result += "\nRunway Length (feet) = ";
113  result += dom_Table_RunwayLengthFeet.get_text();
114  }
115  }
116  if ((elt = dom_Table.elt_get("RunwayElevationFeet")))
117  {
118  xsd__anyType& dom_Table_RunwayElevationFeet = *elt;
119  if (dom_Table_RunwayElevationFeet.get_text())
120  {
121  result += "\nRunway Elevation (feet) = ";
122  result += dom_Table_RunwayElevationFeet.get_text();
123  }
124  }
125  if ((elt = dom_Table.elt_get("LatitudeDegree")))
126  {
127  xsd__anyType& dom_Table_LatitudeDegree = *elt;
128  if (dom_Table_LatitudeDegree.get_text())
129  {
130  result += "\nLatitude Degree = ";
131  result += dom_Table_LatitudeDegree.get_text();
132  }
133  }
134  if ((elt = dom_Table.elt_get("LatitudeMinute")))
135  {
136  xsd__anyType& dom_Table_LatitudeMinute = *elt;
137  if (dom_Table_LatitudeMinute.get_text())
138  {
139  result += "\nLatitude Minute = ";
140  result += dom_Table_LatitudeMinute.get_text();
141  }
142  }
143  if ((elt = dom_Table.elt_get("LatitudeSecond")))
144  {
145  xsd__anyType& dom_Table_LatitudeSecond = *elt;
146  if (dom_Table_LatitudeSecond.get_text())
147  {
148  result += "\nLatitude Second = ";
149  result += dom_Table_LatitudeSecond.get_text();
150  }
151  }
152  if ((elt = dom_Table.elt_get("LatitudeNpeerS")))
153  {
154  xsd__anyType& dom_Table_LatitudeNpeerS = *elt;
155  if (dom_Table_LatitudeNpeerS.get_text())
156  {
157  result += "\nLatitude N or S = ";
158  result += dom_Table_LatitudeNpeerS.get_text();
159  }
160  }
161  if ((elt = dom_Table.elt_get("LongitudeDegree")))
162  {
163  xsd__anyType& dom_Table_LongitudeDegree = *elt;
164  if (dom_Table_LongitudeDegree.get_text())
165  {
166  result += "\nLongitude Degree = ";
167  result += dom_Table_LongitudeDegree.get_text();
168  }
169  }
170  if ((elt = dom_Table.elt_get("LongitudeMinute")))
171  {
172  xsd__anyType& dom_Table_LongitudeMinute = *elt;
173  if (dom_Table_LongitudeMinute.get_text())
174  {
175  result += "\nLongitude Minute = ";
176  result += dom_Table_LongitudeMinute.get_text();
177  }
178  }
179  if ((elt = dom_Table.elt_get("LongitudeSeconds")))
180  {
181  xsd__anyType& dom_Table_LongitudeSeconds = *elt;
182  if (dom_Table_LongitudeSeconds.get_text())
183  {
184  result += "\nLongitude Second = ";
185  result += dom_Table_LongitudeSeconds.get_text();
186  }
187  }
188  if ((elt = dom_Table.elt_get("LongitudeEperW")))
189  {
190  xsd__anyType& dom_Table_LongitudeEperW = *elt;
191  if (dom_Table_LongitudeEperW.get_text())
192  {
193  result += "\nLongitude E or W = ";
194  result += dom_Table_LongitudeEperW.get_text();
195  }
196  }
197  }
198 
199  NSString *airPorts = [NSString stringWithUTF8String:result.c_str()];
200  if (result == "" || !webserviceresult)
201  airPorts = [NSString stringWithFormat: @"%@", @"Error: no know information. Try a different country."];
202 
203  showResults.editable = NO;
204  showResults.showsVerticalScrollIndicator = YES;
205  showResults.text = airPorts;
206 
207  soap_destroy(ctx); // delete objects
208  soap_end(ctx); // delete DOM data
209  soap_free(ctx); // free context
210 
211  service.destroy();
212 }
213 
214 - (void)viewDidLoad {
215  [super viewDidLoad];
216  // Do any additional setup after loading the view, typically from a nib.
217 }
218 
219 - (void)didReceiveMemoryWarning {
220  [super didReceiveMemoryWarning];
221  // Dispose of any resources that can be recreated.
222 }
223 @end

You will notice there is much more code in this example's ViewController.mm. This is because this web service stores the whole XML response within a string instead of appropriate variables. The dom parser can fix this situation so that you can still access your results without having to parse the XML yourself. The dom code in this example was generated via command line in UNIX. To do so, once you have dom executable in your working directory, just execute the command

./domcpp -i airPorts.xml

where airPorts.xml is a file that stores an example xml response. The option -i is what tells the dom tool to generate the code you need to parse your result. To obtain an example XML response, test the web service on http://www.webservicex.net/New/Home/ServiceDetail/20.