digital Infuzion
DIFZ Blogs Home Contact Us Site Map Employee Login
About Us Industries Services Contracting Vehicles Publications

 
DIFZ Blogs
 
DIFZ Blogs : iPhone + Microsoft WCF Services + Security
iPhone + Microsoft WCF Services + Security

iPhone + Microsoft WCF Services + Security

I have been heavily involved in experimenting with the iPhone SDK for some applications we may produce to support Clinical Trials work.  Our company has been mostly doing .NET development for the past 7 years and I have been architecting the solutions solely on the .NET platform.  So experimenting with cocoa, Objective-c, Interface Builder and the iPhone in general was going to be a challenging adventure.

I am planning on creating a series of blogs to address some of the main issues when translating technology from the .NET world to the iPhone world.  The first blog in this series addresses the concern of Data Interoperability.  All of our existing applications are built with Data Interoperability in mind.  These applications have a UI layer, Database Layer and an intermediate layer where we transfer data from the Database to the UI.  In many cases this is done using Web services, or in the new Microsoft way, WCF (Windows Communication Foundation) services.

So after I managed to get a basic application running on the iPhone, I wanted to be able to pull data from our existing WCF web services and integrate that data into an iPhone widget.  Here lies the problem.  Currently there is no direct API provided by Apple to generate nice proxy classes and stubs to hook up to web services.  Microsoft provides very nice code generators to generate proxy classes which take away all the complexities of communicating with SOAP messages.  So after digging around the API and consulting with some obscure documentation I was able to locate four objects which can be used to call web services and process the data.  These four objects are:

  • NSURLRequest
  • NSURLResponse
  • NSURLConnection
  •  NSXMLParser.

By using these 4 objects you can call any Web Service, WCF Service, REST service, RSS Feeds, etc.  The only trouble is that you have to manually create the messages which need to be transferred to the services.  In this blog, I am only going to discus how to use these objects to call a WCF service.

To start off, you need to have a WCF service.  In our case, this service is secured by User Name and Password.  It is also only accessible through HTTPS (SSL).  For those of you who, may be interested, below is the WCF service configuration.


<
system.diagnostics>
    <
sources>
       
        <
source name="System.ServiceModel.MessageLogging" switchValue="Verbose,ActivityTracing">
            <
listeners>
                <
add type="System.Diagnostics.DefaultTraceListener" name="Default">
                    <
filter type="" />
                add>
                <
add name="ServiceModelMessageLoggingListener">
                    <
filter type="" />
                add>
            listeners>
        source>
       
        <
source name="System.ServiceModel" switchValue="Verbose,ActivityTracing"
            propagateActivity="true">
            <
listeners>
                <
add type="System.Diagnostics.DefaultTraceListener" name="Default">
                    <
filter type="" />
                add>
                <
add name="ServiceModelTraceListener">
                    <
filter type="" />
                add>
            listeners>
        source>
    sources>
   
    <
sharedListeners>
       
        <
add initializeData="C:\Web_messages.svclog"
            type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            name="ServiceModelMessageLoggingListener" traceOutputOptions="Timestamp">
            <
filter type="" />
        add>
       
        <
add initializeData="C:\Web_tracelog.svclog"
            type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
            name="ServiceModelTraceListener" traceOutputOptions="Timestamp">
            <
filter type="" />
        add>
    sharedListeners>
    <
trace autoflush="true" />
system.diagnostics>
<
system.serviceModel>

    <
diagnostics wmiProviderEnabled="true">
    <
messageLogging logEntireMessage="true" logMalformedMessages="true"
        logMessagesAtServiceLevel="true" logMessagesAtTransportLevel="true" />
    diagnostics>
    <
services>
        <
service behaviorConfiguration="FootballPool_DAL.GameDataBehavior"
            name="FootballPool_DAL.GameData">
        <
host>
            <
baseAddresses>
                <
add baseAddress="https://[Server URL]/Footballpool_DAL"/>
            baseAddresses>
        host>
       
        <
endpoint address="" binding="basicHttpBinding" bindingConfiguration="SecureBindingsTransport" contract="FootballPool_DAL.IGameData">
            <
identity>
                <
dns value="localhost" />
            identity>
        endpoint>
       
        <
endpoint address="mex" binding="mexHttpsBinding" contract="IMetadataExchange" />
        service>
    services>
    <
bindings>
        <
basicHttpBinding>
            
            <
binding name="SecureBindingsTransport" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647">
                <
readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
                    maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
               
                <
security mode="TransportWithMessageCredential">
                    <
message clientCredentialType="UserName"/>
                security>
            binding>
        basicHttpBinding>
    bindings>
    <
behaviors>
        <
serviceBehaviors>
            <
behavior name="FootballPool_DAL.GameDataBehavior">
                <
serviceMetadata httpGetEnabled="false" />
                <
serviceDebug includeExceptionDetailInFaults="true" />
                <
dataContractSerializer maxItemsInObjectGraph="6553600"/>
                <
serviceCredentials>
                   
                    <
userNameAuthentication userNamePasswordValidationMode="Custom"
                        customUserNamePasswordValidatorType="FootballPool_DAL.FBPoolAuthenticaion, FootballPool_DAL" />
                    <
serviceCertificate findValue="CN=[Certificate URL]"/>
                serviceCredentials>
            behavior>
        serviceBehaviors>
    behaviors>
system.serviceModel>

Once your WCF service is properly configured and you have the appropriate diagnostics setting to log the messages, you can test your WCF service and trace the full request and response using the svcTraceViewer.exe utility, which is found in the following directory: C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SvcTraceViewer.exe  This tool will show you how .NET expects the SOAP Message Request and what .NET will return in the SOAP Message Response.  You can then use this to construct the same message using NSMutableString.  Below is a screen shot of how this tool looks, with a sample request.

svcTraceViewer

So now on to the iPhone Objective-c Code.  This becomes fairly simple now.  I am not going to get into the details of how these objects work, I am just going to show you the code.  The comments within the code will tell you what is going on.  So once you have this structure set up, you will be able to call any .NET WCF Service from an iPhone Application.

- (NSMutableData*)initiateConnection{     NSMutableString *sRequest = [[NSMutableString alloc] init];
     // Create the SOAP body
    [sRequest appendString:@""];
    // This is the header section it is expecting in the body
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    // This is where you pass in the user id and password.  This is sent in the body header as clear text, but since you will be using HTTPS it will be somewhat encrypted.
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@"DIFZ"];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@"TheBest!"];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    // This is the body where you can pass in any parameters the WCF service expects
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
    [sRequest appendString:@""];
   
     // The URL of the Webserver
    NSURL *myWebserverURL = [NSURL URLWithString:@"https://[Server URL]/FootballPool_DAL/GameData.svc"];
    // Use the private method setAllowsAnyHTTPSCertificate:forHost:
    // to not validate the HTTPS certificate.  This is used if you are using a testing environment and have
    // a sample SSL certificate set up
    [NSURLRequest setAllowsAnyHTTPSCertificate:YES forHost:[myWebserverURL host]];
     NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:myWebserverURL];
    // Add the Required WCF Header Values.  This is what the WCF service expects in the header.
    [request addValue:@"text/xml; charset=utf-8" forHTTPHeaderField:@"Content-Type"];
    [request addValue:@"http://tempuri.org/IGameData/GetWeeksList" forHTTPHeaderField:@"SOAPAction"];
    // Set the action to Post
    [request setHTTPMethod:@"POST"];
    // Set the body
    [request setHTTPBody:[sRequest dataUsingEncoding:NSUTF8StringEncoding]];
    // Create the connection
    NSURLConnection *conn = [[NSURLConnection alloc] initWithRequest:request delegate:self];
    // Check the connection object
    if(conn)
    {
      myMutableData=[[NSMutableData data] retain];
    }
    // Make this class the delegate so that the other connection events fire here.
    [NSURLConnection connectionWithRequest:request delegate:self];
   
    NSError *WSerror;
    NSURLResponse *WSresponse;
    // Execute the WCF Service and return the data in an NSMutableData object
    myMutableData = [NSURLConnection sendSynchronousRequest:request returningResponse:&WSresponse error:&WSerror];
    // Return the data to the caller for processing
    return myMutableData;
}
 
+ (BOOL)allowsAnyHTTPSCertificateForHost:(NSString*)host
{
    return YES;
}
 
+ (void)setAllowsAnyHTTPSCertificate:(BOOL)allow forHost:(NSString*)host
{
}

Now that you are able to call the WCF service and retrieve the results, you need to process the returned XML.  To do this, use the NSXMLParser object.  There is a great example of how this works in the iPhone SDK.  The example is titled: seismic.  I used this sample and copied the code from it to process the returned XML.  Below is the syntax for calling the parser.

- (IBAction)btnHitMe_Click:(id)sender {
    // Create an object to the class above which is the connection to the WCF Service
 
    WSCon_Weeks *DataCon = [[WSCon_Weeks alloc] init];
    // call the WCF Service above and return the NSMutableData object
    self.WeeksListData = [DataCon initiateConnection];
    // Create the XML Parser Objects
    NSXMLParser *parser = [[NSXMLParser alloc] initWithData:self.WeeksListData];
    // Set self as the delegate of the parser so that it will receive the parser delegate methods callbacks.
    [parser setDelegate:self];
    // Set Parser Options
    [parser setShouldProcessNamespaces:NO];
    [parser setShouldReportNamespacePrefixes:NO];
    [parser setShouldResolveExternalEntities:NO];
    //
Begin parsing the XML
    [parser parse];
   // Refresh the picker view with the latest data
    [pvWeeksList reloadComponent:0];
    [parser release];
}

First off, thanks a ton for posting this. I have been looking for this information for a while now and had started writing my own SOAP wrapper. This should save me quite a bit of time.

But: I tried this out over http on a WCF service running on my PC and iPhone app running on my Mac. My header was much more complex than the one you display above, needing quite a few more little tweaks. I got the messages in my SvcTraceViewer by running a local .net client and refreshing the viewer. After hacking around at the sRequest contents, I finally got a response that wasn't 'malformed' xml. But it was a security related error. My question is, can I simplify the required header on the web service? I will continue to research this and see what I can come up with. I'll post here if I find it before you reply.

Thanks again for the great post.
-nait
Comment By At 9/30/2008 7:00 PM
I couldn't get this to work with a WCF service, but I did get it to work with an asp.net 2.0 web service. I posted about it here: http://www.iphonedevsdk.com/forum/iphone-sdk-development/2841-resolved-how-call-soap-service-3.html#post21247

-nait
Comment By At 10/1/2008 6:25 PM
Thanks for the comments. We have all of our WCF services integrated with the iPhone. All of our services are using SSL and are secured with id and password. The above pattern is what we followed. What trouble were you having?
Comment By At 10/1/2008 7:33 PM
When I opened up the SvcTraceViewer, the SOAP Envelope Header-Security section was much more complex than the one you have displayed above, including what looked like guids/hashcodes so the server could know which client it was talking to (I assume, as that many unique strings shouldn't be needed for a simple http request). I guess what I want to know is how you got the header the service was expecting to be so simple with just the UserNameToken. This would be ideal for what I'm trying to do. As it is, I'm using an asp.net 2.0 web service over SSL and passing the username and password as part of the body to each function call. Thanks again.
- nait
Comment By At 10/2/2008 1:12 PM
After testing this 1 by 1. You do not need to provide all that detail. Microsoft will provide defaults if you don't provide them. The only ones that are required are the ones listed above in the example. The GUIDs and Timestamps are not needed. You only need:
Comment By At 10/2/2008 1:33 PM
I'm sorry, but I think the end of your post there didn't go through. I realize you are doing me a huge favor here, so I'm not trying to be impatient. Thanks for all of your help so far.
- nait
Comment By At 10/6/2008 4:01 PM
After the parse....how do you format the resulting xml? Can you show an example of that please?
Comment By At 11/21/2008 11:14 PM
How do you deal with the quotes on your strings? Do you get errors when compiling the 'appendString' messages when there are quotes in the XML?
Comment By At 1/12/2009 3:55 PM
Thanks for this article.
I have one problem please help

I want to get result from a web service which accepts a xml as input but when the request is made as above it is not returning any thing. Can u tell me how to pass XML to web service.
Thanks in advance
Comment By At 4/22/2009 8:00 AM
Nice introduction! Being a freshman in blogging, I read lots of literature as far as what and how to write. http://www.ebook-search-queen.com/ helps me with the books, and your post with inspiration! Thanks!
Comment By At 6/26/2009 8:16 AM
I have a WCF webservice (.svc) that returns me JSON object from various methods inside the .svc. Is it possible to call these methods in my iPhone App? I want to keep them as json & use it in my iPhone App.
Comment By At 8/3/2009 1:27 PM
For JSON parsing, try the open-source iphone JSON project on google code.

-Shawn Arney
Comment By At 10/6/2009 5:01 PM
It'd be nice to have a WCF Test Client for the iPhone, I'm betting you'd sell quite a few of those if you built it....
Comment By At 10/31/2009 6:50 AM
Can you please post entire codebase of .NET and Objective C code for the above project ? I am getting exception while trying to ping my services by following your instructions.

Part of the exception details are... 'Check that sender and receiver have the same contract and the same binding (including security requirements, e.g. Message, Transport, None). '

And also getting warnings like... 'NSURLRequest' may not respond to '+setAllowsAnyHTTPSCertificate:forHost:'

Please help me to resolve above said exceptions and consume WCF services from an iPhone application.
Comment By At 10/28/2010 7:43 AM
if theres any way i can do this without a button_click functions?? lets say everytime i start up the app?
Comment By At 12/7/2010 3:18 PM
This has made my day. I wish all potsnigs were this good.
Comment By At 5/18/2011 6:44 PM
Comments are locked for this post.

 
 
 Copyright © 1999-2009 Digital Infuzion, Inc. All Rights Reserved.