[UPDATED: this post was updated in April 2019 for changes with the 3.0 version of the SAP Cloud Platform SDK for iOS Assistant]

In our previous posts, we described the example Corporate Directory app that we built using the SAP Cloud Platform for iOS SDK; how we started the project in SAP’s Cloud Portal, and how we used the SAP Cloud Platform SDK for iOS Assistant to generate an Xcode project. In this post we will describe how we customized the iOS project to meet our design standards and requirements.

Customizing the First Screen

Since we want the app to start with the list of employees, we can skip the entity selection screen altogether. To skip the Entity selection and go directly to the User list, we changed the Main Interface in the project settings:

Xcode project settings

This ensures that the app will start with initial view controller from the User storyboard, which was generated by the Assistant. We removed the generated Main storyboard from the project since it was no longer needed, and updated code in the setRootViewController() function in the AppDelegate to launch the UserMasterViewController. We updated the User storyboard to enable safe area guides to ensure the app works well with devices that support FaceID.

Customizing the List View

In the User storyboard, we added a navigation controller since we removed the Main storyboard, and made it the initial view controller. We added a new view controller to house our static header with the SAP company address, and then embedded the UserMasterViewController below the static header:

Xcode project settings

SAP provides a number of common user interface components in the iOS SDK, referred to as SAP Fiori Components. There is an SAP Fiori iPad app (called Fiori Mentor) freely available in the app store that demonstrates many of these components - we used it to explore and decide what might work for our purposes. As part of our goal for this project, we used as many of these components as possible in the project.

The generated table view used FUIObjectTableViewCell. We changed it to use FUIContactCell, which we then customized to achieve the desired look:


  override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    guard let sectionKey = self.directoryIndex?[indexPath.section],
      let userSectionList = self.indexedEntities?[sectionKey] else {
        return UITableViewCell()
    }
    
    let user = userSectionList[indexPath.row]
    
    let cell = tableView.dequeueReusableCell(withIdentifier: FUIContactCell.reuseIdentifier) as! FUIContactCell
    cell.headlineLabel.font = UIFont(name: "DINOffc", size: 17)
    cell.subheadlineLabel.font = UIFont(name: "DINOffc", size: 13)
    cell.subheadlineLabel.textColor = UIColor(named: "sapDarkGrey") ?? UIColor.black
    cell.detailImage = user.avatarImage ?? UIImage(named: "default_emp_image")
    let firstName = user.firstName ?? "John"
    let lastName = user.lastName ?? "Doe"
    cell.headlineText = "\(firstName) \(lastName)"
    cell.subheadlineText = user.title ?? "Employee"
    
    return cell
  }

(Note: we could have done some of this customization in the storyboard or in a subclass, but this approach made it clear to explain.)

To add the side index, we first needed to determine which letters needed to be in the index. We added some logic to iterate over the list of employees when it is set from the query; that logic creates a list of unique first letters from the each employee’s last name, and keeps a list of which employees are in each section as defined by the last name letter. We implemented the table view methods to have one section per letter, and correctly return the count of employees in each section. Then we implemented the sectionIndexTitles method to tell the table view to display the list of letters in the index on the right side of the table.

For search, we configured the table view to use a UISearchController, with a simple table view for results. Then, we took advantage of SAP’s DataQuery to filter the list of employees by the search terms:


  // get individual terms
  let terms = controller.searchBar.text?.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) ?? ""
   
  // build query
  let searchQuery = DataQuery()
      .filter(User.firstName.contains(terms)
      .or(User.lastName.contains(terms)))
   
  // filter using DataQuery
  if let userSearchResultsController = controller.searchResultsController as? UserSearchResultsController {
      userSearchResultsController.searchResults = try? userEntities.filterWithQuery(searchQuery)
      userSearchResultsController.tableView.reloadData()
  }

Customizing the Detail View

The generated detail view looked like this:

Xcode project settings

Again, all the plumbing was in place for us, and all we had to do was adjust it to our design. To do this, we took advantage of several SAP Fiori components. For the header view, including the employee’s name, position, and avatar, we used a FUIProfileHeader. We customized it to match our design:


  let profileHeader = FUIProfileHeader()
  profileHeader.backgroundColor = UIColor(named: "sapLightGrey")
   
  guard let entity = self.entity else { return profileHeader }
   
  profileHeader.imageView.image = entity.avatarImage ?? UIImage(named: "default_emp_image")
   
  let firstName = entity.firstName ?? "John"
  let lastName = entity.lastName ?? "Doe"
   
  profileHeader.headlineText = "\(firstName) \(lastName)"
  profileHeader.headlineLabel.textColor = .black
  profileHeader.headlineLabel.font = UIFont.systemFont(ofSize: 32)
   
   
  profileHeader.subheadlineText = entity.jobTitle ?? "Employee"
  profileHeader.subheadlineLabel.font = UIFont.systemFont(ofSize: 14)
  profileHeader.subheadlineLabel.textColor = UIColor(red: 142/255, green: 142/255, blue: 147/255, alpha: 1)

Since we had an external source for the avatar images, we customized the generated User model object by adding the avatarImage attribute to generate a UIImage from our source.

We also wanted to have the buttons for messaging, email, and phone available in the header view. Fortunately that was a simple addition, using the FUIActivityControl, which we added to the profile header’s detailContentView:


  let tintColor = UIColor(named:"sapControlTint")!
  let activityControl = FUIActivityControl()
  activityControl.addActivities([.message, .phone, .email])
  activityControl.maxVisibleItems = 3
  activityControl.activityItems[.phone]?.setTintColor(tintColor, for: .normal)
  activityControl.activityItems[.phone]?.setImage(UIImage(named:"call_active"), for: .normal)
  activityControl.activityItems[.message]?.setTintColor(tintColor, for: .normal)
  activityControl.activityItems[.message]?.setImage(UIImage(named:"text_active"), for: .normal)
  activityControl.activityItems[.email]?.setTintColor(tintColor, for: .normal)
  activityControl.activityItems[.email]?.setImage(UIImage(named:"mail_active"), for: .normal)
   
  activityControl.spacing = 40
   
  profileHeader.detailContentView = activityControl
   
  profileHeader.splitPercent = 0.75

Once the header was in place, we decided to limit the additional information to a few set attributes - contact information first, then employment details. We used the FUIKeyValueFormCell for each of these pieces of information and just did minor styling work to it to make it match our design.

What’s Next

What will we do next? For an app like this, offline capability is an important feature. We will be investigating the SDK’s offline features and integrating them into this app. When we build similar apps for clients, we typically ship a “seed database” with data at a known point in time, and then provide the ability to sync from there. This prevents the app from having to do a large initial sync, especially for non-transactional data. We will be investigating how to accomplish this approach using the OfflineODataProvider.

We are also interested in exploring how managers can leverage HR data at their fingertips - there are lots of possibilities, including: capturing and reviewing data for performance reviews; reviewing department budgets and cost center performance (even being notified when estimates are exceeded or targets are hit); and being able to more closely manage key performance indicators without having to wait for generated reports or meetings.

In addition, we will be exploring how we can leverage other Success Factors capabilities from SAP to build apps that extend the enterprise into the mobile space. An obvious direction is to use this same concept for client or customer contact information, and to consider what simple features (including basic order and delivery status) could provide strong customer service benefits.

We would be happy to chat about what we did, and learn what you think would be valuable in a mobile app built with SAP’s Cloud Platform SDK for iOS. Contact us at martiancraft.com.

Joe Keeley

MartianCraft Alumni

MartianCraft is a US-based mobile software development agency. For nearly two decades, we have been building world-class and award-winning mobile apps for all types of businesses. We would love to create a custom software solution that meets your specific needs. Let's get in touch.