The problem to be solved
Recently I needed for a project to interact with the actions of a panel, in a way that is not the default. There were basically to possibilities: to hack the solution into the GeneXus libraries, or to create an External Object to provide the functionality. We decided to use the later.
What I needed, was to be able to display the low-priority actions in a panel where the navigation bar is hidden.
Some background here. When you create a Smart Device Panel in GeneXus, you can define actions, which have a priority: High, Normal or Low. The low-priority actions are displayed in an action sheet, that is presented from a button in the navigation bar.
The problem was that the navigation bar was hidden, so the user didn't even see the button to show the action sheet.
Creating a new External Object
The implementation of an External Object consists on three parts:
- The implementation of the functionality, in Xcode
- The GeneXus object that defines the External Object's API
- The mapper, that translates the name of the GeneXus object to the Objective-C class.
Lets start by creating the definition. To do that, in your GeneXus KB, create a new object of type "External Object" and give it a name. In this case, the object is called EOActions.
After that, you need to define the methods of the External Object. In this case, it has only one method named "ShowLowPriorityActions" that doesn't receive any parameters and has no return value.
Important: every method must be declared as "static" in the method properties.
Implementing the External Object
The implementation is of course in Xcode, since we need to create a library.
So, open Xcode and create a new library project.
Add a reference to the GXFlexibleClient.framework.
The main class implementing the External Object must subclass GXActionExternalObjectHandler (you'll need to add an '#import <GXFlexibleClient/GXFlexibleClient.h>' ), and it must provide two methods:
- + (BOOL)canHandleAction:(id <GXActionExternalObjectDescriptor>)actionDesc
- - (void)actionExecuteWithContext:(id<GXActionHandlerContext>)context delegate:(id<GXActionHandlerDelegate>)delegate
After that, you need to provide your custom implementation, and once you are done, you must call either one of:
- [self onFinishedExecutingWithSuccess];
- [self onFinishedExecutingWithError:error];
After you compile the library, you need to copy it to the GeneXus KB. It must be placed in
- <model_dir>\mobile\iOS\<main_obj_name>\iOS\Genexus\UserControls
Mapping the External Object name to the implementation class
The last piece, is the source file to provide the mapping between the GeneXus' object name and the class implementing it.
The class must be named GXCustomExternalObjectsMapper, and has only one method:
- - (NSString *)externalObjectClassNameForObjectName:(NSString *)objName;
This sources (GXCustomExternalObjectsMapper.h and GXCustomExternalObjectsMapper.m) should be placed in
- <model_dir>\mobile\iOS\<main_obj_name>\iOS\Genexus\Classes
I know this may be a little intimidating at first, but once you've done an External Object, you'll see it is not too difficult. It involves only this three steps as described above.
I've uploaded the XCode project, the GeneXus External Object export (as a .xpz file) and the mapper class to a GitHub repository, so you can play with the code.