iOS Externals SDK
Extend LiveCode's iOS feature set using lower level languages

Contents

1. Introduction
2. Requirements
3. Installation
4. Getting Started
5. Sample External Projects
6. Interface Definition Language
7. Libraries and Frameworks
8. API
9. Objective-C Naming Requirements
10. Build Process Details
11. Compatibility
12. Troubleshooting

 

1. Introduction

LiveCode provides an iOS externals interface which allows you to extend the existing iOS feature set using a lower level language like C, C++ or Objective C.

THIS RELEASE IS FOR IOS EXTERNALS ONLY

This SDK does not replace the existing LiveCode externals interface for the desktop and web platforms.

You can also extend LiveCode functionality on the desktop platform. For more details see this 'externals tutorial series' here.

 

2. Requirement

Before installing the SDK ensure that your system complies with the following requirements:

  • MacOS 10.6 or higher
  • LiveCode 4.6.2 or higher
  • XCode 4.2
  • iOS5 SDK

 

3. Installation

First download the latest version of the iOS SDK:

http:www.runrev.com/downloads/livecode/sdk/LiveCodeSDK-R10.zip

Warning: Do not open any of the XCode projects before running LiveCode SDK Support.mpkg

  1. Unzip the SDK containing:
    • ChangeLog.txt
    • LiveCode SDK Support.mpkg
    • 6 example external projects
  2. Run 'LiveCode SDK Support.mpkg'.
    The installer will add a template externals project to your XCode installation. This will appear in your XCode project wizard and provide a '1 click' option for creating a new LiveCode iOS external project.

 

4. Getting Started

4.1 Video tutorial

The video below provides a step by step guide to creating an iOS external for LiveCode. It guides you through the XCode setup process, coding, compilation and integration of the final external into a LiveCode project.

 

 

4.2 Creating a new project

To create a new external, use the 'New Project…' feature of Xcode and select the appropriate template. This creates a project with the following files:

  • .ios
    The list of framework and library dependencies required by the external.
  • .lcidl
    The interface definition for the external.
  • .mm
    The main source file for the external - this can be changed to c/cpp or objective-c (m); 'mm' is just the best of both worlds as its obj-c++.
  • .livecode
    An empty test stack.
  • test-Info.plist
    The plist used in the test target.

The project has two targets:


  • The target that builds the native code block usable by the engine.
  • test
    An application target that uses pre-built iOS standalones installed by SDK Support to enable easy testing/debugging of the target.

At this time it is important that two points are true:

  1. The external name (target name) must be lower-case.
  2. The name specified in the 'external <…>' clause in the IDL file must match the target name ().

Warning: Failure to observe either of these points will result the external not working.

 

5. Sample External Projects

The SDK comes with a number of sample external projects which have been documented through extensive source code comments. Each project folder contains:

  1. The source code for the external
  2. An XCode project file
  3. A pre-compiled external
  4. A LiveCode test stack

The LiveCode test stacks automatically load the pre-compiled externals allowing you to try out the functionality immediately. Simply load the test stack in LiveCode and test it using the simulator.

 

5.1 rreMicrophone

Audio recording using AVAudioRecorder.

  • Please make sure to check your system preferences and select a valid microphone input.
  • Click 'Start' to begin the recording process (A temporary file is created in the apps 'documents' folder).
  • Click 'Stop' to end the recording.
  • Click 'Play' to playback your recording.

 

5.2 rreNarrator

Text-to-speech using 'flite' (CMU speech synthesis project).

  • Button Speak: Blocks the caller completely and does all processing on main thread meaning nothing else runs at the same time (same as doing wait until... without messages).
  • Button 'Speak Without Messages': Blocks the caller but allows low-level engine event processing to occur - i.e. the animated gif still functions.
  • Button 'Speak With Messages': Blocks the caller but does normal message dispatch - i.e. you can still press buttons and execute other actions (same as doing wait until... with messages).
  • Button 'Speak In Background': Does not block the caller. Instead a callback 'narratorFinished' / 'narratorError' is sent to the button when the task is completed.

 

5.3 rreSocket

Simple client-side socket implementation using NSStreams.

  • Demonstrates HTTP Get to www.runrev.com.

 

5.4 rreMail

Simple example demonstrating use of modal view controller.

  • Fill out your details and click compose. iOS will popup the default 'compose' window with the users inputs ready to send.

 

5.5 rreHardcopy

Exports the visible card to PDF and sends it to the printer over airprint.

  • Uses the rect of the target screen to position the popover via a LCObjectGet call.
  • Button 'Is Available': Checks to see if the airprint facility is available on the host device.
  • Button 'Print': Creates a PDF of the current screen and sends it to the printer via airprint.

 

5.6 rreCanvas

Demonstrates the use of LCImage calls to 'takeover' an image. This example implements a simple 2d vector path drawing API, and example stack renders some example SVG images.

  • Pulldown: Select an image to render.

 

6. Interface Definition Language

Each external should have a corresponding interface definition contained in the .lcidl file. The interface definition details the functions, commands and types the external will make available to the programmer and will be specified using the LiveCode Interface Definition Language (IDL). A complete interface definition greatly simplifies implementing externals as all 'type conversions' and error checking on entry to a function is handled automatically. Upon building the external, the interface definition will be compiled and the appropriate hooks will be created.

6.1 Syntax

The syntax of the IDL is as follows:

interface
: 'external' ID
{ use-clause }
{ hook-clause }
{ definition }

use-clause
: 'uses' USE-ID

hook-clause
: 'on' HOOK-ID 'call' NATIVE-ID

definition
: enum-definition
| command-definition
| function-definition

enum-definition
: 'enum' ID
{ STRING as INTEGER }

command-definition
: 'command' ID
{ parameter-definition }
[ return TYPE-ID ]
[ call NATIVE-ID ]

function-definition
: 'function' ID
{ parameter-definition }
{ optional-parameter-definition }
[ return TYPE-ID ]
[ call NATIVE-ID ]

parameter-definition
: ( 'in' | 'out' | 'inout' ) ID 'as' TYPE-ID

optional-parameter-definition
: 'optional' 'in' ID 'as' TYPE-ID 'default' ( STRING | NUMBER )

 

6.2 External Name

The first entry within the IDL file should be the external name. As before, this must be all lowercase and in the following format:

external rrecanvas

 

6.3 Use Clause

Typically, after the external name, any options or use clauses will be defined. Here 'USE-ID' describes a particular option to use when generating interface hooks. The following are currently understood:

  • c++-exceptions - the external will throw C++ exceptions which should be caught and translated to LiveCode exceptions
  • c++-naming - the native methods are exported using C++ name mangling
  • objc-exceptions - the external will throw obj-c exceptions which should be caught and translated to LiveCode exceptions (discouraged)
  • objc-objects - the native methods use objc-objects and need an associate auto-release pool to manage them

If you are writing your code purely in Objective-C (.m) of C (.c) no extra directives are required.  C++ naming in only required if you are using Objective-C++ (.mm) or C++ (.cpp) where the C++ compiler is adding additional info to function names to take into account overloading etc.

The following is and example use clause:

use c++-naming

 

6.4 Hook Clause

Here 'HOOK-ID' describes non-handler related hooks that the external needs to be notified of. The following are currently understood:

  • startup - the given native method will be called when the application starts up. The signature of the hook should be:
    bool (void)
  • shutdown - the given native method will be called when the application shuts down. The signature of the hook should be:
    void (void)

The following is and example hook clause:

on startup call rreMicrophoneStartup

 

6.5 Command & Function Definition

Once we have defined the external names and any clauses required, we are ready specify the functions and commands. Here 'ID' and 'NATIVE-ID' describe native code function names.

The following is and example command definition:

command rreMail

 

6.6 Parameters & Return Values

Each function and command can have a set of parameters and return a value. Here 'TYPE-ID' describes a type. It can be one of:

  • boolean - converted to/from a native bool type.
  • c-string - converted to/from a native char *'referencing a NUL terminated string.
  • c-data - converted to/from a native LCBytes * type describing a block of memory of arbitrary length.
  • integer - converted to/from a native int type.
  • real - converted to/from a native double type.
  • objc-string - converted to/from a native NSString * object.
  • objc-number - converted to/from a native NSNumber * object.
  • objc-data - converted to/from a native NSData * object.
  • any id defined as an enum - converted to/from a native int type, as mapped through the list of strings provided for the enum.

Binding to native methods provides three parameter 'modes':

  • in - the value is provided to the function and no return is expected.
  • out - on entry, the parameter is initialized to nil and on exit any value that is present is copied into the LiveCode variable.
  • inout - the current value of the LiveCode variable is provided to the function on exit, and upon exit the value is copied by to it.

Both out and inout parameter modes map to 'reference' parameters in the native methods. When c++-naming is used this corresponds to a '&' parameter, otherwise an extra level of indirection is used (i.e. an extra '*'). For example:

command foobar
  in myInParameter as double
  out myOutParameter as c-string
  inout myInOutParameter as objc-string


Would require the following native function signatures:

  • c++-naming:
    void foobar(double myInParameter, char*& myOutParameter, NSString*& myInOutParameter)
  • not c++-naming:
    void foobar(double myInParameter, char ** myOutParameter, NSString** myInOutParameter)

     

The following rules must be observed for in mode:

  • c-string - the pointer passed to the native method is const char * and its contents must not be modified.
  • c-data - the pointer passed to the native method is const LCBytes * and the memory pointed to by the buffer element must not be modified.

The following rules must be observed for the out mode:

  • c-string - the pointer passed back must have been allocated with malloc/calloc/realloc, and ownership passes to the engine.
  • c-data - the pointer passed back in the buffer element must have been allocated with malloc/calloc/realloc, and ownership passes to the engine.
  • objc-* - the object passed back should be in the current auto-release pool if the external no longer needs it.

The inout mode is a combination of the rules for in and out. In particular, in the c-* case, the input buffer should not be modified, and a new buffer should be provided on exit if it has changed.

 

6.7 Enumerative Types

You can also specify in the interface definition any enumerative types your external will use. An example enumeration is as follows:

enum print-status-enum
  "" as 0
  "printing not available" as 1
  "file does not exist" as 2
  "cannot print that file" as 3
  "printing canceled" as 4
  "printing failed" as 5

 

7. Libraries and Frameworks

Any system frameworks and libraries (i.e. .framework / .dylib files) should be referenced in the .ios file. For example, if you use libz and AVFoundation you would write:

framework AVFoundation
library z

You must not attempt to use any third-party frameworks or dylibs, Apple will not allow apps in the AppStore which attempt to load third-party frameworks - all code in an app not provided by iOS must be statically linked into the main executable.

 

8. API

LCExceptionRaise
LCObjectExists
LCObjectRetain
LCObjectRelease
LCObjectSend
LCObjectPost
LCObjectGet
LCObjectSet
LCContextMe
LCContextTarget
LCContextDefaultStack
LCContextDefaultCard
LCWaitCreate
LCWaitRetain
LCWaitRelease
LCWaitIsRunning
LCWaitRun
LCWaitBreak
LCWaitReset
LCImageAttach
LCImageDetach
LCImageDescribe
LCImageDescribeMask
LCImageUpdate
LCImageUpdateRect
LCRunOnMainThread
LCPostOnMainThread
LCInterfaceQueryView
LCInterfaceQueryViewScale
LCInterfacePresentModalViewController
LCInterfaceDismissModalViewController

LCExceptionRaise

Description

Set the error to throw on return from the current external handler.

The thrown string is constructed using the printf-style formatting string 'format' and any subsequent arguments as appropriate.

Parameters (in) format - const char *
Context Safety Must be called from handler context.

 

LCObjectExists

Description

Checks to see if the object referred to by the handle 'object' still exists. The 'r_exists' parameter is set to 'true' if it is still there, or 'false' otherwise.

Parameters (in) object - LCObjectRef
(out) r_exists - bool
Errors NoObject - the 'object' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCObjectRetain

Description

Increase the reference count of the handle passed in 'object'.

Parameters (in) object - LCObjectRef
Errors NoObject - the 'object' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCObjectRelease

Description

Decrease the reference count of the handle passed in 'object'. If the reference count reaches zero, the handle is destroyed and is no longer valid.

Parameters (in) object - LCObjectRef
Errors NoObject - the 'object' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCObjectSend

Description

Sends 'message' to 'object' with the given parameters. The caller blocks until the message has been handled.

The parameter list is constructed based on the 'signature' c-string. Each character in the signature determines the type of the subsequent arguments and is used to convert them to a form suitable for LiveCode script.

The characters that are currently understood are: 'b' - the parameter is of type 'bool', converts to 'true' or 'false' 'i' - the parameter is of 'int' type, converts to a number 'r' - the parameter is of 'double' type, converts to a number 'z' - the parameter is of 'c-string' type, converts to a (text) string 'y' - the parameter is of 'c-data' type, converts to a (binary) string 'N' - the parameter is of 'NSNumber*' type, converts to a number 'S' - the parameter is of 'NSString*' type, converts to a (text) string 'D' - the parameter is of 'NSData*' type, converts to a (binary) string

The parameters appear in the resulting LiveCode message in the same order that they appear in the signature.

The 'z' type should be passed a 'const char *' (zero-terminated) string. The 'y' type should be passed a 'const LCBytes *' type.

Parameters (in) object - LCObjectRef
(in) message - const char *
(in) signature - const char *
(in) ... - variadic argument list
Errors OutOfMemory - memory ran out while attempting to perform the operation.
NoObject - the 'object' parameter was nil.
NoObjectMessage - the 'message' parameter was nil.
ObjectDoesNotExist - the object handles target no longer exists.
ScriptExited - the message caused 'exit to top' to be called.
ScriptFailed - the message caused an error to be thrown.
Context Safety Must be called in dispatch context.

 

LCObjectPost

Description

Appends an event to the internal event queue. When the event is dispatched 'message' is sent to the target object with the given parameters.

Note: Posting from an auxiliary thread will block until the main thread reaches a suitable point to process the request and schedule the event.

Parameters (in) object - LCObjectRef
(in) message - const char *
(in) signature - const char *
(in) ... - variadic argument list
Errors OutOfMemory - memory ran out while attempting to perform the operation.
NoObject - the 'object' parameter was nil.
NoObjectMessage - the 'message' parameter was nil.
ObjectDoesNotExist - the object handles target no longer exists.
Context Safety May be called in universal context.

 

LCObjectGet

Description

Fetches the value of the given (array) property from the specified object and returns the value in the way specified by 'options'. The usage of options and values is as described in the section on 'values'.

This method works in an identical way to property fetching in script.

i.e. LCObjectGet(object, ..., property, NULL, value) is the same as: put the of into

 

i.e. LCObjectGet(object, ..., property, key, value) is the same as: put the [] of into

 

Note that just like the script analogs, if is not an engine property custom properties are used (with getprop messages dispatched as needed), and thus the current custom property set of the object comes into effect when required.

Parameters (in) object - LCObjectRef
(in) options - unsigned int
(in) property - const char *
(in) key - const char *
(out) value - depends on options
Errors OutOfMemory - memory ran out while attempting to perform the operation.
NoObject - the 'object' parameter was nil.
NoObjectProperty - the 'property' parameter was nil.
NoObjectPropertyValue - the 'value' parameter was nil.
ObjectDoesNotExist - the object handles target no longer exists.
Exited - the message caused 'exit to top' to be called.
Failed - the message caused an error to be thrown.
NotABoolean - the value was requested as a boolean, and it is not a boolean.
NotANumber - the value was requested as a number, and it is not a number.
NotAnInteger - the value was requested as an integer, and it is not an integer.
NotABinaryString - the value was requested as binary data, and it is not binary data.
NotAString - the value was requested as a string, and it is not a string.
NotAnArray - the value was requested as an array, and it is not an array.
Context Safety Must be called in dispatch context.

 

LCObjectSet

Description

Sets the value of the given (array) property to the given value in the way specified by 'options'. The usage of options and values is as described in the section on 'values'.

This method works in an identical way to property setting in script.

i.e. LCObjectSet(object, ..., property, NULL, value) is the same as: set the of to

 

i.e. LCObjectSet(object, ..., property, key, value) is the same as: set the [] of to

 

Note that just like the script analogs, if is not an engine property custom properties are used (with setprop messages dispatched as needed), and thus the current custom property set of the object comes into effect when required.

Parameters (in) object - LCObjectRef
(in) options - unsigned int
(in) property - const char *
(in) key - const char *
(out) value - depends on options
Errors OutOfMemory - memory ran out while attempting to perform the operation.
NoObject - the 'object' parameter was nil.
NoObjectProperty - the 'property' parameter was nil.
NoObjectPropertyValue - the 'value' parameter was nil.
ObjectDoesNotExist - the object handles target no longer exists.
Exited - the message caused 'exit to top' to be called.
Failed - the message caused an error to be thrown.
NotABoolean - the value was requested as a boolean, and it is not a boolean.
NotANumber - the value was requested as a number, and it is not a number.
NotAnInteger - the value was requested as an integer, and it is not an integer.
NotABinaryString - the value was requested as binary data, and it is not binary data.
NotAString - the value was requested as a string, and it is not a string.
NotAnArray - the value was requested as an array, and it is not an array.
Context Safety Must be called in dispatch context.

 

LCContextMe

Description

Returns a weak object handle to object's whose script invoked the external handler.

The caller is responsible for calling 'LCObjectRelease' on the handle when it is no longer needed.

Parameters (out) r_me - LCObjectRef
Errors OutOfMemory - memory ran out while attempting to perform the operation.
Context Safety Must be called from handler context.

 

LCContextTarget

Description

Returns a weak object handle to 'the target'.

The caller is responsible for calling 'LCObjectRelease' on the handle when it is no longer needed.

Parameters (out) r_target - LCObjectRef
Errors OutOfMemory - memory ran out while attempting to perform the operation.
Context Safety Must be called from handler context.

 

LCContextDefaultStack

Description

Returns a weak object handle to 'the defaultStack'.

The caller is responsible for calling 'LCObjectRelease' on the handle when it is no longer needed.

Parameters (out) r_default_stack - LCObjectRef
Errors OutOfMemory - memory ran out while attempting to perform the operation.
Context Safety Must be called from handler context.

 

LCContextDefaultCard

Description

Returns a weak object handle to 'this card of the defaultStack'.

The caller is responsible for calling 'LCObjectRelease' on the handle when it is no longer needed.

Parameters (out) r_default_card - LCObjectRef
Errors OutOfMemory - memory ran out while attempting to perform the operation.
Context Safety Must be called from handler context.

 

LCWaitCreate

Description

Creates and returns a handle to a 'Wait' object, allowing pausing of script execution in both blocking or dispatching modes.

The caller is responsible for callback 'LCWaitDestroy' on the handle when it is no longer needed.

Parameters (in) options - unsigned int
Errors OutOfMemory - memory ran out while attempting to perform the operation.
Context Safety Must be called from handler context.

 

LCWaitRetain

Description

Increases the reference count for the given wait object.

Parameters (in) wait - LCWaitRef
Errors NoWait - the 'wait' parameter was nil.
Context Safety May be called in any context from any thread.

 

LCWaitRelease

Description

Reduces the reference count for the given wait object, destroying it if the reference count reaches zero.

Parameters (in) wait - LCWaitRef
Errors NoWait - the 'wait' parameter was nil.
Context Safety May be called in any context from any thread.

 

LCWaitIsRunning

Description

Returns 'true' if the wait object is currently running, false otherwise.

Note: Although this call is thread-safe, the return value may be immediately invalid if the wait object finishes just after its current state is fetched.

Parameters (in) wait - LCWaitRef
(out) running - bool
Errors NoWait - the 'wait' parameter was nil.
Context Safety May be called in any context from any thread.

 

LCWaitRun

Description

Blocks the caller's execution and runs the main application event loop until either 'LCWaitBreak' is called on the wait object, or the wait is aborted (due to application exit). In the latter case, 'kLCWaitAborted' is returned.

If 'LCWaitBreak' has already been called on the wait object LCWaitRun returns without running the event loop.

To re-use the wait object after it has been run, use LCWaitReset.

Parameters (in) wait - LCWaitRef
Errors NoWait - the 'wait' parameter was nil.
WaitRunning - the 'wait' object is already running.
WaitAborted - the 'wait' ran but was aborted rather than broken.
Context Safety Must be called from handler context.

 

LCWaitBreak

Description

Marks the given wait object as being 'broken' and interrupts the event loop if LCWaitRun was invoked on the wait.

Parameters (in) wait - LCWaitRef
Errors NoWait - the 'wait' parameter was nil.
Context Safety May be called in any context from any thread.

 

LCWaitReset

Description

Resets the wait object so that it can be run again.

Parameters (in) wait - LCWaitRef
Errors NoWait - the 'wait' parameter was nil.
WaitRunning - the 'wait' object is already running.
Context Safety Must be called from handler context.

 

LCImageAttach

Description

Creates and attaches rasters to the given image object allowing direct access to the bits of the main (color) raster, and it's mask (if any).

The attachment locks the size of the image leaving complete control of the contents to the external. When detached, the image returns to its previous state (including contents).

Parameters (in) object - LCObjectRef
(in) options - unsigned int
(out) r_image - LCImageRef
Errors OutOfMemory - there was not enough memory to complete the operation.
ObjectDoesNotExist - the given object no longer exists.
NotAnImageObject - the given object is not an image object.
Context Safety Must be called from dispatch context.

 

LCImageDetach

Description

Detaches from an image, previously attached to using LCImageAttach.

Parameters (in) image - LCImageRef
Errors NoImage - the 'image' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCImageDescribe

Description

Returns the details of the image's main raster, allowing access to the RGB values that make up the image.

The raster is always in the native format provided by the engine on the current platform.

On iOS this is 8-bit components, organized in memory as RGBx where the last byte is ignored.

Parameters (in) image - LCImageRef
(out) r_raster - LCImageRaster
Errors NoImage - the 'image' parameter was nil.
Context Safety Can be called from any context.

 

LCImageDescribeMask

Description

Returns the details of the image's mask raster, allowing access to the 1-bit mask that controls transparency of the image.

The raster is always in the 1-bit gray format.

Parameters (in) image - LCImageRef
(out) r_raster - LCImageRaster
Errors NoImage - the 'image' parameter was nil.
Context Safety Can be called from any context.

 

LCImageUpdate

Description

Mark the image object to which 'image' is attached as needing a redraw.

Parameters (in) image - LCImageRef
Errors NoImage - the 'image' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCImageUpdateRect

Description

Mark the image object to which 'image' is attached as needing a redraw but only within the specified rectangle. The co-ordinates are relative to the top-left of the image.

Parameters (in) image - LCImageRef
Errors NoImage - the 'image' parameter was nil.
Context Safety Must be called from dispatch context.

 

LCRunOnMainThread

Description

Executes the given callback on the main (engine) thread as soon as is possible.

If the call is made from the main thread and 'Wait' is passed as an option, the callback is invoked before LCRunOnMainThread returns; otherwise the callback is scheduled to run at the next invocation of the run loop (cf. send ... to me in 0 millisecs).

If the call is made from an auxiliary thread then the callback is scheduled on the main thread's run loop to be executed at the next possible opportunity. If 'Wait' is specified in this case, the caller will block until the main thread has executed the callback; otherwise the caller will block only until the request has been posted.

When invoked, the context of the callback is native - only certain API calls can be made.

Parameters (in) options - unsigned int
(in) callback - LCRunOnMainThreadCallback
(in) state - void *
Errors OutOfMemory - there was not enough memory to service the request.
Context Safety May be called in any context.

 

LCPostOnMainThread

Description

Posts an event to the event queue that when dispatched causes the given callback to be invoked. If the event is never reached (due to application exit), the callback is still invoked, but with the 'canceled' flag this allows the callback to clean-up any dynamic state associated with it.

The call will block until the request has been successfully posted to the event queue and then return.

When invoked, the callback is executed in dispatch context - all but handler context requiring API calls can be made.

Parameters (in) options - unsigned int
(in) callback - LCPostOnMainThreadCallback
(in) context - void *
Errors OutOfMemory - there was not enough memory to service the request.
Context Safety May be called in any context.

 

LCInterfaceQueryView

Description

Returns the UIView for the currently visible stack that makes up the main view of the application.

Parameters (out) r_view - UIView *
Errors (none)
Context Safety May be called in any context on the main thread.

 

LCInterfaceQueryViewScale

Description

Returns the multiplier currently applied to LiveCode co-ordinates when mapping to and from UIKit co-ordinates. In particular, if running on a device with a Retina display, and 'UseDeviceResolution' is true, the scale with be 2.0, otherwise it will be 1.0.

Parameters (out) r_scale - double
Errors (none)
Context Safety May be called on any context on the main thread.

 

LCInterfacePresentModalViewController

Description

Presents the given view controller modally, with or without animation as specified by the 'animated' parameter.

This call corresponds to doing: [ presentModalViewController: controller animated: animated]

Parameters (in) controller - UIViewController *
(in) animated - bool
Errors (none)
Context Safety Must be called from handler context.

 

LCInterfaceDismissModalViewController

Description

Dismisses the given view controller that was previously presented using LCInterfacePresentModalViewController. The dismissal is performed with or without animation, depending on the value of the 'animated' parameter.

This call corresponds to doing: [ dismissModalViewController: controller animated: animated]

Parameters (in) controller - UIViewController *
(in) animated - bool
Errors (none)
Context Safety Must be called from handler context.

 

9. Objective-C Naming Requirements

It is important to be aware that when writing externals in objective-c that it does not have any notion of 'namespaces' - all classes and protocols (and category methods) exist in a flat namespace; this means that if care is not taken conflicts can easily occur - it is not possible to create such things that are 'local' to a particular block of code.

This is particularly problematic in the realm of application extensions or plugins (which externals are). If they have classes with identical names, then it is not possible to use them together - iOS applications must be statically linked, or the application may not be linked!

To resolve this we require that all classes (and protocols) that are used within LiveCode externals must follow a strict naming scheme. All names must be prefixed by a reverse domain pertinent to the producer. e.g.

com_runrev_rremicrophone_MicrophoneDelegate
com_foobar_mycoolexternal_SomeObject

Objective-C provides some support to make this easier (i.e. to stop having to type long identifiers!). Define a class like this:

@interface com_foobar_mycoolexternal_SomeObject : NSObject

@end
@compatibility_alias SomeObject com_foobar_mycoolexternal_SomeObject;
@implementation SomeObject

@end

Anything after the 'compatibility_alias' line can use SomeObject instead of the more lengthy identifier.

There is no 'built-in' (syntax level) support for protocol definitions, so a define must be used instead:

@protocol com_foobar_mycoolexternal_SomeProtocol

@end
#define SomeProtocol com_foobar_mycoolexternal_SomeProtocol

Category methods must also be named uniquely in the same way. A category method is one that is 'added' to an existing class augmenting its behavior - they aren't global, but are 'global' within a given class's method table hence the need for disambiguation. Again, there is no direct support for shortening names, so defines can be used (albeit with care!):

@interface NSString (com_foobar_mycoolexternal_StringStuff)
- (bool) com_foobar_mycoolexternal_superCoolStringOperation: (int)foo
@end
#define StringStuff com_foobar_mycoolexternal_StringStuff
#define superCoolStringOperation com_foobar_mycoolexternal_superCoolStringOperation
@implementation NSString (StringStuff)
- (bool) superCoolStringOperation: (int)foo
{

}
@end

 

10. Build Process Details

Any code files that should be compiled into the external should be added to the target - this includes any static libraries the external may use (see rrenarrator for an example of this). The output of the target is two-fold:

  1. A '.dylib' file which is used when running through Xcode on a device; or when running in the simulator either in the IDE or through Xcode.
  2. An '.lcext' file which is a partially linked object file. This is used by the standalone builder when producing device builds. It contains all the non-system code required by the external, and is built so that the only symbols that are exported are the hooks required by the engine.

Non-code files that are needed for testing/debugging the app in the simulator / on the device should be added to the 'test' target as a Copy Files/Resources phase (see rrecanvas for an example of this).

The two targets themselves rely on two 'support' shell scripts that are run as last phases on the target (Build External / Build Test).

The 'Build External' phase of target calls the 'lclink.sh' script. This builds the '.dylib' form and the '.lcext' form of the external and ensures that the dependency information (provided by the .ios file) is embedded for access by the standalone builder. It also copies the products into the 'binaries' folder for easy interoperation with the IDE (i.e. just point a 'Copy Files' entry to the folder and the external will be automatically included in iOS builds).

The 'Build Test' phase of the 'test' target constructs an app using some precompiled binaries which are part of the SDK. This results in an iOS standalone which loads the external and runs the test stack - this makes it trivial to run the external in any simulator, or on any device through Xcode. In particular - it enables easy debugging of the native code using breakpoints.

Both these scripts rely on various 'User-Defined' build settings that are present in the project.

The 'test' target relies on the following (both defined at the project level, but could be defined at the 'test' target level instead):

  • LIVECODE_TEST_EXTERNAL - the name of the external target that should be loaded - by default this is , but should be changed if additional targets are added or the name of the external changed.
  • LIVECODE_TEST_STACK - the stack to build into the test app for debugging - by default this is .livecode but could be changed as necessary.

The '' target relies on the following (defined at the target level):

  • LIVECODE_DEP_FILE - the file to use for dependency information - by default this is .ios

At the project level there is also a LIVECODE_SDKROOT setting. This describes the path to the SDK support files (~/Application Support/RunRev/Components/LiveCodeSDK) and is used in the following places:

  1. At the project level the 'Header Search Paths' option is defined to be "$(LIVECODE_SDKROOT)/headers" - this allows source-files to find the LiveCode.h file.
  2. In both the lclink.sh and lclinktest.sh scripts, which use it to find the support files needed by the SDK.

Note: If a target / project needs additional header file paths than its important the LiveCode header path not be eliminated by mistake. Either define at the target level and use $(inherited) or be careful just to them along-side at the project level. (See rrenarrator for an example).

 

11. Compatibility

We only guarantee source compatibility between engine versions. This means that future engines may not be able to load/work with compiled externals built using current SDK releases - to work in a future engine, it might be necessary to re-compiled externals with newer SDK release that accompany the engines.

 

12. Troubleshooting

12.1 When I try to build one of the sample external projects in XCode I get the following error:
"warning: no rule to process file '$(PROJECT_DIR)/rrenarrator.lcidl' of type text for architecture armv6".

This warning is most likely due to the fact that the project file was opened in XCode before the LiveCode SDK installer was run. XCode has changed the file type of the lcidl file to 'text' from the original 'sourcecode.livecode.idl'. This is because XCode was not aware of the new filetype before the SDK installer was run.

If you have not already run the SDK installer run it now. See installation instructions above. Once installed perform either 1) or 2) to fix the XCode compilation issue.

  1. Change the filetype of the lcidl source file to 'sourcecode.livecode.idl. This can be done via the properties palette of the file.
  2. Re-download the SDK to get a fresh copy of the project file.