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: 
3,155
In this post, I’ll show you how easy it is to add custom functionality to a Mobile Development Kit app with an extension control. By the time you’ve gone through these steps, you’ll have integrated a KPI view from the SAP Cloud Platform SDK into your MDK app. First, you’ll see how to add the extension control in the editor. Next, you’ll define a basic Swift control and see it displayed in your app. Finally, you’ll see how to customize the control’s appearance and behavior!

A Mobile Development Kit app consists of controls, or UI elements that show application data and allow user input. Developers can use the visual editor to drag controls such as text fields, buttons, and list pickers into the app. But sometimes you may need to use a control – say, a KPI view – which is not included in MDK out of the box. With extension controls, you can incorporate this functionality while leveraging actions, rules, and user data from your app.



If you want to follow along, you should first complete the video tutorials in this YouTube playlist, including the optional “Setup Client” video. This way, you’ll have a working development environment and a starting point to build on. If you’re new to MDK, you can find more links and information here.

 

Add the extension control in the editor

In the tutorials, you created an application that shows a list of products and allows the user to select a product to view its details. We’ll add an extension control to the detail page. The first step is to register a new extension in the editor. You can do this by right-clicking the top-level Workspace folder and selecting New | MDK Extension Control.



In the wizard, enter "CustomKPIControl" as both the control name and class.



Click Next, then Next again because you don't need to define a schema just yet. Click Finish to create the extension control definition. You'll notice that a new project called "MDKExtensionControls" is created with your newly registered extension definition. It's a separate project so that you can easily reuse the controls in multiple apps.



Now select the ProductDetail page so that the extension can be added to it. Under the Registered Extension Control section in the Controls pane, drag the CustomKPIControl into the sectioned table. In the Properties view, set the Height to 300.



When you’re satisfied, save and publish your app!



Switch over to the MDK client. It should pick up your latest change. Is the detail page any different? You should see that your new control is shown, except it only displays a message: “Extension with class name CustomKPIControl was not found”. The client checked for a Swift class by that name but couldn’t find it. That’s no surprise because you haven’t defined the class yet!



 

Define a basic Swift control and see it displayed in your app

To create a custom extension, you’ll need to open your client project in Xcode. If you have a tns command running in Terminal, cancel it with Ctrl+C. In Xcode, select File | Open… , then find your client project and open <ProjectName>/platforms/ios/<ProjectName>.xcworkspace. You can use this workspace to run the client, just like with the “tns run” command.

The extension is defined as a Swift class, so create one from scratch by right-clicking the client project and selecting New File… . Select the “Swift File” option, then enter “CustomKPIControl.swift” as the file name. If Xcode asks you about configuring an Objective-C bridging header, choose “Don’t Create.”



Now that you have a blank Swift file whose name matches what you specified in the editor, copy the following code into the file. This is a nearly blank extension implementation which only displays a label. You can see that in the viewDidLoad method, the label is created and added to the extension as a subview.
import Foundation
import SAPMDC
import SAPFiori

@objc(CustomKPIControl)
public class CustomKPIControl: UIViewController, MDKExtension {
public var delegate: SwiftExtensionDelegate?

public func controlValueChanged(with value: NSDictionary) {
}

public func update(with params: NSDictionary) {
}

private let label: UILabel = UILabel()

public override func viewDidLoad() {
super.viewDidLoad()
// enable auto-resizing
self.label.autoresizingMask = [UIViewAutoresizing.flexibleHeight, UIViewAutoresizing.flexibleWidth]
// Note: In Xcode 10, the above line must be changed to the following:
// self.label.autoresizingMask = [UIView.AutoresizingMask.flexibleHeight, UIView.AutoresizingMask.flexibleWidth]
// Center the text
self.label.textAlignment = NSTextAlignment.center
// Update the label frame size
self.label.frame = self.view.frame
self.label.text = "This is a sample extension."
self.view.addSubview(label)
}
}

Test out your change by clicking Product | Run. If the app builds and runs successfully, you’ll see that the extension control appears a little different this time: The label text that’s created in the viewDidLoad method is shown.



Next, let’s add the KPI view into the extension. You’ll do this by creating an FUIKPIView control instead of an FUILabel. Replace the contents of CustomKPIControl.swift with the code below to make this change. Notice that whatever kind of control you want to add, you just have to create it and add it as a subview. The code below configures the KPI view with some canned data.
import Foundation
import SAPMDC
import SAPFiori

@objc(CustomKPIControl)
open class CustomKPIControl: UIViewController, MDKExtension {
public var delegate: SwiftExtensionDelegate?

var kpiItems: [FUIKPIViewItem]?

public func update(with params: NSDictionary) {
}

public func controlValueChanged(with binding: NSDictionary) {
}

override open func viewDidLoad() {
super.viewDidLoad()
// Create the stack view to hold the KPIs
let stackView = UIStackView()
self.view.addSubview(stackView)
stackView.axis = .horizontal
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 25
stackView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: self.view.centerYAnchor)
])

// Create a KPI view to add to the stack view
let kpiView = FUIKPIView()
let metric = FUIKPIMetricItem(string: "294")
let currency = FUIKPIUnitItem(string: "USD")
kpiView.items = [metric, currency]
kpiView.captionlabel.text = "Price"
stackView.addArrangedSubview(kpiView)
}
}

Replace the Swift file with these contents, then run the application again with Product | Run. The KPI view should now appear in the extension control:



 

Customize the control’s appearance and behavior

It’s great that you can develop a Swift control that shows up in the application, but what’s even more useful is that the control can consume data from the OData service. In this case, the extension section is bound to the product being displayed. Its data is provided to the controlValueChanged(with binding: NSDictionary) method when the page loads and anytime the binding happens to be updated. The method is empty now; let’s change that to display data in the KPI! Change the controlValueChanged to the following:
    public func controlValueChanged(with binding: NSDictionary) {
let price: AnyObject = binding.value(forKey: "Price") as! NSNumber
let currencyCode: String = binding.value(forKey: "CurrencyCode") as! String
let metric = FUIKPIMetricItem(string: price.stringValue)
let currency = FUIKPIUnitItem(string: currencyCode)
self.kpiItems = [metric, currency]
}

Now that the KPI items are populated from the OData service, you can stop using canned data. Replace the bottom of viewDidLoad with the following:
        // Create a KPI view to add to the stack view
let kpiView = FUIKPIView()
kpiView.items = self.kpiItems
kpiView.captionlabel.text = "Price"
stackView.addArrangedSubview(kpiView)

Now run the application again, and you’ll see that the Price and CurrencyCode properties are shown in the KPI!



To bring things full circle, let’s go back to WebIDE to see another way that you can integrate between the app and your extension. Right-click the actions folder in your app and choose the option to create a new action. Choose the Message action type, and name your action “ExtensionMessage.” For the message text, enter something like “Message triggered from the extension!” Specify the Title and OK Caption properties as “Extension Message” and “OK”.



Next, you'll want to make the action's definition path ("/Demo/Actions/ExtensionMessage.action") available in Swift so that it doesn't need to be hard coded. This can be accomplished with the schema you skipped earlier. Navigate to the MDKExtensionControls project and open the CustomKPIControl extension. Update the schema field with the following:
{
"OnPressAction": ""
}

This is meant to tell the editor that the extension will pass a string called OnPressAction to the Swift control. Now click "Generate Schema" and the content will be changed to look like this:



This updated JSON is used internally by the editor to make it easy to specify the value of OnPressAction. Save this change and view the ProductDetail page again, selecting the extension section. You'll see that the right pane now shows the OnPressAction property under Extension Properties.



Click the link button in the Value column to specify the ExtensionMessage action. In the Object Browser, you can find it by first selecting the Actions and Rules view in the top left dropdown, then searching for it in the top right. Double click the action so that it's added to the Expression field, then click OK.



Now that the action is configured in the Extension Properties, publish your app so that the change is picked up by the client.

On the Swift side, let's store the action reference and trigger the action when the user touches the control. The Extension Properties are passed to Swift as an NSDictionary in the update(with params:) method. Create a new member variable to store the dictionary, and assign it in the update method:
	var extensionProperties: NSDictionary?

public func update(with params: NSDictionary) {
extensionProperties = params
}

You can configure the KPI view to respond to touches by directly triggering the action in Swift. Any calls from the extension to the app are made with the control’s delegate. This API can be checked by right-clicking the SwiftExtensionDelegate class near the top of the file and choosing Jump to Definition. This will show you the delegate’s protocol:



To trigger the action, define a showMessage method that will call executeActionOrRule with the path to the definition you defined.
	@objc func showMessage() {
self.delegate?.executeActionOrRule(definitionPath: extensionProperties?.value(forKey: "OnPressAction") as! NSString, callback: { (result: String) in
print("Called ExtensionMessage.action.")
})
}

Next, connect the user’s touches to this method by adding the following to the bottom of viewDidLoad:
        kpiView.addTarget(self, action:#selector(CustomKPIControl.showMessage), for: .touchUpInside)

Now try it out: Run the app again and see what happens when you press the KPI view. The message should be displayed!



As you can see, Mobile Development Kit makes it a breeze to implement a custom control in Swift. It’s just a matter of defining the extension control in the editor, hooking it up to a Swift class in your project and configuring the control to suit your needs. Please note that there are still more ways to integrate between your app and your extension control: You can also resolve the value of any rule, global or target path and even call into Swift from an MDK rule. Extension controls let you customize your app when needed, and focus on high-level business logic the rest of the time. Please leave your questions and comments. I look forward to seeing how you use extension controls in Mobile Development Kit!
9 Comments