Skip to Content

Introduction of Fiori for iOS control Series Blog

The series blogs will be a full introduction of  Fiori for iOS controls .

It helps you to get best benefits of controls provided by SAP to develop iOS apps using swift language.

Part1: Overview 

Introduction of Fiori for iOS

Overview of Controls of Fiori for iOS

Part2: Step by step blogs to use Fiori controls

  Set up development environment for Fiori for iOS development

2.1 Build input form using FUIFormCell series controls

Getting Start with FUIFormCell control and it’s sub classes

Build complex input form

Make your code reusable (This Blog)

Short Summary

Part3: Realize Fiori floor plans using Fiori for iOS controls

Part4: Theme customization

 

 

 

 

 

In my previous blog, you’ve got the ability to build a complex input form using Fiori for iOS. After you have done it, you may find the class is getting bigger and the code is not easy to maintain.

In this blog I will try to make this class more reusable, then you can use it as a pattern for build complex input form.

To start this tutorial, I assume you have finished the project in my previous blog, and this would be the start point of it.

Before we start to modify our project, I will explain my idea of how to make the code reusable.

Discussion of How to Optimize the Code.

A big class or several classes?

As you class get bigger, you may want to separate your class to several classes. For example, you can create a single class to handle the call backs of the attachment control.

But cut one big class to several classes will make your program more complex. In swift, there is a concept called extension, which can help you to split your class to several files. To make my code more clear, I will split my FioriFormTableViewController.swift into 4 files.

FioriFormTableViewController.swift is the main part of the class, I do declaration of attributes in this file( you can only declare attributes in this file!).  I will declare attributes not only save thumbnails of images but also save some configuration data to make my program reusable. Methods which relative to form configuration is also in this file.

FioriFormTableViewController_Fields.swift contains methods for each field, I will create methods for each of those fields. This can avoid you put code of cells in the long “switch… case” statement.

FioriFormTableViewController_TableView.swift contains delegate methods to feed data to the table view.

FioriFormTableViewControler_Attachment.swift contains delegate methods for attachement control, because this control is more complex than others.

Optimize the register of controls

In the example, we’ve registered controls one by one in the viewDidLoad method, the first thing I want to optimize my code is to create a dictionary variable to store which controls I’used and initialize them by loop the dictionary variable.

Optimize the code of returning the cell.

In the example, we create the cell in method

tableView(_ tableView: UITableView, numberOfRowsInSection section: Int).

That means you should generate your cell according the line number, and hard code the reuse identifier of the control. That makes your code not easy to maintain.

So I need to create some dictionary object to store:

1.In which section and row, Which property of the object should be displayed.

2.Which control is used to display a specific property.

 

Start to optimize

Open the project of my previous blog. Or download the template file.

Create extension for your class

1.Create new files for extension

Right click your project folder and choose New File from the context menu

In ‘Choose a template for your new file’ dialog, choose Swift File and click Next

Input your file name : “FioriFormTableViewController_Fields.swift” and click Create

Repeat previous steps for FioriFormTableViewController_TableView.swift  and FioriFormTableViewControler_Attachment.swift.

The final structure of your project should like the figure below:

Open FioriFormTableViewController_Fields.swift, import SAPFiori and create an extension for class FioriFormTableViewController

Do the same thing to FioriFormTableViewController_TableView.swift  and FioriFormTableViewControler_Attachment.swift.

Modify Code

1.Set property keys to avoid hard code for property name

In this example, we need to store information of every property, so we need to use the name of properties. To avoid hard code, we need to create constants for every property.

Open Person.swift and create a static structure to store property names.

Then you can access the names by typing Persion.propertyKeys.xxxxx

 

2.Refactor the code of register controls

In FioriFormTableViewController.swift, create a dictionary constant named usedControls,just below the declaration of thumbnails

The dictionary’s key is string, the reuse identifier of the control, and is’s value is the class type.

Copy the method viewDidLoad to file FioriFormTableViewController_TableView.swift

and change the code to register controls by  look up the dictionary.

3.Create methods for each cell.

Open FioriFormTableViewController_Fields.swift

Create method for every cell like following.

Each method accept a UITableViewCell variable , convert it to a specific type and set the properties and call back function like we have done before.

This time we write methods for each field makes your code more structure and easier to maintain.

Mention the signature part of function, the . ‘_’ means the label of the parameter is omitted. That means when you invoke the method, you do not need to assign name for the parameter. You can call it using modifyCellForFirstname(<cellVariable>). And you can access the parameter using ‘cell’ in your function body.

4.Move code of complex attachment control to a single file

Open FioriFormTableViewController.swift

 

 

Add a import of library Photos on on the top of FioriFormTableViewControler_Attachment

Copy following methods to FioriFormTableViewControler_Attachment.swift

attachmentsViewController(_ attachmentsViewController: SAPFiori.FUIAttachmentsViewController, didPressDeleteAtIndex index: Int)

attachmentsViewController(_ attachmentsViewController: SAPFiori.FUIAttachmentsViewController, couldNotPresentAttachmentAtIndex index: Int)

numberOfAttachments(in attachmentsViewController: SAPFiori.FUIAttachmentsViewController)

attachmentsViewController(_ attachmentsViewController: FUIAttachmentsViewController, iconForAttachmentAtIndex index: Int)

attachmentsViewController(_ attachmentsViewController: SAPFiori.FUIAttachmentsViewController, urlForAttachmentAtIndex index: Int)

addPhotoAttachmentAction(_ action: FUIAddPhotoAttachmentAction, didSelectPhotoAt url: URL)

takePhotoAttachmentAction(_ action: FUITakePhotoAttachmentAction, didTakePhotoAt url: URL)

addAttachmentURL(_ url: URL)

setupThumbnails(_ url:URL)

5.Create a dictionary to store control type for each field

No we need to create a dictionary type to find which control will be used for a specified field. For this. we need a dictionary which store the mapping between property name and reuse identifier of the control.

In FioriFormTableViewController.swift, create a . <String,String> dictionary constant named

propertyInputTypes and assign it with values.

6.Create an array to store information of position for fields.

The magic of renderer a table control is that the runtime gave us section and row, and we create the cell according the section number and row number.

Now we need to create an array, which has element is also array, to store information for section and row.

In the array we store the property name to help us get it’s position , or get the name by the position.

For example, array[1][2] means section 1, row 2.

For this example, we have 1 section and 6 rows.

7.Re write TableView methods

After so much preparation, now we start to modify code for the table view and it’s data source.

Before we change the code, let’s think about how to use those configuration data to make our code better.

a.The section count and row count can get from the sections constant.

b.If we have indexPath, we can use the section number and row number to find field name

c.We can get the reuse identifier of a field from propertyInputTypes constant, so we can init our cell in the tableView delegate according configuration data

dAnd we need one more method to help us call methods for each field according to property name.

 

Now copy following methods to FioriFormTableViewController_TableView.swift

override func viewDidLoad()

override func didReceiveMemoryWarning()

override func numberOfSections(in tableView: UITableView) -> Int

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

 

Then we start to modify those methods.

 

Modify viewDidLoad

In this method, we registered all classes we used by hard code, this time , we can get this information from the dictionary constant usedControls

 

Modify numberOfSections(in tableView: UITableView) -> Int

The number of section can be get from the length of array self.sections

Modify tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int

The number of rows of particular section can be fetched by self.sections[<section_number>] since self.sections is a tow dimension array.

 

Create a router method to call methods for each cell

Before we modify the tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell method. We need to create a new method , which can call methods for each field we created in step 3. And the router method will be called when the runtime want to render a cell.

In FioriFormTableViewController.swift, create a new method called modifyCell.

The function receive the cell needs to be render, and the property name of the cell, route the process to corresponding function.

 

Modify tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell

Delete all code of the method and rewrite it as following

 

Now you can run and test your code!

Here is the link of the source code. Please add the SDK file yourself.

 

Here we have finished FUIFormCell series. I will add a summary for this series of controls. Then I will pick another series control to show.

 

 

 

 

 

 

 

 

 

 

To report this post you need to login first.

4 Comments

You must be Logged on to comment or reply to a post.

  1. Ramesh Vodela

    After adding photos from library the refresh does not take place and hence photos are not displayed in attachments – this is not the case in the previous example

    Ramesh

    (0) 
  2. Ramesh Vodela

    By making the following changes I could make this to work

    func addPhotoAttachmentAction(_ action: FUIAddPhotoAttachmentAction, didSelectPhoto asset: PHAsset, at url: URL) {

            self.person.attachments.append(url)

             setupThumbnails(url)

            DispatchQueue.main.async {

                self.tableView.reloadData()

                self.tableView.scrollToRow(at: IndexPath(row: 0, section: 0) , at: .middle, animated: true)

            }

        }

        

        

        

        

        

        func setupThumbnails(_ url:URL){

                let status : PHAuthorizationStatus = PHPhotoLibrary.authorizationStatus()

                if status == PHAuthorizationStatus.authorized

                {

                    

                    let imgManager = PHImageManager.default()

                    

                    let requestOptions = PHImageRequestOptions()

                    

                    requestOptions.isSynchronous = true

                    

                    

                    

                    let fetchOptions = PHFetchOptions()

                    

                    fetchOptions.sortDescriptors = [NSSortDescriptor(key:“creationDate”, ascending: true)]

                    

                    

                    

                    if let fetchResult: PHFetchResult = PHAsset.fetchAssets(with: PHAssetMediaType.image, options: fetchOptions)

                        

                    {

                        

                        if (fetchResult.count > 0)

                            

                        {

                            

                            let asset = fetchResult.firstObject

                            

                            imgManager.requestImage(for: asset!, targetSize: CGSize.init(width: 80, height: 80), contentMode: .default, options: nil, resultHandler: { image,array in

                                

                                self.thumbnails[url.absoluteString] = image

                                

                                self.tableView.reloadData()

                                

                                self.tableView.scrollToRow(at: IndexPath.init(row: 0, section: 0), at: .middle, animated: true)

                                

                                

                                

                                

                                

                            })

                            

                        }

                        

                    }

                    

                }

                

            }

     

    (0) 

Leave a Reply