Extending the language / User-defined front calls |
Custom front call modules for the iOS front-end are implemented by using the API for GMI front-calls in Objective-C.
In order to extend the GMI with your own front calls, you must be familiar with Objective-C programming, and if you want to interface with iOS Apps, have a knowledge of the iOS API.
The API for GMI front calls is based on the FrontCall class and the FrontCallHelper and FunctionCall protocols. You can find these in the file frontcall.h in the in the FGLDIR/include/gmi diretory.
To implement custom front calls, write a class which extends FrontCall and implement the “moduleName” and “execute:retCount:params” methods as well as the “initWithFunctionModuleHelper:” initializer.
To register your front calls with GMI, implement a function “NSArray* frontCalls()” in your extension project, which has to return an array of Objective-C strings with the names of the FrontCall classes you implemented.
To get parameters passed from the Genero program to the front call, and return values from the front call to the Genero program, use the following macros and methods of the FrontCall class:
Macro / Method | Description |
---|---|
(void) FC_REQUIRED_PARAMS(count) |
Checks that the number of parameters passed by the Genero program equals count. This macro will raise an error in the Genero program if not enough parameters were passed. |
(NSString *) FC_PARAM(index) |
Get the string parameter passed to the front call, at the given position. If the parameters are of a different type, use the doubleValue, floatValue and integerValue methods on NSString or a NSScanner, to convert the parameter to the expected type. |
(int) FC_PARAM_INT(index) |
Get the int parameter passed to the front call, at the given position. |
(void) intResult:(int) intValue |
Ends the front call by returning one integer to Genero. |
(void) doubleResult:(double) doubleValue |
Ends the front call by returning one double to Genero. |
(void) stringResult:(NSString * ):stringValue* |
Ends the front call by returning one string to Genero. |
(void) startResult |
Initiate setting multiple result values. Must be followed by add* function calls and ended with endResult. |
(void) addIntResult:(int)intValue |
Add an integer to the list of results returned. To be used after a startResult call. |
(void) addDoubleResult:(double) doubleValue |
Add a double to the list of results returned. To be used after a startResult call. |
(void) addStringResult:(NSString * ) stringValue* |
Add a string to the list of results returned. To be used after a startResult call. |
(void) endResult |
Finalize the setting of multiple result values an return the results to the Genero program, with front call error code zero (i.e. success). |
(void) ok |
Ends the front call without returning any value to Genero, indicating that the front call execution was successful. |
(void) error(FCErrorCode):error |
Ends the front call with a specific front call error code defined in FCErrorCode enum in frontcall.h, to indicate that front call execution failed, typically because of invalid parameters or invalid function name. |
(void) errorWithMessage(NSString * )message* |
Ends the front call with front call return code -4 (maps to BDL error -6333), and a user-defined error message, that can be read with ERR_GET() in the Genero program. |
(void) willSetResultLater |
To be called at the end of the execute function, if result values are intended to be set after the execute function did return. If the willSetResultLater function is used, the current front call will not end until one of the result functions is called. For example, if your front call opens a message box, the execute function will return before one of the message box buttons are selected. Once a button is pressed, the front call result value is set. |
In the Genero program, use the ui.Interface.frontCall() API to call the front-end function. This method takes the front call module name as first parameter and the front call function name as second parameter.
The front call module name is defined by the string value returned from the -(NSString * ) moduleName* method of your front call implementation, and the front-call function name is passed to the execute method you implemented as first parameter (name).
For example, if you implement the following class:
#import <gmi/frontcall.h> ... @interface MyFrontCall : FrontCall ... @end @class MyFrontCall -(instancetype) initWithFunctionModuleHelper:(id)aHelper { if (self = [superinitWithFunctionModuleHelper:aHelper]) { ... } return self; } -(NSString*) moduleName{ return @"MyModule"; } -(void)execute:(NSString)name retCount:(int)retCount params:(NSArray)params { [super execute:name retCount:retCount params:params]; if ([[name lowercaseString]isEqualToString:@"myfrontcall"]) { ...
The Genero program code must pass the module name "MyModule" as front call module name and the class name "MyFrontCall" as front call function name:
CALL ui.Interface.frontCall("MyModule", "MyFrontCall", ["John DOE"],[msg])
[super execute:name retCount:retCount params:params];
The execute method must check the name of the front call function passed as parameter, to perform the expected code. This is the function name passed to the ui.Interface.frontCall() call in the Genero program:
if([[name lowercaseString] isEqualToString:@"myfunction"]) {Implement the body of the front call function in the if() block as follows:
assert(retCount == 2);
In order to get the parameters passed from the Genero program, use the FC_* macros in the body of your front call function.
FC_REQUIRED_PARAMS(3); ...
NSString * info = FC_PARAM(0); int v1 = [FC_PARAM(1) integerValue]; double v2 = [FC_PARAM(2) doubleValue];
Implement the actual code of the front call.
[self startResult]; [self addIntResult:isIpad]; [self addIntResult:canLocate]; [self endResult];
[self willSetResultLater];
Additionally, if you call the willSetResultLater method, you need to call one of the result methods like stringResult at a later time.
The complied Objective-C classes and the NSString frontCalls() function must be included in the iOS app build process.
The same app building rules apply for custome front calls as for C extensions.
See Building iOS apps with Genero for more details.
In this example, the ExtensionFrontCall class implements two front calls: "isipad" and "logindialog".
We start by defining the interface for the custom front call module:
@interface ExtensionFrontCall : FrontCall<UIAlertViewDelegate> @end
The ExtensionFrontCall class extends FrontCall, and implements the UIAlertViewDelegate protocol which is used by the "logindialog" front call.
@implementation ExtensionFrontCall -(instancetype) initWithFunctionModuleHelper:(id)aHelper { if (self = [superinitWithFunctionModuleHelper:aHelper]) { } return self; } -(NSString*) moduleName{ return @"ExtensionFrontCall"; } -(void)execute:(NSString)name retCount:(int)retCount params:(NSArray)params { [super execute:name retCount:retCount params:params]; ... } ... @end
We use the standard initializer which will be called by GMI on startup and define "ExtensionFrontCall" as module name by returning it from the moduleName method.
We also start the implementation of the execute method by calling the super method.
NSArray* frontCalls() { return @[ [ExtensionFrontCall class] ]; }This function is added above the interface implementation in the example, but could be defined in any file, as long as it is included in the project. If more than one module has to be defined, add the class names of the other modules to the returned array (e.g. return @[ [ExtensionFrontCall class] , [AnotherFrontCall class] ];)
if ([[name lowercaseString] isEqualToString:@“isipad”]) { assert(retCount == 1); BOOL isIpad = UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad; [self startResult]; [self addIntResult:isIpad]; [self endResult]; }
After checking that only one return parameter was defined in Genero, the code identifies the platform with the UI_USER_INTERFACE_IDIOM() API and stores the result in the isIpad variable.
The next three lines return the result value to Genero, by starting a result block with startResult, adding an int to the return set with addIntResult, and finally calling endResult to send the result to the Genero progra,.
We could also have used the single line: [self intResult:isIpad]; to achieve the same behavior, since we only return one result value.
DEFINE res INTEGER CALL ui.Interface.frontCall( "ExtensionFrontCall", "isipad", [ ], [res] )
if([[name lowercaseString] isEqualToString:@“logindialog”]) { assert(retCount == 2); FC_REQUIRED_PARAMS(2); NSString *title = FC_PARAM(0); NSString *message = FC_PARAM(1); UIAlertView *alert = [[UIAlertView alloc] initWithTitle:title message:message delegate:self cancelButtonTitle:NSLocalizedString(@"Cancel",@"Cancel") otherButtonTitles:NSLocalizedString(@"OK",@"OK"),nil]; alert.alertViewStyle = UIAlertViewStyleLoginAndPasswordInput; [alert show]; [self willSetResultLater]; }
We first check that two result values were set in Genero and that two parameters were supplied to the front call.
Then we use the FC_PARAM macro to fetch the parameters and assign them to NSStrings.
Then we allocate and initialize an UIAlertView with the given message and title and set the alertViewStyle to "UIAlertViewStyleLoginAndPasswordInput", so that one plain text field and one password field will be displayed on the alert.
In the initWithTitle call we also set "self" as the delegate of the alert so that we receive callbacks after user input (we had added the UIAlertViewDelegate protocol to our ExtensionFrontCall interface definition).
Finally, we call willSetResultLater, to keep the control flow in iOS. If we don’t call this function, GMI concludes the front call was not handled by the execute function (as none of the xxxResult functions was called inside), and the front call will fail with a “Frontcall not found” error message.
pragma mark UIAlertViewDelegate(void) alertView:(UIAlertView *)alertViewdidDismissWithButtonIndex:(NSInteger)buttonIndex { [self startResult]; if (buttonIndex != alertView.cancelButtonIndex) { [self addStringResult:[alertViewtextFieldAtIndex:0].text]; [self addStringResult:[alertViewtextFieldAtIndex:1].text]; } else { [self addStringResult:nil]; [self addStringResult:nil]; } [self endResult]; }
This method will be called after the user has tapped on one of the buttons and the view has been dismissed. Inside this method, we first call startResult to enable adding more than one return value.
If the tapped button was not the Cancel button, we add the values of the login and password fields as strings to the results and then call endResult to return the control flow to the Genero program.
DEFINE ul, up STRING CALL ui.Interface.frontCall( "ExtensionFrontCall", "logindialog", ["MyApp","User login"], [un, up] ) IF up IS NULL THEN ERROR "Login canceled" EXIT PROGRAM END IF