Developer

Working with Data

Feed the label with data from an OData stream.

Context

So far the tutorial have assigned a static text to the label. This task feeds the label with data coming from an OData stream, fetched via the Internet from a server.

Procedure

  1. First, add these libraries, which provide connectivity, OData parsing and caching, user authentication, and so on:
    • MAF
      • libMAFSDMDataSourceAdapter.a
      • libSDMDataSource.a
    • SUP/SDM libs:
      • libConnectivity.a
      • libParser.a
      • libRequest.a
      • libPerformanceLib.a
      • libE2ETrace.a
    • System
      • CFNetwork.framework
      • MessageUI.framework
      • SystemConfiguration.framework
      • MobileCoreServices.framework
  2. Place the public headers and the library files in your existing folder structure.
    The header and the library paths are already set up and the search is recursive, therefore you need not update your build settings. However, do not forget to add the libraries to your project as described in Adding Binary Dependencies.
    This screenshot shows the complete list of libraries and frameworks:
    List of Libraries and Frameworks

    The Extensibility Framework includes two libraries for working with OData:
    • MAFSDMDataSourceAdapter – a data source adapter that converts between generic data format and OData.
    • SDMDataSource – a facade on top of the SAP Mobile Platform/SDM libraries that acts as a wrapper, simplifying the sequence of fetch and parse calls, and providing additional features, such as caching and demo data support.

    This example uses the NorthWind endpoint: http://services.odata.org/Northwind/Northwind.svc/

    The OData feed contains a set of collections; this example uses only the "Customers" collection.

  3. At the beginning of the layout_phone.xml, add a binding definition that describes the business objects that feed data to the tile's UI. The boType must match the collection name for OData:
    <?xml version="1.0" encoding="UTF-8"?>
    <MAF xmlns="http://schemas.sap.com/maf/2012/cfg">
    
        <!-- Define a binding ID - you can name it as you wish - it's recommended to keep it in synch with the collection name -->
        <!-- We are going to use this binding ID to refer to this collection throughout the XML -->
        <Binding bindingId="CustomersCollection" type="collection">
            <!-- the boType must match the collection href in the OData feed:
            
            ?xml version="1.0" encoding="utf-8" standalone="yes"?>
            <service xml:base="http://services.odata.org/Northwind/Northwind.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns="http://www.w3.org/2007/app">
                <workspace>
                
                ...skipped for brevity
             
                    <collection href="Customers">
                        <atom:title>Customers</atom:title>
             -->
            <P pid="boType" value="Customers"></P>
        </Binding>
    
        <!-- Define a "tile" - it translates to UIViewController -->
        <Tile tileId="First Extensible Screen">
    	<!-- skipped for brevity -->
    This example displays the customer's name from the OData entry identified by the CompanyName property, which is part of the Customers collection. Next, add the binding reference to the tile:
        <!-- Define a "tile" - it translates to UIViewController -->
        <Tile tileId="First Extensible Screen">
            <!-- This is our root view -->
            <P pid="isRoot" value="true"/>
            
            <!-- Reference to the data binding which provides data for this tile -->
            <!-- Note that we are using the bindingId here -->
            <BindingRef ref="CustomersCollection"/>

    The label's text is fed with data coming from the binding:

    <!-- Define a label which shows the customer company name -->
                <UIElement type="label">
                    <!-- The label text is showing real data coming from the OData feed http://services.odata.org/Northwind/Northwind.svc/Customers:
                    
                     <link rel="self" title="Customers" href="Customers" />
                     ...details skipped for brevity
                     
                     <entry>
                     <category term="NorthwindModel.Customer" scheme="http://schemas.microsoft.com/ado/2007/08/dataservices/scheme" />
                     <content type="application/xml">
                     <m:properties>
                     <d:CustomerID m:type="Edm.String">ALFKI</d:CustomerID>
                     <d:CompanyName m:type="Edm.String">Alfreds Futterkiste</d:CompanyName>
                     <d:ContactName m:type="Edm.String">Maria Anders</d:ContactName>
                     <d:ContactTitle m:type="Edm.String">Sales Representative</d:ContactTitle>
                     <d:Address m:type="Edm.String">Obere Str. 57</d:Address>
                     <d:City m:type="Edm.String">Berlin</d:City>
                     <d:Region m:type="Edm.String" m:null="true" />
                     <d:PostalCode m:type="Edm.String">12209</d:PostalCode>
                     <d:Country m:type="Edm.String">Germany</d:Country>
                     <d:Phone m:type="Edm.String">030-0074321</d:Phone>
                     <d:Fax m:type="Edm.String">030-0076545</d:Fax>
                     </m:properties>
                     </content>
                     </entry>

    Note that the company name is retrieved from the first entry.

  4. You can visualize the remaining entries using a ListContainer (UITableViewController), see Managing Lists.
                    -->
                    <P pid="text" value="Company: {$CustomersCollection.CompanyName}"></P>
                    <P pid="halign" value="center"></P>
                    <P pid="margin_top" value="20pt"></P>
                    <P pid="style" value="CustomLabel"></P>
                </UIElement>

    The {$CustomersCollection.CompanyName} syntax refers to the company name. Curly brackets indicate that the content must be evaluated by the Extensibility Framework. The $ sign denotes a binding (or expression), followed by the collection and the entry name separated by a period.

    The layout_phone.xml you have now:

    <?xml version="1.0" encoding="UTF-8"?>
    <MAF xmlns="http://schemas.sap.com/maf/2012/cfg">
    
        <!-- Define a binding ID - you can name it as you wish - it's recommended to keep it in synch with the collection name -->
        <!-- We are going to use this binding ID to refer to this collection throughout the XML -->
        <Binding bindingId="CustomersCollection" type="collection">
            <!-- the boType must match the collection href in the OData feed:
            
            ?xml version="1.0" encoding="utf-8" standalone="yes"?>
            <service xml:base="http://services.odata.org/Northwind/Northwind.svc/" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:app="http://www.w3.org/2007/app" xmlns="http://www.w3.org/2007/app">
                <workspace>
                
                ...skipped for brevity
             
                    <collection href="Customers">
                        <atom:title>Customers</atom:title>
             -->
            <P pid="boType" value="Customers"></P>
        </Binding>
    
        <!-- Define a "tile" - it translates to UIViewController -->
        <Tile tileId="First Extensible Screen">
            <!-- This is our root view -->
            <P pid="isRoot" value="true"/>
            
            <!-- Reference to the data binding which provides data for this tile -->
            <!-- Note that we are using the bindingId here -->
            <BindingRef ref="CustomersCollection"/>
            <!-- Define the layout container. All UI elements included in a layout container will be auto-arranged -->
            <LinearContainer layout="vertical">
                <!-- Define our label -->
                <UIElement type="label">
                    <!-- Assign a static text to be displayed -->
                    <P pid="text" value="Hello World! This is Extensibility!"></P>
                    <P pid="halign" value="center"></P>
                    <P pid="margin_top" value="20pt"></P>
                    <P pid="style" value="CustomLabel"></P>
                </UIElement>
                
                <!-- Define a label which shows the customer company name -->
                <UIElement type="label">
                    <!-- The label text is showing real data coming from the OData feed http://services.odata.org/Northwind/Northwind.svc/Customers  
                     ...details skipped for brevity  
                    Note that the company name is retrieved from the first entry. 
                    The remaining entries could be visualized as well using a ListContainer (UITableViewController), see Chapter 4.2.6 'Managing Lists'
                    -->
                    <P pid="text" value="Company: {$CustomersCollection.CompanyName}"></P>
                    <P pid="halign" value="center"></P>
                    <P pid="margin_top" value="20pt"></P>
                    <P pid="style" value="CustomLabel"></P>
                </UIElement>   
            </LinearContainer>
        </Tile>
    </MAF>
  5. Now you have to do some coding. Add a strong property for the SDMDataSource instance, to prevent it from releasing prematurely when the SDMDataSource loadModel async API is called. Modify the AppDelegate.h:
    #import <UIKit/UIKit.h>
    
    @class ViewController;
    // SDMDataSource forward declaration
    @class SDMDataSource;
    
    @interface AppDelegate : UIResponder <UIApplicationDelegate>
    
    @property (strong, nonatomic) UIWindow* window;
    
    // our data source facade
    @property (strong, nonatomic) SDMDataSource* dataSource;
    
    @end
    
    Add these headers to your AppDelegate.m: 
    
    #import "MAFSDMDataSourceAdapter.h"
    #import "SDMDataSource.h"
  6. Enhance the AppDelegate didFinishLaunchingWithOptions:
    • Configure the endpoint and set user credentials required for authentication on the server side
    • Load the OData Service Document and metadata via SDMDataSource loadModel API
    • Instantiate a data source adapter, and pass it to the Extensibility Framework loader API
    These changes are reflected in the modified didFinishLaunchingWithOptions: implementation:
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    
        // set the path for our custom styling XML
        [MAFCore sharedInstance].applicationSpecificStylePath = [[NSBundle mainBundle] pathForResource:@"CustomStyles" ofType:@"xml"];
    
        // Configure the data source
        // Set the endpoint URL
        [SDMDataSource setBaseURL:@" http://services.odata.org/Northwind/Northwind.svc/"];
        
        // If the server requires authentication, set the username and the password 
        // Commented out since the server used in this example does not require authentication
        // [SDMDataSource setUsername:@"username"];
        // [SDMDataSource setPassword:@"password"];
    
        // Set request type to plain HTTP
        [SDMDataSource setRequestType:SDMHTTPRequestType];    
        
        self.dataSource = [[SDMDataSource alloc] init];
        // establish connection with the OData Server, fetch and parse the OData Service Doc and metadata - see odata.org for details
        [self.dataSource loadModel:^(NSError* error) {
            // OData SVC and metadata fetched and parsed
            if( !error )
            {
                // instantiate the data source adapter
                MAFSDMDataSourceAdapter* datasourceAdapter = [[MAFSDMDataSourceAdapter alloc] init];
    
                // Start the Extensibility engine
                [[MAFCore sharedInstance] loadWithWindow:self.window
                                       datasourceAdapter:datasourceAdapter
                                      andCompletionBlock:^(NSError* error)
                 {
                     // Extensibility engine started successfully
                     if( !error )
                     {
                         // Display an alert view
                         [[[UIAlertView alloc] initWithTitle:@"Success!"
                                                     message:@"Extensibility is Up and Running!"
                                                    delegate:nil
                                           cancelButtonTitle:@"Dismiss"
                                           otherButtonTitles:nil, nil] show];
                         NSLog( @"Extensibility is Up and Running!" );
                     }
                     // Extensibility engine failed to start!
                     else
                     {
                         NSLog( @"Extensibility load failure. Details:%@", error.description );
                     }
                 }];
            }
            else
            {
                NSLog( @"Could not load OData model. Details: %@", error.description );
            }
        }];
        
        return YES;
    }

Results

This example uses another API to instantiate and load the extensibility engine, and this API requires a valid data source adapter instance. This approach allows passing in a custom data source adapter, but the default implementation works correctly for OData.

This setup is all you need to build an extensible app that does not require custom hooks or mixing native UIs with UIs that are defined via metadata. From this point on, you need only enhance the layout_phone.xml to define new tiles, UI elements, bindings and so on.

Running the app shows this screen:

Working With Data

Loading time increases slightly because the app is fetching data from a remote server, however, depending on your network speed and OData endpoint load, the increase should not be more than a couple of seconds.