Developing with Objective Pascal

Part 11: Using Web Services


Contents

Introduction
Requirements
Parsing the WSDL File
Testing the Web service
Making the code cross-platform


Introduction

Many iOS native apps can be thought of as Web apps, although of the out-of-browser variety. Look at the standard apps included with iOS: Mail, Stocks, Weather, etc. - they all need an Internet connection to show up-to-date information. But unlike in-browser Web apps, they look and feel great (more responsive, better transitions) and often have limited functionality even when off-line.

These notes describe how to add support to your iOS app for retrieving Internet data via a Web service using the cross-platform, open-source Web Service Toolkit.


Requirements


Parsing the WSDL file

First compile the WST's ws_helper program. Open a Terminal window and change to the ws_helper subdirectory in your WST checkout, then enter the following (or put it in a script file):
fpc -Cirot -Fi../ -Fu../ -Fu../wst_rtti_filter -dWST_HANDLE_DOC ws_helper.pas
Next parse the WSDL file for the Web service you want to use. For this example, we'll use a WSDL file from the U.S. National Weather Service. Download file ndfdXML.wsdl from this site:

http://www.usgovxml.com/DataService.aspx?ds=NDFD.

Parse ndfdXML.wsdl by entering the following all on one line (or put it in a script):

ws_helper -uA -p -o. -fhttp_graphical_weather_gov_xml_DWMLgen_schema_DWML_xsd=ndfdDWML_schema ndfdXML.wsdl
For more information about the various switches used, run ws_helper without any switches. The one switch that might require some explanation is the -f switch. It's used so that the name of one of the generated units is shortened to just ndfdDWML_schema.

With ndfdXML.wsdl, the ws_helper parser generates these files that you'll use along with the WST units:

  ndfdXML.pas
  ndfdXML.wst
  ndfdXML_proxy.pas
  ndfdDWML_schema.pas

Testing the Web service

Web Service Toolkit can be used with any of these HTTP libraries:

  Indy
  Synapse
  ICS
  FPC's HTTP client
  Foundation framework-based HTTP client (for iOS)
WST includes an xxxx_http_protocol.pas file for each library that implements two library-specific classes required by WST. You use the appropriate protocol unit along with the units generated from the Web service's WSDL file to call the Web service from your app.

To test on iOS, we'll use the NDFD Web service to retrieve a 5-day weather forecast for Silverton, Colorado, a town in the Rocky Mountains that generally has fairly cool nighttime temperatures because of its elevation (9,305 feet, or about 2,835 meters).

Create an iOS Pascal project in Xcode, then do the following:

  1. Copy the four ndfd files created by ws_helper into the project's source folder, or add their location to FPC's unit search path with the -Fu switch (if you created your app with the Xcode Pascal template, you can add -Fu to the FPC_COMMON build setting).

  2. Download the two iOS HTTP client files (ns_url_request.pas and NSHelpers.pas), then copy them into the project's source folder or add their location to FPC's unit search path with the -Fu switch.

  3. Add the location of the WST checkout to FPC's unit search path with the -Fu switch.

  4. Add these units to your app's uses statement:

    SysUtils,
    DateUtils,
    ns_http_protocol,  //WST units
    soap_formatter,
    base_service_intf,
    ndfdXML,  //Web service units
    ndfdXML_proxy,
    ndfdDWML_schema
    
  5. Add the following code to your app's startup code (for example, in application_didFinishLaunchingWithOptions) or to an action receiving method or event handler. Note that all we're doing at this point is testing the Web service, so we'll just dump the retrieved XML to the Xcode debug window so we can see what's returned by the Web service.
    var
      NdfdService : ndfdXMLPortType;
      Latitude    : Double;
      Longitude   : Double;
      NumDays     : Integer;  
      StartDate   : TDateRemotable;
      UnitsToUse  : ndfdDWML_schema.unitType;
    begin
      NS_RegisterHTTP_Transport;
      try
        NdfdService := wst_CreateInstance_ndfdXMLPortType;
        if not Assigned(NdfdService) then
          WriteLn('Unable to connect to NDFD Web service.')
        else
          begin
          Latitude := 37.81;  //Silverton, CO's lat/long
          Longitude := -107.66;
          NumDays := 5;
          StartDate := TDateRemotable.Create;
          StartDate.AsDate := Today;
          UnitsToUse := ndfdDWML_schema.e;  //use ndfdDWML_schema.m for metric
          try
            WriteLn(NdfdService.NDFDgenByDay(Latitude, Longitude, 
                                             StartDate, NumDays, UnitsToUse, 
                                             formatType__24_hourly));
          finally
            StartDate.Free;
            end;
          end;
      except on E:Exception do
        WriteLn(E.Message);
      end;
    
  6. Now run your app and look at the XML dumped to the debug window (if you don't see the debug window, choose View | Debug Area | Show Debug Area). You can also see the output in the Log navigator's debug log. Scroll down to the forecast's maximum/minimum temperatures, then check to see that they're close to what a reputable weather site gives (for example Weather Underground).

Making the code cross-platform

Since WST is cross-platform, it makes sense to write your code to be cross-platform too, even if you're currently interested only in iOS. That way you can effortlessly recycle your code if you target other platforms in the future. Here are three areas to focus on.

Separating WST-related code from the UI

In the code fragment above, the WriteLn calls are only placeholders for use in testing. In an actual app, you would present any error message or the results of a Web service call in the app's user interface. But you would not mix non-UI code like Web service calls with UI code that does the presentation of the results of those calls. Instead, you should pass back the error message or results to the calling code without regard to what the calling code ultimately does with it - that way your non-UI code can be used in a console app, Web app, DLL, etc. as well as in a desktop or mobile app, regardless of how the app's user interface is implemented (it may not even have a user interface).

Naturally that means moving the WST-related code into its own unit and either wrapping it in a class or in a series of functions. In no case should this unit include VCL/LCL units like Forms, Dialogs, Controls, etc. in its uses statement.

The same goes for inputs to your WST-related code. For example, with the NDFD Web service, you would probably pass in the latitude and longitude rather than hard-wiring it the way we did for testing purposes. The NDFD Web service also provides methods for retrieving lat/long values for cities, zip codes, etc. You could wrap up these methods in a similar fashion.

Supporting multiple HTTP libraries

First decide what HTTP libraries you're going to use. For example, you might decide like this:

To handle these libraries conditionally, change your uses statement so it includes something like this:

{$IFDEF FPC}
 {$IF DEFINED(IPHONESIM) OR (DEFINED(DARWIN) AND DEFINED(CPUARM))}  {iOS?}
  ns_http_protocol,
 {$ELSE}
  fpc_http_protocol,
 {$IFEND}
{$ELSE}  {Delphi}
  delphi_init_com,
  indy_http_protocol,
{$ENDIF}
Register the libraries conditionally in a similar fashion:

{$IFDEF FPC}
 {$IF DEFINED(IPHONESIM) OR (DEFINED(DARWIN) AND DEFINED(CPUARM))}
  NS_RegisterHTTP_Transport;
 {$ELSE}
  FPC_RegisterHTTP_Transport;
 {$IFEND}
{$ELSE}
  INDY_RegisterHTTP_Transport;
{$ENDIF}  
That's all you need to do. The rest of your WST-related code will be the same for all platforms.

Processing XML returned by the Web service

Many Web services, including NDFD, return data as XML, so you'll need to process this XML to find the data you need. Unfortunately the Free Pascal and Delphi XML units are not compatible, so you may need to write this code twice, once for Free Pascal and once for Delphi. For example, with Free Pascal you would use the XMLRead and DOM units, with Delphi you would use the XMLIntf and XMLDoc units. You could also develop a wrapper unit for both that conditionally compiles the appropriate code depending on compiler used.

Note that the NDFD methods return XML in a string. You could write this string to a temporary file, passing this file name back to the calling code, which then passes it on to your XML processing code. You could also write the XML string to a stream and pass the stream object back to the calling code. You can load either a file or a stream with the Free Pascal and Delphi XML parsers.


Copyright 2012 by Phil Hess.

macpgmr (at) fastermac (dot) net

First posted Jan. 5, 2012; last edited May 9, 2013.