Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
Qualiture
Active Contributor

[Updated February 28, 2017: Rebranded "SAP HANA Cloud Platform" to "SAP Cloud Platform"]



Purpose of this blog


There are various blogs and resources describing how to build iOS applications with the Swift language, how to implement SAP Cloud Platform mobile services, and using OData REST services. However, after some research, I found no blog describing the full development process end-to-end using all three topics. This blog tries to fill that gap.



Does this blog already cover the announced SAP Cloud Platform SDK for iOS?


No.



Both SAP Fiori for iOS design components as well as the SAP Cloud Platform SDK for iOS are still in development and not available yet.


This blog covers development using 'current' available technologies, and therefore has one significant restriction:




  • True OData support is not covered. I expect this to be available in the upcoming SAP Cloud Platform SDK for iOS.


Requirements


In order to create the application yourself using this blog, you need at least the following:




  • Mac computer running macOS 10.11.4 (El Capitan) or higher

  • Xcode 8 development environment (Swift 3.0 is included in Xcode 😎

  • Trial account on SAP Cloud Platform


Part 1 - Setting up SAP Cloud Platform, mobile services



  1. Login to you SAP Cloud Platform account cockpithttps://account.hanatrial.ondemand.com/cockpit/

  2. Navigate to Services > Mobile Services and click the Development & Operations tile:

  3. If it's not yet enabled, enable it.

  4. Click the link Go to Service.

  5. The SAP Cloud Platform, mobile services administration cockpit will now open

  6. Navigate to Applications and click the Create Application button

  7. Specify a unique Application ID (you will need these later) and Name.
    Tick the Ignore Case for User Name checkbox
    Set Security Configuration to None

  8. Click the Save button to finalise.

  9. In the now created Application, navigate to Back End

  10. For the purpose of this blog, we connect to the freely accessible and well-known Northwind OData service.

    1. Set the Back-End URL to http://services.odata.org/V4/Northwind/Northwind.svc/

    2. Set Proxy Type to Internet

    3. Tick Propagate Username and Use Default JDK Trust Store

    4. Set SSO Mechanism to No Authentication




  11. The Application is now configured on SAP Cloud Platform Mobile Services. You can check the connection to the back-end URL by navigating to the Application Overview page, and click the Ping button:


Part 2 - Create the iOS application skeleton



  1. Start Xcode on your Mac, and choose Create a new Xcode Project

  2. Select Single View Application and click Next

  3. Specify a Product Name (this is the name of your application) and Organization Identifier (i.e. package name).
    Make sure Language is set to Swift, and Devices is set to Universal (this ensures your app will run on both iPhone and iPad, on all screen sizes)

  4. Specify a location to store your app.
    Out of habit, I always tick the Create Git Repository for version control; make it your habit too 😉

  5. Your single view iOS app is now created, and ready for development


Part 3 - Create the Table View layout


Most, if not all, application layout design is done via the app's Storyboard. A Storyboard is the visual representation of your app's user interface, screen, content, and navigation flow.




  1. In the Project Navigator (the 'folder' icon in the left pane), select Main.storyboard

  2. Select the View Controller in the Storyboard, and delete it.

  3. From the Object Library in the bottom of the right pane, drag a Table View Controller to the Storyboard. Your Storyboard should now resemble the following:

  4. We now have a view, but not the actual implementing controller behind it. From the menu, select File > New > File... and select Cocoa Touch Class.

  5. Set Class to Categories, and Subclass of to UITableViewController. You will notice Class is now renamed to CategoriesTableViewController. Also, make sure Language is set to Swift


  6. Click Next, make sure it is saved inside your project, and click Create.

  7. In the Storyboard, select the Table View Controller, and from the Identity Inspector at the top of the right pane, set the Custom Class to the just created CategoriesTableViewController class.

  8. With the Table View Controller selected, switch from the Identity Inspector to the Attributes Inspector and tick the Is Initial View Controller checkbox. This will make the view the starting point for the application's Storyboard.

  9. For convenience, we also create a custom controller class for the table's cells. From the menu, select File > New > File... and select Cocoa Touch Class.

  10. Set Class to Categories, and Subclass of to UITableViewCell. You will notice Class is now renamed to CategoriesTableViewCell. Also, make sure Language is set to Swift


  11. Click Next, make sure it is saved inside your project, and click Create.

  12. In the Storyboard, select the Table View Cell, and from the Identity Inspector at the top of the right pane, set the Custom Class to the just created CategoriesTableViewCell class.


Part 4 - Create the Table View Cell layout


Now, we will design the table's cell layout. For the purpose of this blog, we are going to query the Northwind's Category entityset, and we want to display both the Category Name and Description in the cell. To accommodate these details, we need to adjust the table cell height, and add the controls to the table cell which will display these properties.




  1. Increase the Row Height of both the Table View:

    as well as the Table View Cell:

    to 80 pixels.

  2. Set the Identifier of the Table View Cell to CategoryCell:

    This identifier is needed later in our code.

  3. From the Object Library, drag two Label controls to the Table View Cell, one on top of the other.
    Make them span the whole width of the table cell margin guides, and increase the top label font size to 24.0.
    Your Table View Cell should now resemble the following:

  4. Click the Assistant Editor button in the top-right corner of Xcode's window. The Assistant Editor for the Storyboard now opens on the right. Switch the Assistant Editor from Automatic to Manual, and point to the CategoriesTableViewCell.swift class

  5. In the Storyboard, select the top label in the table cell, and Ctrl-drag it just below the line class CategoriesTableViewCell: UITableViewCell {

  6. In the dialog, set Name to nameLabel and click Connect.
    A line of code should now be added:
    @IBOutlet weak var nameLabel: UILabel!

  7. Do the same for the lower label; set Name to descriptionLabel.

  8. Your CategoriesTableViewCell.swift class should now look like this:
    //
    // CategoriesTableViewCell.swift
    // HCPmsDemo
    //
    // Created by Robin van het Hof on 09/11/2016.
    // Copyright © 2016 Robin van het Hof. All rights reserved.
    //

    import UIKit

    class CategoriesTableViewCell: UITableViewCell {

    // MARK: - Properties

    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var descriptionLabel: UILabel!

    override func awakeFromNib() {
    super.awakeFromNib()
    // Initialization code
    }

    override func setSelected(_ selected: Bool, animated: Bool) {
    super.setSelected(selected, animated: animated)

    // Configure the view for the selected state
    }

    }



Part 5 - Connect to SAP Cloud Platform Mobile Services datasource


If you made it this far, and click the Run button, your app should run successfully, but doesn't show anything:

Of course, this makes perfect sense, since we didn't create any connectivity to our datasource on SAP Cloud Platform Mobile Services.


In this part of the blog, we create the connection using an open-source project called Alamofire. (We could do it using the standard networking libraries in Swift, but that would require a bit more coding. Besides, as developers, we don't want to re-invent the wheel)


To include Alamofire to our project, we use Cocoapods, a dependency manager for Swift / Cocoa projects (think of Cocoapods as the Cocoa equivalent to Java's Maven and Javascript's npm, Browserify or Bower)




  1. If you haven't already installed Cocoapods, simply run the following command from Terminal:
    sudo gem install cocoapods


  2. Close Xcode using Cmd-Q. It is important to have it fully closed before continuing!

  3. In Terminal, change directory to your project root directory

  4. Run the command:
    pod init


  5. This command will create a file Podfile. Open this file with a text editor (Sublime, Atom, any text editor without formatting)

  6. Uncomment the line:
    platform :ios, '9.0'


  7. Under
    # Pods for HCPTableApp

    add the line
    pod 'Alamofire', '4.0.1'

    Your Podfile should now resemble the following:
    # Uncomment the next line to define a global platform for your project
    platform :ios, '9.0'

    target 'HCPmsDemo' do
    # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
    use_frameworks!

    # Pods for HCPmsDemo
    pod 'Alamofire', '4.0.1'

    end


  8. In Terminal, run the command
    pod install


  9. It will now add a dependency to your project for Alamofire. You will also note it will add (among other files) a <Project Name>.xcworkspace file. From now on, always use the workspace file instead of the project file to edit your project!


  10. In Finder, double-click the file <Project Name>.xcworkspace to open your Xcode project workspace.

  11. Edit the file CategoriesTableViewController.swift and just below the line import UIKit, add a line import Alamofire

  12. It will now show a build error:

    Simply press Cmd-B to resolve the dependencies, and the error should be removed.

  13. Since we will consume the Category entityset of the OData service exposed through SAP Cloud Platform Mobile Services, it is convenient to create a simple class to hold this data.
    From the menu, choose File > New > File... and select Swift File

  14. Save it as Category, and click Create

  15. Open the just created Category.swift file, and replace the line
    import Foundation

    with
    import UIKit


  16. Add two properties name and description, and implement the required init function. Your code should be like this:
    import UIKit

    class Category {

    // MARK: - Properties

    var name: String
    var description: String

    // MARK: - Initialization

    init(name: String, description: String) {
    self.name = name
    self.description = description
    }
    }


  17. Switch back to the file CategoriesTableViewController.swift, and just below the line class CategoriesTableViewController: UITableViewController { add the line:
    var categories = [Category]()

    This will hold the returned data from the OData service into an array of Category objects (the class we created earlier)

  18. Change the following function to return the size of the just defined categories array:
    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return categories.count
    }


  19. Change the following function to return 1 section instead of 0:
    override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
    }


  20. Change the following function to return the CategoriesTableViewCell, and set the cell's control text properties to the equivalent Category properties:
    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath) as! CategoriesTableViewCell

    let category = categories[indexPath.row]

    cell.nameLabel.text = category.name
    cell.descriptionLabel.text = category.description

    return cell
    }


  21. Now, create an empty function loadCategories, and call that function in the overridden function viewDidLoad(). Your code should now look like this:
    import UIKit
    import Alamofire

    class CategoriesTableViewController: UITableViewController {

    var categories = [Category]()

    override func viewDidLoad() {
    super.viewDidLoad()

    loadCategories()
    }

    func loadCategories() {
    //TODO: Implement!
    }

    override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
    }

    // MARK: - Table view data source

    override func numberOfSections(in tableView: UITableView) -> Int {
    return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return categories.count
    }

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "CategoryCell", for: indexPath) as! CategoriesTableViewCell

    let category = categories[indexPath.row]

    cell.nameLabel.text = category.name
    cell.descriptionLabel.text = category.description

    return cell
    }

    }


  22. The last step is to implement the just created function loadCategories.
    I won't go into too much details here (it is described in detail here), but what needs to happen is the following:

    1. The unique Device ID is needed to register on Cloud Platform Mobile Services

    2. As extra properties, we also capture the Device Type and Name

    3. HTTP Headers are defined:

      1. Authorization header with basic authentication

      2. Content-Type is set to JSON

      3. X-SMP-APPCID is required, and should contain the Device ID



    4. Parameters array is defined for the Device Type and Name

    5. A POST request will be performed to set up the connection and register the device to Cloud Platform Mobile Services, using the aforementioned HTTP Headers. The Parameters array is sent as payload in the request.

    6. If connected successfully, we can now perform the actual GET request to retrieve the Categories entityset.

    7. The categories array will be filled with the retrieved Category objects, and the table is reloaded to display the retrieved data.



  23. The complete loadCategories function implementation is:
    func loadCategories() {
    let deviceId = UIDevice.current.identifierForVendor!.uuidString
    let deviceType = UIDevice.current.model
    let deviceName = UIDevice.current.name

    let urlConnection = "https://hcpms-s0007138856trial.hanatrial.ondemand.com/odata/applications/latest/nl.qualiture.swift.demo/Connections"
    let urlService = "https://hcpms-s0007138856trial.hanatrial.ondemand.com/nl.qualiture.swift.demo"

    let httpHeaders : HTTPHeaders = [
    "Authorization" : "Basic czA<...replace with your own string...>LUy4=",
    "Content-Type" : "application/json",
    "X-SMP-APPCID" : deviceId
    ]

    let parameters: Parameters = [
    "DeviceType": deviceType,
    "UserName" : deviceName
    ]

    Alamofire.request(urlConnection, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: httpHeaders).responseString { response in

    let statusCode = response.response?.statusCode

    Alamofire.request(urlService + "/Categories", method: .get, headers: httpHeaders).responseJSON { (response) -> Void in

    let jsonValue = response.result.value as! NSDictionary

    if let categoryArray = jsonValue["value"] as? [NSDictionary] {
    for categoryObj in categoryArray {
    let name: String = categoryObj.value(forKey: "CategoryName") as! String
    let description: String = categoryObj.value(forKey: "Description") as! String

    let category: Category = Category(name: name, description: description)

    self.categories.append(category)

    self.tableView.reloadData()
    }
    }
    }
    }
    }



Part 6 - Run the application



  1. If you now run the application, everything should now go as planned, and you will be presented the following:

  2. Whoooohoo, data is retrieved!! However, as you see, the table doesn't have a nice header, and the top table cell is slightly under the phone's top bar. To solve this, select the CategoriesTableViewController and from the top menu, select Editor > Embed in > Navigation Controller. Your storyboard should now look like the following:

  3. Select the Navigation Item, and change the Title to Categories.

  4. Run the application again, it it should now look a lot nicer:

  5. If you now go back to SAP Cloud Platform Mobile Services administration cockpit, and navigate to Registrations and Users, you will now see the device properly registered to the defined Application ID on SAP Cloud Platform Mobile Services:


Final words


I hope this blog would enable you to jumpstart iOS Swift development using SAP Cloud Platform. There is a lot more to discover -- for instance, next to the Single View Application we used, there is also a Master Detail Application template -- and hopefully you will be up to speed once the SAP Fiori for iOS or SAP Cloud Platform SDK for iOS are available in 2017.


Until then... happy coding!

14 Comments
Labels in this area