External Object for iOS Devices

Official Content

Suppose we want to add a new feature to the iOS environment to show a 'non intrusive' message to the user.

You can create a Native External Object within GeneXus; publish the desired method and properties to be called and do the implementation to integrate with the iOS platform.

Below there is a walkthrough you can follow to create an External object for the iOS platform. If you want the complete sample and more, download this sample repository and check the instructions.

You could use external implementations such as Toast-Swift or create your own. In this example we will create or own implementation to show a Toast message using a label and an animation.

Create an External Object

Create a new native object called BasicExternalObject.

ExternalObjectForSDv15iOS_1

Add a new method called Hello with the following values:

Is Static: True

Add a second method called Message with an Character argument.

Once the external object is saved, you can use it with the following syntax:

BasicExternalObject.Hello()
BasicExternalObject.Message(&text)

Now, we have to do some implementation to actually get this working.

You need to create an extension library where you declare your implementation details, dependencies and so on.

Mapping the External Object with an implementation File

During execution, the GeneXus iOS application does not know the External Object implementation class, just the External Object method executed with its arguments.

The iOS External Object developer must provide a binding for a class which actually resolves the method execution.

Create a swift class named SampleExtensionLibrary with the following code; you are registering your External Object:

import GXCoreBL
@objc(SampleExtensionLibrary)
public class SampleExtensionLibrary: NSObject, GXExtensionLibraryProtocol {
    public func initializeExtensionLibrary(withContext context: GXExtensionLibraryContext) {
        GXActionExternalObjectHandler.register(BasicExternalObject.self, forExternalObjectName:BasicExternalObject.classIdentifier)
    }
}

Then you need to actually implement the external object methods and properties so create a BasicExternalObject swift class.

Implementation Class

Notice the class must inherit from GXActionExternalObjectHandler and the following methods must be implemented:

override public class func handleActionExecutionUsingMethodHandlerSelectorNamePrefix() -> Bool {}
@objc public func gxActionExObjMethodHandler_Message()
@objc public func gxActionExObjMethodHandler_Hello()

Your implementation methods will vary depending on the method names associated to your External Object. Notice Message and Hello are related to this External Object definition. As a rule, the pattern you need to follow to implement the associated methods are:

@objc <ClassVisibiliy> func gxActionExObjMethodHandler_<ExternalObjectMethodName>()

where:

  • ClassVisibiliy: is related to the visibility assigned to your class.
  • ExternalObjectMethodName: method name that must match the External Object definition.

The minimal implementation for the class would be:

public static let classIdentifier = "BasicExternalObject"

override public class func handleActionExecutionUsingMethodHandlerSelectorNamePrefix() -> Bool {
     return true
}

@objc public func gxActionExObjMethodHandler_Message() {
     guard let message = self.readParameter() else {
         let error = NSError.wrongNumberOfParametersDeveloperError(forMethod: self.actionExObjDesc.actionExternalObjectMethod)
            self.onFinishedExecutingWithError(error)
            return
        }
    self.showToast(message: message)
    self.onFinishedExecutingWithSuccess()
}

@objc public func gxActionExObjMethodHandler_Hello() {
    let hello = "Hello World!"
    self.showToast(message: hello)
    self.onFinishedExecutingWithSuccess()
}

Notice that some helper methods are missing:

private func readParameter() -> String? {
    guard let actionParameterArray = self.actionExObjDesc.actionParametersDescriptor??.actionParametersDescriptors,
        actionParameterArray.count == 1 else {
        return nil
    }
    let paramValue = self.readStringParameter(actionParameterArray[0], from: self.contextEntityData())
    return GXUtilities.nonEmptyString(from: paramValue)
}

And the toast implementation !!!

private func showToast(message : String) {
    // Ideas from https://stackoverflow.com/questions/31540375/how-to-toast-message-in-swift
    // First get the controller and its view
    if let rootController = GXExecutionEnvironmentHelper.keyWindow?.rootViewController {
        // Build the Toast sample
        let size = rootController.view.frame.size
        let toastLabel = UILabel(frame: CGRect(x: (size.width)/2 - 75, y: (size.height) - 100, width: 150, height: 35))
        toastLabel.backgroundColor = UIColor.black.withAlphaComponent(0.6)
        toastLabel.textColor = UIColor.white
        toastLabel.textAlignment = .center;
        toastLabel.font = UIFont(name: "Montserrat-Light", size: 12.0)
        toastLabel.text = message
        toastLabel.alpha = 1.0
        toastLabel.layer.cornerRadius = 10;
        toastLabel.clipsToBounds  =  true
        
        rootController.view.addSubview(toastLabel)
        
        UIView.animate(withDuration: 4.0, delay: 0.1, options: .curveEaseOut, animations: {
            toastLabel.alpha = 0.0
        }, completion: {(isCompleted) in
            toastLabel.removeFromSuperview()
        })
    }
}

Implementation details

The GeneXus iOS application will call the handleActionExecutionUsingMethodHandlerSelectorNamePrefix method whenever it finds an action that calls the external object, to check if the action can be handled. Then depending on the method executed it will automatically bind to your implementation:

gxActionExObjMethodHandler_<ExternalObjectMethodName>()

Get the method arguments (for this case we are using the readParameter method.

In relation to the Toast sample, the actual implementation is done on the showToast function.

Done ! you are ready to test your iOS BasicExternalObject external object with two available methods.

Recommendations

Make sure each External Object method calls the onFinishedExecutingWithSuccess, so the base class can call any other object to finish the method execution, for errors use onFinishedExecutingWithError function.

Troubleshooting

Use Xcode to debug the application calling the External object methods.

See also

Was this page helpful?
What Is This?
Your feedback about this content is important. Let us know what you think.