Adding Screenlet Actions

With multiple Interactors, it’s possible for a Screenlet to have multiple actions. You must create an Interactor class for each action. For example, if your Screenlet needs to make two server calls, then you need two Interactors: one for each call. Your Screenlet class’s createInteractor method must return an instance of each Interactor. Also, recall that each action name is given by the restorationIdentifier of the UI components that trigger them. You should set this restorationIdentifier to a constant in your Screenlet.

This tutorial walks you through the steps necessary to add an action to your Screenlet, and trigger an action programmatically. As an example, this tutorial uses the advanced version of the sample Add Bookmark Screenlet. This Screenlet is similar to the sample Add Bookmark Screenlet created in the Screenlet creation tutorial. The advanced Add Bookmark Screenlet, however, contains two actions:

  1. Add Bookmark: Adds a bookmark to the Bookmarks portlet in a Liferay DXP installation. This is the Screenlet’s main action, created in the Screenlet creation tutorial.

  2. Get Title: Retrieves the title from a bookmark URL entered by the user. This tutorial shows you how to implement this action.

Note that this tutorial doesn’t explain Screenlet creation in general. Before proceeding, make sure you’ve read the Screenlet creation tutorial. And without any further ado, it’s time to implement your Screenlet’s action!

Implementing Your Action

Use the following steps to add an action to your your Screenlet:

  1. Create a constant in your Screenlet class for each of your Screenlet’s actions. For example, here are the constants in Add Bookmark Screenlet’s Screenlet class (AddBookmarkScreenlet):

     static let AddBookmarkAction = "add-bookmark"
     static let GetTitleAction = "get-title"
  2. In your Theme’s XIB file, add any new UI components that you need to perform the action. For example, Add Boookmark Screenlet’s XIB file needs a new button for getting the URL’s title:

    Figure 1: The sample Add Bookmark Screenlets XIB file contains a new button next to the Title field for retrieving the URLs title.

    Figure 1: The sample Add Bookmark Screenlet's XIB file contains a new button next to the *Title* field for retrieving the URL's title.

  3. Wire the UI components in your XIB file to your View class. In your View class, you must also register the events you want to react to (e.g., button clicks). The BaseScreenletView class contains a set of userAction methods that you can call in your View class to perform actions programmatically. For example, it’s possible to trigger Add Bookmark Screenlet’s GetTitleAction automatically whenever the user leaves the URLTextField. Since BaseScreenletView is the delegate for all UITextField objects by default, this is done in the View class (AddBookmarkView_default) by implementing the textFieldDidEndEditing method to call the userAction method with the action name:

     func textFieldDidEndEditing(textField: UITextField) {
         if textField == URLTextField {
             userAction(name: AddBookmarkScreenlet.GetTitleAction)
  4. Update your View class or View Model protocol to account for the new action. For example, Add Bookmark Screenlet contains a View Model (AddBookmarkViewModel) so it can support multiple Themes. This View Model must allow the new action to set its title variable:

     import UIKit
     @objc protocol AddBookmarkViewModel {
         var URL: String? {get}
         var title: String? {set get}
  5. If your Screenlet uses a View Model, conform your View class to the updated View Model. For example, the title variable in Add Bookmark Screenlet’s View class (AddBookmarkView_default) must implement the setter from the previous step:

     var title: String? {
         get {
             return titleTextField?.text
         set {
             self.titleTextField?.text = newValue
  6. Create a new Interactor class for the new action. To do this, use the same steps detailed in the Screenlet creation tutorial. For example, here’s the Interactor class for Add Bookmark Screenlet’s Get Title action:

     import UIKit
     import LiferayScreens
     public class GetWebTitleInteractor: Interactor {
         public var resultTitle: String?
         var url: String
         //MARK: Initializer
         public init(screenlet: BaseScreenlet, url: String) {
             self.url = url
             super.init(screenlet: screenlet)
         override public func start() -> Bool {
             if let URL = NSURL(string: url) {
                 // Use the NSURLSession class to retrieve the HTML
                 NSURLSession.sharedSession().dataTaskWithURL(URL) {
                         (data, response, error) in
                     if let errorValue = error {
                     else {
                         if let data = data, html = NSString(data: data, encoding: NSUTF8StringEncoding) {
                             self.resultTitle = self.parseTitle(html)
                 return true
             return false
         // Parse the title from a webpage HTML
         private func parseTitle(html: NSString) -> String {
             let range1 = html.rangeOfString("<title>")
             let range2 = html.rangeOfString("</title>")
             let start = range1.location + range1.length
             return html.substringWithRange(NSMakeRange(start, range2.location - start))
  7. Update your Screenlet class’s createInteractor method so it returns the correct Interactor for each action. For example, the createInteractor method in Add Bookmark Screenlet’s Screenlet class (AddBookmarkScreenlet) contains a switch statement that returns an AddBookmarkInteractor or GetWebTitleInteractor instance when the Add Bookmark or Get Title action is called, respectively. Note that the createAddBookmarkInteractor() and createGetTitleInteractor() methods create these instances. Although you don’t have to create your Interactor instances in separate methods, doing so leads to cleaner code:

     override public func createInteractor(name name: String, sender: AnyObject?) 
         -> Interactor? {
             switch name {
             case AddBookmarkScreenlet.AddBookmarkAction:
                 return createAddBookmarkInteractor()
             case AddBookmarkScreenlet.GetTitleAction:
                 return createGetTitleInteractor()
                 return nil
     private func createAddBookmarkInteractor() -> Interactor {
         let interactor = AddBookmarkInteractor(screenlet: self,
                                                folderId: folderId,
                                                title: viewModel.title!,
                                                url: viewModel.URL!)
         // Called when the Interactor finishes succesfully
         interactor.onSuccess = {
             let bookmarkName = interactor.resultBookmarkInfo!["name"] as! String
             print("Bookmark \"\(bookmarkName)\" saved!")
         // Called when the Interactor finishes with an error
         interactor.onFailure = { _ in
             print("An error occurred saving the bookmark")
         return interactor
     private func createGetTitleInteractor() -> Interactor {
         let interactor = GetWebTitleInteractor(screenlet: self, url: viewModel.URL!)
         // Called when the Interactor finishes succesfully
         interactor.onSuccess = {
             let title = interactor.resultTitle
             self.viewModel.title = title
         // Called when the Interactor finishes with an error
         interactor.onFailure = { _ in
             print("An error occurred retrieving the title")
         return interactor

Great! Now you know how to support multiple actions in your Screenlets.

Creating iOS Screenlets

Create and Use a Connector with Your Screenlet

Creating iOS List Screenlets

Architecture of Liferay Screens for iOS

« Supporting Multiple Themes in Your ScreenletCreate and Use a Connector with Your Screenlet »