Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
KRiedelsheimer
Community Manager
Community Manager

The SAP BTP SDK for iOS, SwiftUI & OData


Wow! What a title right?!

It is actually pretty simple to create a SwiftUI based app and connect it against any OData V2 service with the help of SwiftUI implementation of the SAP Fiori for iOS Design Language Open Source GitHub repository which currently provides you with Swift Packages to accomplish a SwiftUI implementation following the SAP Fiori Design Guidelines. In a previous blog post I've talked about that in a bit more detail, what is different from this blog post to the last one is that here I want to introduce you to a pure SwiftUI approach compared to the UIKit + SwiftUI approach from the last post.

So fire up the Advocates Service on your SAP BTP Trial landscape and let's get started!

! Important: This is a sample to display the technical possibilities. If you want to use the SAP BTP SDK for iOS in production, be aware that you will need a SAP Mobile Services license to do so. In SAP BTP Trial or Free Tier you can implement this example as is.

We follow a couple of steps to get where we can start building the SwiftUI views for our Advocates App implementation:

 

  1. Start the Advocates Service on SAP BTP Trial and the SAP HANA Cloud instance.

  2. Download the SAP BTP SDK for iOS latest version as we're going to use the OData proxy generator tool to generate our model layer.

  3. Use the OData Proxy generator to generate the OData proxy classes

  4. Setup the Data Service layer to connect to the OData endpoint

  5. Create the Advocates App views

  6. Enjoy your SwiftUI Advocates App 🥳


Make sure your instances are running


With SAP BTP Trial your SAP HANA Cloud instance as well as your running applications get stopped during night so you have to start them back up in order to use the Advocates Service.

  1. Open SAP HANA Cloud Central and start your HANA instance (The linked URL is for EU10)

  2. Start your server module application within SAP BTP Trial

  3. Check if your service is running by opening the service URL


Install the OData Proxy Generator


With the download of the SAP BTP SDK for iOS Assistant you automatically get the underlying CLI for the OData Proxy Class Generator. This tool can be installed on your local machine through the Install Tools menu item of the SAP BTP SDK for iOS Assistant.



Generate the OData Proxy Classes


Alright let us create the OData Proxy Classes using our Terminal, iTerm2 or any other terminal app on your macOS machine.

We're going to use the OData Proxy Class Generator tool to parse a local copy of the Advocates Service Metadata document into Swift-native proxy classes as our model layer in our Advocates App.

We are using the OData Proxy Class Generator tool directly as we do not use the SAP BTP SDK for iOS Assistant to create the app. We want to create a project with a SwiftUI interface and SwiftUI App lifecycle while the Assistant can only create a project with Storyboard interface and UIKit App Delegate lifecycle.

  1. Open up your service and navigate to the metadata document

  2. Right-click and save as xml file, without the $ character in the front, to your local file system

  3. Open up your Terminal an paste in the following command, make sure to fill the arguments correctly
    sapcpsdk-proxygenerator -m <path>/metadata.xml -s advocatesService -d <destination-path>​

    The output should show you a message about the successful creation of the proxy classes

  4. Inspect the Proxy Classes by navigation to the output destination


The proxy classes will be used by the data service to decode the HTTP responses into the correct Swift classes in order for you to work with the data in Swift. With the Proxy Classes the generator also creates you a complete API for you to more easily access the backend service.

Create the SwiftUI app and the needed Frameworks


With Xcode you can simply create a new SwiftUI based app for your app logic to live in. This will give you the needed project files to get started developing an SwiftUI app. The generated proxy classes folder you can simply drag from finder into your created SwiftUI project and check the Copy Files if needed checkbox to also change the folders location on your file system.


Okay we got the proxy classes, we got the project created, but one thing is still missing: The needed Frameworks.

With the iOS Assistant you can export the frameworks of the SAP BTP SDK for iOS directly to your project. What I like doing is to create a Group within my Xcode project and export the frameworks in there. You can find the export option as menu item in the iOS Assistant.


Using the Fiori SwiftUI open source frameworks you need to add the FioriSwiftUI Swift Package to your Xcode project using the Swift Package Manager.

Simply add the FioriSwiftUI Swift Package using the repository URL. If you need more information about SwiftUI read here .

Xcode will automatically fetch necessary package dependencies as defined in the package manifest file.


Establish Connection to the OData Service


Now we get to the truly fun part! We want to establish a connection to the OData Service and bring together the Proxy Classes with SwiftUI. In order to do that we have to first understand how SwiftUI operates in comparison to UIKit.

UIKit


UIKit has been the main framework for building user interfaces for iOS over the last decade. It does its job really well and is a mature framework with tons of APIs to have a great flexibility when it comes to building UX. UIKit leverages the Interface Builder to build and design UI without writing much code. UIKit based applications usually follow the Model-View-Controller (MVC) design pattern and is code focused where views and layout constraints are created in Swift or Objective-C. It can be quite complex for beginners but can be truly powerful! Providing data and interaction with the UI is implemented through delegation and datasource patterns which can result in quite some code if you have complex UIs.

SwiftUI


SwiftUI is a declarative programming framework used for building Swift based apps for iOS and Mac. With UIKit you delegate and partially manage the relationship between data, events and changes to the UI where with SwiftUI the framework does this for you. SwiftUI works seamlessly together with the Combine framework which helps you creating a data binding between your data model and the UI. You're not using Interface Builder (.storyboard and .xib) to layout your UI, instead you're declaring the UI and Xcode displays the changes alongside the declaration.

With UIKit you have to load the data from the data service and display and react to changes through delegation and data source implementation. Using SwiftUI you have to make sure that your data model works with Combine and SwiftUI in order to create the proper data binding.

Our task is now to extend the proxy classes in a way that it works together with this data binding approach.

Model Extension


If we look at the Member Proxy Class we can see it is a Swift implementation of data model class. It contains of the OData properties and methods to set and read the data. We need to let the data class conform to the Identifiable protocol.
Use the Identifiable protocol to provide a stable notion of identity to a class or value type. For example, you could define a User type with an id property that is stable across your app and your app’s database storage. You could use the id property to identify a particular user even if other data fields change, such as the user’s name.

Identifiable leaves the duration and scope of the identity unspecified. Identities could be any of the following:

  • Guaranteed always unique (e.g. UUIDs).

  • Persistently unique per environment (e.g. database record keys).

  • Unique for the lifetime of a process (e.g. global incrementing integers).

  • Unique for the lifetime of an object (e.g. object identifiers).

  • Unique within the current collection (e.g. collection index).


It is up to both the conformer and the receiver of the protocol to document the nature of the identity.

API Documentation - https://developer.apple.com/documentation/swift/identifiable


  1. Create a new class with the name Member+Extensions to add an extension to the Member proxy class

  2. Add an extension for Identifiable

  3. Import the FioriSwiftUICore module

  4. Add an extension of ObjectItemModel to the Member and implement the needed methods


import Foundation
import FioriSwiftUICore
import SwiftUI

extension Member: Identifiable {}

extension Member: ObjectItemModel {
public var title_: String {
firstName! + " " + lastName!
}

public var subtitle_: String? {
title!
}

public var footnote_: String? {
focusArea!
}

public var descriptionText_: String? {
description!
}

public var status_: TextOrIcon? {
nil
}

public var substatus_: TextOrIcon? {
nil
}

public var detailImage_: Image? {
nil
}

public var icons_: [TextOrIcon]? {
nil
}

public var actionText_: String? {
nil
}

public func didSelectAction() {
()
}
}

The  ObjectItemModel is a protocol defined by the FioriSwiftUICore module for you to use as a helper protocol in order to get a SwiftUI conform model definition out of the box. With this model you can define what the values should be when using a Proxy Class, in this case Member, within your SwiftUI Views.

Data Model implementation & The ObservableObject protocol


The Combine framework is using the ObservableObject protocol to observe and listen to changes for a specific class. If changes occur Combine will make sure that it triggers the update of the SwiftUI views for you so the newest data is being displayed.

The implementation is simple:

  1. Make the DataModel conform to ObservableObject

  2. Publish your data set via the @Pubished property wrapper to publish the property

  3. To make it simple, implement an init method where you define the OData endpoint, create the OData provider and pass the provider to the service

  4. Fetch the needed data


import Foundation
import Combine
import SAPFoundation
import SAPOData

class DataModel: ObservableObject {
@Published var advocates: [Member] = []

init() {

guard let serviceEndpoint = URL(string: "https://advocatesservice.cfapps.eu10.hana.ondemand.com/v2/advocates") else {
return
}
let provider = OnlineODataProvider(serviceRoot: serviceEndpoint)
let service = AdvocatesService(provider: provider)

service.fetchMember { members, error in

if let error = error else {
// Error handling
return
}
self.advocates = members ?? []
}
}
}

Now your service is created and the data published with the Combine framework. You can now build your UI against this and it will automatically update when the dataset changes.

Implement your SwiftUI Views


In this part of this Blog post I just want to give you a rough overview on how you could implement a SwiftUI view connecting against the DataModel class and display some Advocates in a List.
import SwiftUI
import FioriSwiftUICore

struct ContentView: View {

@EnvironmentObject var dataModel: DataModel

var body: some View {
NavigationView {
List {
ForEach(dataModel.advocates) { advocate in
// Model-based initializer
NavigationLink(destination: AdvocatesDetailView(advocate: advocate)) {
ObjectItem(model: advocate)
.padding(EdgeInsets(top: 0, leading: 32, bottom: 0, trailing: 32))

// Alternative: ViewBuilder-based initializer
// if let firstName = advocate.firstName, let lastName = advocate.lastName, let title = advocate.title, let area = advocate.focusArea {
// ObjectItem(title: {
// Text(firstName + " " + lastName)
// }, subtitle: {
// Text(title)
// }, footnote: {
// Text(area)
// })
// }
}
}
}.navigationTitle("Advocates App")
}
}
}

struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView().environmentObject(DataModel())
}
}

What we do here is:

  1. Define an EnvironmentObject which represents the DataModel. The EnvironmentObject is a property Wrapper which represents a published property. This is the connection to the data model, the binding!

  2. Implement your View with SwiftUI components

  3. Use the ObjectItem defined by the FioriSwiftUICore module. This is an implementation of a simple list item by the framework which can work with the ObjectItemModel we've defined.

  4. Set the EnvironmentObject on the ContentView within the PreviewProvider to use the live preview.


If you look more closely, you should see a commented section with an alternative approach on building the List Item. With FioriSwiftUICore you get multiple options on how to instantiate such an Object Item. There is the initializer approach as used in the example above, where you pass the model directly into the object and the ViewBuilder based initializer which allows you to have more control over the UI element.

In this case the view looks like this when run on the simulator.



Conclusion


You can see, by using the SwiftUI implementation of SAP Fiori Design Language available over the open source project on GitHub, you can simply make the proxy classes work together with SwiftUI. Please remember, that the repository is currently under development so if you want to use the provided Frameworks be aware that the APIs are not supported at the moment and they are not stable. Still it is a ton of fun to use this approach and frameworks to try out SwiftUI in combination with SAP BTP services.

Next time we will look at how to implement Authentication in the Advocates Service and how to adapt the app with a authorisation screen.

In the upcoming SAP Tech Bytes video of this series I will go more into the implementation details of the SwiftUI based Advocates App.

As always, Keep Coding! And have fun trying this out on your own 🙂.
1 Comment