Build input form using FUIFormCell series controls-Make your code reusable
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.
Part2: Step by step blogs to use Fiori controls
2.1 Build input form using FUIFormCell series controls
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.
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.
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
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)
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 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.
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.