background image
HomeRecent PostsDrupalSearchTagsRSSContactAboutAccount
Eric.London's picture

In this article, I'll share some code that I wrote that uses Appcelerator Titanium to create a simple iPhone application that communicates with a Drupal site via SOAP. From the iPhone side of things I used the Suds Javascript SOAP client, and from Drupal, I created a nusoap soap server instance.

Let's start with the SOAP server instance. I created a module called "nusoap", and copied the lib folder from nusoap into it. I then created a file in the module's directory called "soap-server.php" and added the following code. For the sake of this article, I did not include the rest of the Drupal module's code.

<?php
// $Id$

// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

// define path to nusoap library file
$nu_soap_path = 'lib/nusoap.php';

// ensure nu_soap library exsists
if (!file_exists($nu_soap_path)) {
  die(
'An error has occurred initializing the soap server.');
}

// include nu_soap library
require_once ($nu_soap_path);

// create new soap server instance
$soap_server = new nusoap_server();

// configure wsdl
$soap_server->configureWSDL(NUSOAP_NAME_SPACE, 'urn:'. NUSOAP_NAME_SPACE);

$soap_server->register(
 
// method name
 
'login_fetch_data',
 
// input args
 
array(
   
'name' => 'xsd:string',
   
'pass' => 'xsd:string',
  ),
 
// output args
 
array(
   
'return' => 'xsd:string'
 
),
 
// namespace
 
'uri:'. NUSOAP_NAME_SPACE,
 
// SOAPAction
 
'uri:'. NUSOAP_NAME_SPACE .'#login_fetch_data',
 
// style
 
'rpc',
 
// use
 
'encoded'
);

// change directory to docroot
chdir($_SERVER['DOCUMENT_ROOT']);

// drupal bootstrap
require_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

// process raw post data
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$soap_server->service($HTTP_RAW_POST_DATA);

/**
* Define soap methods
*/

function login_fetch_data($name, $pass) {
 
 
$params = array(
   
'name' => $name,
   
'pass' => $pass,
  );
 
 
$account = user_authenticate($params);
 
 
// check for error
 
if (!is_object($account)) {
   
// handle authentication error here
    // return something
 
}
   
 
// return user uid, or some other piece of data
 
return $account->uid;
 
}
?>

The above code registers one soap server method "login_fetch_data" which accepts the arguments (name and pass), loads a full Drupal bootstrap, authenticates the user, and returns back the user's uid. By browsing to the soap server file directly, I can see nusoap's WSDL definition: (example: http://drupal.vm/sites/all/modules/custom/nusoap/soap-server.php)

SOAP Server Methods

To test if the soap server method was working, I used the follow PHP code snippet (which I pulled out of a test page callback):

<?php
// define path to nu_soap library, relative to module directory
define('NUSOAP_PATH', drupal_get_path('module', 'nusoap') .'/lib/nusoap.php');

// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

// include nu_soap library
require_once(NUSOAP_PATH);

// define wsdl path
$wsdl_path = 'http://' . $_SERVER['HTTP_HOST'] . base_path() . drupal_get_path('module', 'nusoap') . '/soap-server.php?wsdl';

// create new soap client instance
$soap_client = new nusoap_client($wsdl_path, true);

// check for error
$error = $soap_client->getError();
if (
$error) {
 
// handle error
}
 
$args = array(
 
'name' => 'Eric.London',
 
'pass' => 'super secret password',
);

// call soap server method
$result = $soap_client->call('login_fetch_data', $args);

$output = "";
$output .= "<pre>";
$output .= "SENT: ";
$output .= print_r($args, true);
$output .= "RECEIVED: ";
$output .= print_r($result, true);
$output .= "</pre>";
   
return
$output;
?>

I then created a new Titanium iPhone project. In the Resources directory, I created a directory called "soap-test-includes" to contain the suds.js library, and the soap.js file which uses this library. The suds.js file is available in the Kitchen Sink Titanium project, if you choose to import it.

Titanium folder structure

I added the following code to the app.js file:

// this sets the background color of the master UIView (when there are no windows/tab groups on it)
Titanium.UI.setBackgroundColor('#000');

// create tab group
var tabGroup = Titanium.UI.createTabGroup();

//
// create base UI tab and root window
//
var win1 = Titanium.UI.createWindow({ 
    title:'Soap Test',
    backgroundColor:'#fff',
    url: 'soap-test-includes/soap.js'
});
var tab1 = Titanium.UI.createTab({ 
    icon:'KS_nav_views.png',
    title:'Soap Test',
    window:win1
});

//
//  add tabs
//
tabGroup.addTab(tab1); 

// open tab group
tabGroup.open();

And, I added the following code to the soap.js include file:

// include suds soap library
Titanium.include('suds.js');

var window = Ti.UI.currentWindow;
var label = Ti.UI.createLabel({
  top: 10,
  left: 10,
  width: 'auto',
  height: 'auto',
  text: 'Contacting Drupal soap server...\n'
});

window.add(label);

// define soap params
var url = "http://drupal.vm/sites/all/modules/custom/nusoap/soap-server.php";
var callparams = {
  name: 'Eric.London',
  pass: 'super secret password'
};

// create new suds soap client
var suds = new SudsClient({
  endpoint: url,
  targetNamespace: 'erl.dev'
});

try {
 
  // make soap server call
  suds.invoke('login_fetch_data', callparams, function(xmlDoc) {
 
    // fetch results from method
    results = xmlDoc.documentElement.getElementsByTagName('login_fetch_dataResponse');
      
    // check if results exits
    if (results && results.length>0) {
 
      // output results
      label.text += "User uid: " + results.item(0).text;     

    }
    else {
      // handle error    
    }

  });
} catch(e) {
  Ti.API.error('Error: ' + e);
}

When I build and run the project, the iOS Simulator opens and shows the results of the soap connection!

iOS soap connection

As you can see, Titanium allows you to write an iPhone application without touching the native Objective-C code. In this example, all the code was written in javascript, with a PHP backend. Much of the javascript code I used is based on code found in the Kitchen Sink project; I suggest you check it out :)

Eric.London's picture

In this tutorial, I'll show how you can make a SOAP call from a Drupal page callback using the nusoap library.

For this example, I decided to create a sample soap server instance for testing purposes. I created the following directory structure for my new module: sites/all/modules/custom/nusoap/. I then download the nusoap library (nusoap-0.7.3.zip), extracted the archive, and put the "lib" folder in my module directory (sites/all/modules/custom/nusoap/lib).

In this directory, I created a file called "soap-server.php" to contain my soap server instance and added the following code:

<?php
// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

// define path to nusoap library file
$nu_soap_path = 'lib/nusoap.php';

// ensure nu_soap library exsists
if (!file_exists($nu_soap_path)) {
  die(
'An error has occurred initializing the soap server.');
}

// include nu_soap library
require_once ($nu_soap_path);

// create new soap server instance
$soap_server = new nusoap_server();

// configure wsdl
$soap_server->configureWSDL(NUSOAP_NAME_SPACE, 'urn:'. NUSOAP_NAME_SPACE);

// add a custom data type: person
$soap_server->wsdl->addComplexType(
 
'person',
 
'complexType',
 
'struct',
 
'all',
 
'',
  array(
   
'firstName' => array(
     
'name' => 'firstName',
     
'type' => 'xsd:string',
    ),
   
'lastName' => array(
     
'name' => 'lastName',
     
'type' => 'xsd:string',
    ),
  )
);

// register method: personTransfer
$soap_server->register(
 
// method name
 
'personTransfer',
 
// input args
 
array('person' => 'tns:person'),
 
// output args
 
array('return' => 'tns:person'),
 
// namespace
 
'uri:'. NUSOAP_NAME_SPACE,
 
// SOAPAction
 
'uri:'. NUSOAP_NAME_SPACE .'#personTransfer',
 
// style
 
'rpc',
 
// use
 
'encoded'
);

// process raw post data
$HTTP_RAW_POST_DATA = isset($HTTP_RAW_POST_DATA) ? $HTTP_RAW_POST_DATA : '';
$soap_server->service($HTTP_RAW_POST_DATA);

/**
* Define soap methods
*/
function personTransfer($person = array()) {

 
// per testing, modify data
 
foreach ($person as $key => $value) {
   
$person[$key] = $value . "!";
  };

  return
$person;

}
?>

Now, if I browse directly to my soap-server.php file (for example: http://drupal.erl.dev/sites/all/modules/custom/nusoap/soap-server.php), I see the following screen:

soap server

Clicking on the WSDL link will show my automatically generated WSDL/XML; clicking on the method name "personTransfer" will show more details about the soap server method. Thanks nusoap!

Next, I created the custom Drupal module file...

<?php
// define namespace
define('NUSOAP_NAME_SPACE', 'erl.dev');

/**
* Implements hook_perm()
*/
function nusoap_perm() {
  return array(
'access soap');
}

/**
* Implements hook_menu()
*/
function nusoap_menu() {

 
$items = array();
 
 
$items['soap-client'] = array(
   
'title' => t('Soap client'),
   
'description' => t('Soap client'),
   
'page callback' => 'nusoap_page_callback_soap_client',
   
'access arguments' => array('access soap'),
   
'type' => MENU_CALLBACK
 
);
 
  return
$items;

}

/**
* Implements custom page callback for soap client
*/
function nusoap_page_callback_soap_client() {

 
// include nu_soap library
 
require_once(drupal_get_path('module', 'nusoap') .'/lib/nusoap.php');

 
// define wsdl path
 
$wsdl_path = 'http://' . $_SERVER['HTTP_HOST'] . base_path() . drupal_get_path('module', 'nusoap') . '/soap-server.php?wsdl';

 
// create new soap client instance
 
$soap_client = new nusoap_client($wsdl_path, true);

 
// check for error
 
$error = $soap_client->getError();
  if (
$error) {
   
// handle error
 
}

 
// define method arguments
 
$args = array(
   
'person' => array(
     
'firstName' => 'Eric',
     
'lastName' => 'London'
   
)
  );

 
// call soap server method
 
$result = $soap_client->call('personTransfer', $args);

 
// debug output:
 
$output = "";
 
$output .= "<pre>";
 
$output .= "SENT: ";
 
$output .= print_r($args, true);
 
$output .= "RECEIVED: ";
 
$output .= print_r($result, true);
 
$output .= "</pre>";
   
  return
$output;

}
?>

Browsing to the new page callback (ex: http://drupal.erl.dev/soap-client) shows the following debug output. sweet.

soap client

Syndicate content