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

 
DIFZ Blogs
 
DIFZ Blogs : DIFZ Blogs
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];
}


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