Skip to Content

Data Visualisation with SAP Cloud Platform SDK for iOS

Previous

Data Visualisation with SAP Cloud Platform SDK for iOS

By Robin van het Hof

Build an iOS app with the SAP Cloud Platform SDK for iOS and use the new SAP Fiori for iOS Chart Floorplan API's to visualise entity values of an OData service.

How-To Details

As of version 2.1, the SAP Cloud Platform SDK for iOS now incorporates a Chart Floor plan for data visualisation.

In this tutorial, you will use the Sample OData service to visualise stock item quantities using a bar chart.

How-To Details
Step 1: Create Xcode project with SDK Assistant

On your local machine, navigate to your ./<SAP Cloud Platform SDK for iOS> folder. Double-click the SAP Cloud Platform SDK for iOS Assistant icon to start the application.

Click the Plus button on the top-right of the SDK Assistant. The first page of the Xcode Project generation wizard lets you define the Project Properties.

Enter the following details:

Field Value
Product Name DataViz
Author <your name>
Organization Name <your company name>
Organization Identifier com.sap.tutorials.demoapp
Destination <choose a local destination>
Create Xcode project with SDK Assistant

Click Next to advance to the SAP Cloud Platform mobile service for development and operations Configuration step.

In the SAP Cloud Platform mobile service for development and operations Configuration page, select the Sample tab button.

Create Xcode project with SDK Assistant

The Sample Application Identifier and Sample Application Name are now pre-filled with the Bundle Identifier value from the previous page.

Click Next to advance to the OData Services step.

In the OData Services page, the Sample OData service connection you have specified in the previous wizard step is displayed:

Create Xcode project with SDK Assistant

Click Next to advance to the Optional Features step.

In the Optional Features page, you have the option to generate a Master-Detail Application, enable logging and log uploads, and enable remote notifications. Since you’re using the Sample OData service, all settings are enabled but greyed out.

Create Xcode project with SDK Assistant

After you have clicked Finish in the previous step, the SDK Assistant now loads the OData service’s metadata. This metadata describes the data model, and can be accessed via <service URL>$metadata. For your service, the metadata URL would be https://hcpms-<your account>trial.hanatrial.ondemand.com/SampleServices/ESPM.svc/$metadata

After the SDK Assistant has finished, Xcode will launch and open the just generated DataViz project.

Create Xcode project with SDK Assistant
Please log in to access this content.
Step 2: Test the generated Xcode project

In Xcode, assign the appropriate development account to the project’s Team property in the General > Signing panel, and then build and run the app in the simulator.

Test the generated Xcode project

Once the app has started, dismiss the push notifications message by clicking Allow.

The on-boarding landing page is now displayed:

Test the generated Xcode project

Click the blue Start button, and in the SAML login screen, provide your SAP Cloud Platform credentials:

Test the generated Xcode project

After you click Log on, the EULA screen is displayed:

Test the generated Xcode project

Click Agree, and the single entity collection is now shown:

Test the generated Xcode project
Please log in to access this content.
Step 3: Create View Controller

In the Project navigator, navigate to the DataViz > ViewControllers > MyPrefixStock folder. Right-click this folder, and from the context menu, select New File…

In the dialog, select Swift File:

Create View Controller

Click Next to continue. In the next page, save the file as StockChart

Create View Controller

Click Create to finalize the wizard. The new file will now open.

Please log in to access this content.
Step 4: Implement View Controller

First, add the necessary import statements:

import UIKit
import SAPFoundation
import SAPOData
import SAPFiori
import SAPCommon

Then, create an empty class:

class StockChartViewController: FUIChartFloorplanViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
    }

}

Since the idea is to mimic the behavior of the StockMasterViewController file, but display the data in a chart instead of a table view, you need to reuse some functionality from this file.

Add the following private fields:

private let appDelegate = UIApplication.shared.delegate as! AppDelegate
private var myServiceClass: MyServiceClass<OnlineODataProvider> {
    return self.appDelegate.myServiceClass
}

private var entities: [MyPrefixStock] = [MyPrefixStock]( )
private let logger = Logger.shared(named: "MyPrefixStockMasterViewConterollerLogger")
private let okTitle = NSLocalizedString("keyOkButtonTitle",
                                        value: "OK",
                                        comment: "XBUT: Title of OK button.")

These will hold, among others, references to the appDelegate file, the OData service class, and stores the retrieved Stock entities in the entities field.

To load the Stock data, add the following methods (which are derived from the existing master view controller file) :

private func updateChartView() {
    let oq = OperationQueue()
    oq.addOperation({
        self.loadData {
        }
    })
}

private func loadData(completionHandler: @escaping () -> Void) {
    self.requestEntities { error in
        defer {
            completionHandler()
        }
        if let error = error {
            let alertController = UIAlertController(title: NSLocalizedString("keyErrorLoadingData", value: "Loading data failed!", comment: "XTIT: Title of loading data error pop up."), message: error.localizedDescription, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: self.okTitle, style: .default))
            OperationQueue.main.addOperation({
                // Present the alertController
                self.present(alertController, animated: true)
            })
            self.logger.error("Could not update table. Error: \(error)", error: error)
            return
        }
        OperationQueue.main.addOperation({
            self.renderChart()
            self.logger.info("Chart updated successfully!")
        })
    }
}

func requestEntities(completionHandler: @escaping (Error?) -> Void) {
    // Only request the first 20 values. If you want to modify the requested entities, you can do it here.
    let query = DataQuery().selectAll().top(20)
    self.myServiceClass.fetchStock(matching: query) { stock, error in
        guard let stock = stock else {
            completionHandler(error!)
            return
        }
        self.entities = stock
        completionHandler(nil)
    }
}

This will give an error, stating the class has no member renderChart. This method will be implemented in the next step

Please log in to access this content.
Step 5: Implement display of chart

Add the following 3 methods:

private func initChart() {
    title = "Stock Overview"

    titleText.text         = "Total Stock available"
    status.text            = "Click chart for details"
    categoryAxisTitle.text = "Quantity"
    valuesAxisTitle.text   = "Stock Item"

    headerView.dataSource = self

    renderChartSummaryItem(values: [""])
}

private func renderChart() {

    chartView.chartType         = .bar
    chartView.numberOfGridlines = 4
    chartView.dataSource        = self
    headerView.dataSource       = self

    let values: [Double] = {
        var values: [Double] = []
        for series in chartView.series {
            let categoriesUpperBound = series.numberOfValues - 1
            if let valuesInSeries = series.valuesInCategoryRange((0...categoriesUpperBound), dimension: 0) {
                values.append(valuesInSeries.flatMap({ $0 }).reduce(0.0, +))
            }
        }
        return values
    }()

    renderChartSummaryItem(values: values.map { $0.description })
}

private func renderChartSummaryItem(values: [String]) {
    let item = FUIChartSummaryItem()

    item.categoryIndex = -1
    item.isEnabled = false
    item.isPreservingTrendHeight = false

    item.valuesText = values
    item.title.text = "Stock Details"

    headerView.addItem(item)
}

The first method sets the ‘static’ details for the chart, and will be called from the viewDidLoad() method.

The second method is called once the OData service has returned data (and was missing in the previous step)

The third method is called from the previous two methods, and will display a summary of the data when clicking on the chart.

You will also see a couple of other errors. This is because the class needs a couple of delegates which aren’t referenced yet. This will be corrected in the next step.

Please log in to access this content.
Step 6: Add delegates

Add the following two extensions:

extension StockChartViewController: FUIChartSummaryDataSource {

    func chartView(_ chartView: FUIChartView, summaryItemForCategory categoryIndex: Int) -> FUIChartSummaryItem? {

        let item = FUIChartSummaryItem()

        item.categoryIndex = categoryIndex
        item.isEnabled = true
        item.isPreservingTrendHeight = false

        let values: [Double] = {
            var values: [Double] = []
            for series in chartView.series {
                values.append(series.valueForCategory(categoryIndex, dimension: 0)!)
            }
            return values
        }()

        item.valuesText = values.map { $0.description }
        item.title.text = chartCategoryTitles()[categoryIndex]

        return item
    }
}

extension StockChartViewController: FUIChartViewDataSource {

    func chartSeriesTitles() -> [String] {
        return ["Stock"]
    }

    func chartCategoryTitles() -> [String] {
        return entities.map{$0.productID!}
    }

    func chartData() -> [[Double]] {
        return [entities.map{$0.quantity!.doubleValue()} /*, entities.map{$0.minStock!.doubleValue()} */ ]
    }

    func numberOfSeries(in: FUIChartView) -> Int {
        return chartData().count
    }

    func chartView(_ chartView: FUIChartView, numberOfValuesInSeries seriesIndex: Int) -> Int {
        return chartData()[seriesIndex].count
    }

    func chartView(_ chartView: FUIChartView, valueForSeries series: Int, category categoryIndex: Int, dimension dimensionIndex: Int) -> Double? {
        return chartData()[series][categoryIndex]
    }

    func chartView(_ chartView: FUIChartView, formattedStringForValue value: Double, axis: FUIChartAxisId) -> String? {
        return value.description
    }

    func chartView(_ chartView: FUIChartView, titleForCategory categoryIndex: Int, inSeries seriesIndex: Int) -> String? {
        return chartCategoryTitles()[categoryIndex]
    }

}

The first delegate is called when an item on the cart is clicked.

The second delegate populates the chart with the OData entities values (see the mapped values in chartCategoryTitles() and chartData() methods).

Which bound property in the chart holds the amount of stock?
×
Step 7: Modify viewDidLoad method

Add the following method calls in the viewDidLoad() method:

initChart()
updateChartView()
Please log in to access this content.
Step 8: Bind the View Controller to the View

The final step is to bind the view controller to a view.

In the Project navigator, navigate to DataViz > ViewControllers > MyPrefixStock and open MyPrefixStock.storyboard.

From the Object library, drag a View Controller to the storyboard.

In the Identity inspector, set the Custom Class to StockChartViewController.

Set its storyboard ID to StockDataViz.

Run the application

In the Attributes inspector, set the view to be the Initial View Controller.

You may remove the other views in the storyboard.

Navigate up to DataViz > ViewControllers and open file CollectionsViewController.swift.

Locate method collectionSelected and change the switch for .myPrefixStock to load the new view with ID StockDataViz:

case .myPrefixStock:
    let myPrefixStockStoryBoard = UIStoryboard(name: "MyPrefixStock", bundle: nil)
    masterViewController = myPrefixStockStoryBoard.instantiateViewController(withIdentifier: "StockDataViz")
    masterViewController.navigationItem.title = "Stock"
Please log in to access this content.
Step 9: Run the application

Build and run the application.

Once loaded, select the Stock collection. It should now display the stock quantities in a bar graph:

Run the application

Select one of the chart items. It should now display the summary info at the top:

Run the application
Please log in to access this content.

Updated 07/11/2018

Time to Complete

20 Min

Beginner

Prerequisites

Next
Back to top