SAP

Manage my Account SAP Devs YouTube ↗ Learnings ↗ Community ↗ Provide Feedback ↗
Logout
⤢ Open full site

Customize the Overview View Controller to Display Customers and Products

Use the Swift programming language and the SAP BTP SDK for iOS to implement data loading from the sample data service and display the results in a Table View in your app. Also add Table View Section Headers and Footers to give the data more structure and a nice clean UI.

Overview

You will learn

  • ✔How to implement a Fiori Object Table View Cell
  • ✔How to implement Table View Section Headers and Footers
  • ✔How to use the generated data service to load data from the OData service You will add Table View Section Headers and Footers to give the data more structure and a nice clean UI.
Kevin Muessig K Kevin Muessig August 21, 2026
Created on January 9, 2023
Contributors

More from Kevin Muessig

See all 13 tutorials by Kevin Muessig →

Prerequisites

  • Development environment: Apple Mac running macOS Catalina or higher with Xcode 12 or higher
  • SAP BTP SDK for iOS: Version 6.0

Steps

Details

You will learn

  • How to implement a Fiori Object Table View Cell
  • How to implement Table View Section Headers and Footers
  • How to use the generated data service to load data from the OData service

You will add Table View Section Headers and Footers to give the data more structure and a nice clean UI.


Step 1 Implement loading of customer and product data
—

In the previous tutorials you’ve built the foundation for implementing the logic behind the Overview View Controller. Before you can implement the data source and delegate logic for loading the Table View, you’ll need to retrieve some data.

This is fairly simple thanks to the SAP BTP SDK for iOS and the generated model layer and convenience data service.

  1. Open up the OverviewViewController.swift class and right below the import UIKit add the following import statements:

    Swift
    
    import SAPFiori
    import SAPOData
    import SAPOfflineOData
    import SAPCommon
    import SAPFoundation
    import SAPFioriFlows
    import ESPMContainerFmwk
    import SharedFmwk

    These import statements will import SAP’s UI framework, the OData Online as well as the Offline framework, and the Common framework containing the Logging API.

  2. Next you’ll add a couple of properties required for storing a data service instance, the App Delegate instance, a Logger instance, and two arrays used to store the customer and product data.

    Add the following lines of code inside the class brackets and right below the class definition:

    Swift
    
    // The Logger is already setup in the AppDelegate through the SAP BTP SDK Assistant for iOS , that's why you can easily can get an instance here.
    private let logger = Logger.shared(named: "OverviewViewController")
    
    private var customers = [Customer]()
    private var products = [Product]()
  3. To give the user feedback of the loading process, you’re going to use a FUILoadingIndicatorView. The assistant generates a helper protocol providing you with convenient way to display a loading indicator.

    Add the SAPFioriLoadingIndicator class protocol to your class definition:

    Swift
    
    class OverviewViewController: UITableViewController, SAPFioriLoadingIndicator

    The protocol requires you to add a reference to a FUILoadingIndicatorView instance.

    Add the following line of code right below the products array:

    Swift
    
    var loadingIndicator: FUILoadingIndicatorView?
  4. Next you will implement code which is responsible for retrieving and storing a data service instance.

    Add the following lines of code as class properties to the OverviewViewController.swift class:

    Swift
    /// First retrieve the destinations your app can talk to from the AppParameters.
    let destinations = FileConfigurationProvider("AppParameters").provideConfiguration().configuration["Destinations"] as! NSDictionary
    
    var dataService: ESPMContainer<OfflineODataProvider>? {
        guard let odataController = OnboardingSessionManager.shared.onboardingSession?.odataControllers[ODataContainerType.eSPMContainer.description] as? ESPMContainerOfflineODataController, let dataService = odataController.dataService else {
            AlertHelper.displayAlert(with: "OData service is not reachable, please onboard again.", error: nil, viewController: self)
            return nil
        }
        return dataService
    }
  5. Implement a method for loading the initial data used to populate the Table View.

    Add the following method right below the viewDidLoad(:) method:

    Swift
    
    private func loadInitialData() {
      // start showing the loading indicator
      self.showFioriLoadingIndicator()
    
      // Using a DispatchGroup will help you to get notified when all the needed data sets are loaded
      let group = DispatchGroup()
    
      // Fetch customers and products, pass in the DispatchGroup to handle entering and leaving of the group
      fetchCustomers(group)
    
      fetchProducts(group)
    
      // When all data tasks are completed, hide the loading indicator and reload the table view. This will cause a refresh of the UI, displaying the newly loaded data
      group.notify(queue: DispatchQueue.main) {
          self.hideFioriLoadingIndicator()
          self.tableView.reloadData()
      }
    }

    Adding the method above will result in compile time errors because the fetchCustomers(_:) and fetchProducts(_:) methods are not implemented yet.

  6. Add the following lines of code below the loadInitialData() method to implement the query for customers:

    Swift
    
    private func fetchCustomers(_ group: DispatchGroup) {
        // Enter the DispatchGroup
        group.enter()
    
        // Define a Data Query which is a class of the SAPOData framework. This query will tell the OData Service to also load the available Sales Orders for each Customer
        let query = DataQuery().expand(Customer.salesOrders)
    
        // Now call the data service and fetch the customers matching the above defined query. When during runtime the block gets entered you expect a result or an error. Also you want to hold a weak reference of self to not run into object reference issues during runtime.
        dataService?.fetchCustomers(matching: query) { [weak self] result, error in
    
            // If there is an error show an AlertDialog using the generated convenience class AlertHelper. Also log the error to the console and leave the /group.
            if let error = error {
                AlertHelper.displayAlert(with: "Failed to load list of customers!", error: error, viewController: self!)
                self?.logger.error("Failed to load list of customers!", error: error)
                group.leave()
                return
            }
            // sort the customer result set by the number of available sales orders by customer.
            self?.customers = result!.sorted(by: { $0.salesOrders.count > $1.salesOrders.count })
    
            group.leave()
        }
    }
  7. Add the following lines of code below the fetchCustomers(_:) method to implement the query for products:

    Swift
    
    private func fetchProducts(_ group: DispatchGroup) {
        // Enter the DispatchGroup
        group.enter()
    
        // Define a Data Query only fetching the top 5 products.
        let query = DataQuery().top(5)
    
        dataService?.fetchProducts(matching: query) { [weak self] result, error in
            if let error = error {
                AlertHelper.displayAlert(with: "Failed to load list of products!", error: error, viewController: self!)
                self?.logger.error("Failed to load list of products!", error: error)
                group.leave()
                return
            }
            self?.products = result!
            group.leave()
        }
    }
  8. The last step is to call the loadInitialData() method when the OverviewViewController class is loaded.

    Add the following line of code as the last line in the viewDidLoad(:):

    Swift[4]
    
    override func viewDidLoad() {
        super.viewDidLoad()
    
        loadInitialData()
    }

    Now every time our Overview View Controller gets loaded it will load the needed data from the OData Service.

Step 2 Set up Table View and register needed cells
+
Step 3 Implement Table View's data source and delegate
+
Step 4 Create the needed segue to the Customer Detail View Controller
+
Step 5 Implement the prepare for segue method
+
Step 6 Call the perform segue method
+
Step 7 Run app to see if navigation works
+

Resources

Discussion

Share feedback on this tutorial or join the conversation in SAP Community.

Submit detailed feedback Discuss in Community
Steps
Step 1 of 7
1. Implement loading of customer and product data 2. Set up Table View and register needed cells 3. Implement Table View's data source and delegate 4. Create the needed segue to the Customer Detail View Controller 5. Implement the prepare for segue method 6. Call the perform segue method 7. Run app to see if navigation works
Next