Codea icon Objective-C

Reference › Interfacing with Objective-C code

Guides

Using Native APIs top ↑

Overview

Use objc to call Objective-C classes from Lua. This gives your project access to system features beyond Codea’s built-in drawing, input, and sound commands.

Start small

Look up a class, create an object, and call its methods. Check the method’s parameter types: Lua values, native objects, selectors, and structs are passed in different ways. See Objective-C for conversions and examples.

Present native controls from the active view controller. Keep callback objects alive while native code uses them, and release observers or delegates when the project stops. System permissions and entitlements still apply.

Objective-C

objc top ↑

Module

Use Objective-C classes and system features from Lua.

Native Classes

To access a native Objective-C class, append its name to objc.

-- access the UIScreen class
UIScreen = objc.UIScreen

Constructor

If the native class has a new constructor, you can invoke it directly instead of calling the new method.

-- create an instance of NSDateComponents
dateComponents = objc.NSDateComponents()

Properties

Properties can be read and modified directly.

-- read the screen brightness
brightness = objc.UIScreen.mainScreen.brightness

-- change the screen brightness
objc.UIScreen.mainScreen.brightness = 0.5

Methods

Methods are invoked using the ‘:’ operator and their full selector name, including named arguments separated by underscores ‘_’ instead of colons. Note that the Objective-C method names must always end with an underscore in Codea. The underscores are required for Codea to find the corresponding signatures in Objective-C.

-- Calling a method with multiple arguments
controller:presentViewController_animated_completion_(newController, true, nil)

-- Calling a method with no argument
webView:goBack_()

Callbacks

Callbacks such as completion handlers with up to 7 arguments are supported by passing a function where each parameter is prefixed to indicate the corresponding native type.

The following prefixes are supported for callback arguments:

  • c: char
  • i: int, NSInteger
  • uc: unsigned char
  • ui: unsigned int, NSUInteger
  • f: float
  • d: double, CGFloat
  • b: bool, BOOL
  • s: char*
  • o: NSObject, NSString, etc.

Note that only the first one or two characters are important in your argument names. bGranted and boolGranted will both work for a boolean argument, but for our examples, we decided to go with the second option.

Struct members are passed separately. For example, for an NSRange argument, you could use intLocation and intLength arguments.

Pointers for value types are also supported by prefixing them with p, (e.g. pboolStop). The actual value can be accessed and modified using .value.

Note that when using objc from within a callback, changes are not guaranteed to occur on the main thread. Consider using objc.async inside your callback if you need changes to happen on the main thread, such as modifying UIControls.

Automatic Conversions

Some Codea types will be converted to corresponding Objective types automatically when passed to native methods or properties.

  • color: UIColor
  • vec2: CGPoint
--- uiView: objc.UIView
uiView.backgroundColor = Color(255, 0, 0)

--- uiTextView: objc.UITextView
uiTextView:setContentOffset_(vec2(0, 100))

Conversions also happen in the other direction: a UIImage or UIColor returned from a native method arrives in Lua as a Codea image or color.

Disabling automatic conversion

Because the conversion happens on the way into Lua, a returned UIImage has already become a Codea image and can no longer be configured. Set objc.automaticBridging to false to receive the native object instead.

objc.automaticBridging = false

local native = objc.UIImage:systemImageNamed_("globe")
native = native:imageWithTintColor_(color(255, 0, 0))

objc.automaticBridging = true

img = objc.bridge(native)   -- convert it now

objc.bridge(value) performs the conversion explicitly, whatever the current setting is.

Prefer objc.withoutBridging(f), which restores the previous setting afterwards — including when f raises an error, where a bare flag would stay switched off for the rest of the session.

img = objc.withoutBridging(function()
    local native = objc.UIImage:systemImageNamed_("globe")
    native = native:imageWithTintColor_(color(255, 0, 0))
    return objc.bridge(native)
end)

Note

For SF Symbols specifically, prefer image.symbol(), which needs no objc at all.

Examples

Change the screen brightness to 50%

objc.UIScreen.mainScreen.brightness = 0.5

Request application review

scene = objc.app.keyWindow.windowScene
store = objc.SKStoreReviewController
store:requestReviewInScene_(scene)

Request notifications permission

center = objc.
    UNUserNotificationCenter.
    currentNotificationCenter
options = objc.enum.UNAuthorizationOptions
center:requestAuthorizationWithOptions_completionHandler_(
    options.badge | options.sound | options.alert,
    function(boolGranted, objError)
    if boolGranted then
        print("granted")
    elseif objError then
        print("error " .. objError.code)
    end
    end)

Enumerate words until the word stop is found

txt = "First second stop third fourth"
str = objc.string(txt)
str:enumerateSubstringsInRange_options_usingBlock_(
    objc.range(0, txt:len()),
    objc.enum.NSStringEnumerationOptions.byWords,
    function(objWord,
            isubstringLocation,
            iSubstringLength,
            iEnclosingLocation,
            iEnclosingLength,
            pboolStop)
        if objWord == "stop" then
            pboolStop.value = true
        else
            print(objWord)
        end
    end)

objc.delegate(name)

Function

Syntax

objc.delegate("DelegateName")

Returns a type which can be instantiated and used as an Objective-C delegate for the specified type.

Parameters

name string.

The name of the delegate.

Returns

table

A type to be used as an Objective-C delegate.

UITextViewDelegate Example

Delegate = objc.delegate("UITextViewDelegate")

function Delegate:textViewShouldBeginEditing_(objTextView)
    -- replace with false to prevent editing
    return true
end

function Delegate:textViewDidChange_(objTextView)
    print(objTextView.text)
end

-- uiTextView.delegate = Delegate()

WKScriptMessageHandler Example

-- Exposes the following method to JavaScript:
-- window.webkit.messageHandlers.log.postMessage
Handler = objc.delegate("WKScriptMessageHandler")

function Handler:
    userContentController_didReceiveScriptMessage_(
    objUserContentController, objMessage)
    print(objMessage.body)
end

function setup()
    local ctrl = objc.WKUserContentController()
    local logHandler = Handler()
    ctrl:addScriptMessageHandler_name_(
    logHandler, "log")
end

objc.class(name)

Function

Syntax

objc.class("ClassName")

Returns a type which can be instantiated and used as an Objective-C class, for example combined with a selector when registering for notifications through the NSNotificationCenter.

Parameters

name string.

The name of the class.

Returns

table

A type to be used as an Objective-C class.

NotificationHandler Example

NotificationHandler = objc.class("NotificationHandler")

function NotificationHandler:textDidChange(objNotification)
    print(objNotification.object.text)
end

handler = NotificationHandler()

notificationCenter = objc.NSNotificationCenter.defaultCenter
notificationCenter:addObserver_selector_name_object_(
    handler,
    objc.selector("textDidChange"),
    "UITextViewTextDidChangeNotification",
    nil)

objc.selector(name)

Function

Syntax

objc.selector("SelectorName")

Returns an Objective-C selector with the specified name which can be used in combination with an objc.class, for example to register for notifications through the NSNotificationCenter.

Parameters

name string.

The name of the selector.

Returns

table

An Objective-C selector (or SEL).

NotificationHandler Example

NotificationHandler = objc.class("NotificationHandler")

function NotificationHandler:textDidChange(objNotification)
    print(objNotification.object.text)
end

handler = NotificationHandler()

notificationCenter = objc.NSNotificationCenter.defaultCenter
notificationCenter:addObserver_selector_name_object_(
    handler,
    objc.selector("textDidChange"),
    "UITextViewTextDidChangeNotification",
    nil)

objc.set(table)

Function

Syntax

objc.set({1, 2, 3})

Returns an Objective-C NSSet initialized from a Lua table.

By default, NSSet returned from calls to Objective-C (or reading properties) are automatically converted to Lua tables. If you need to use the NSSet, you can convert the table to NSSet using objc.set.

Parameters

table table.

A Lua table.

Returns

table

An Objective-C NSSet.

objc.string(text)

Function

Syntax

objc.string("Text")

Returns an Objective-C NSString initialized from a Lua string.

By default, strings returned from calls to Objective-C (or reading properties) are automatically converted to Lua strings and vice versa. If you need to access NSString methods, you can convert the strings to NSString using objc.string.

Parameters

text string.

The text to convert to an NSString.

Returns

table

An Objective-C NSString.

objc.automaticBridging

Property

Syntax

objc.automaticBridging = false

Whether values crossing from Objective-C into Lua are converted to their Codea equivalents. Defaults to true.

While false, a UIImage or UIColor returned from a native call arrives as the wrapped Objective-C object instead of a Codea image or color, so it can still be configured. Prefer objc.withoutBridging() over setting this directly — an error raised while it is false would leave it switched off for the rest of the session.

objc.withoutBridging(body)

Function

Syntax

img = objc.withoutBridging(function()
    local native = objc.UIImage:systemImageNamed_("globe")
    native = native:imageWithTintColor_(color(255, 0, 0))
    return objc.bridge(native)
end)

Runs body with objc.automaticBridging disabled, restoring the previous setting afterwards — including when body raises an error.

Returns whatever body returns.

Parameters

body function.

The function to run with bridging disabled.

objc.bridge(value)

Function

Syntax

img = objc.bridge(native)

Converts an Objective-C value to its Codea equivalent, whatever the current objc.automaticBridging setting is.

Used to convert a native object once it has been configured. Values with no Codea equivalent are returned unchanged.

Parameters

value any.

The Objective-C value to convert.

Returns

value

The Codea equivalent, or the value unchanged.

objc.enum

Property

Syntax

objc.enum.EnumName.ValueName

Exposes native Objective-C enumerations.

When value names are prefixed with their enumeration’s name, the prefix is removed to simplify their usage.

For example, objc.enum.NLTokenUnit.paragraph is the integer value for NLTokenUnitParagraph (2).

Unnamed enum values can be found directly under objc.enum, e.g. objc.enum.NSUTF8StringEncoding.

Combine UNAuthorizationOptions

opts =
    objc.enum.UNAuthorizationOptions.badge |
    objc.enum.UNAuthorizationOptions.sound |
    objc.enum.UNAuthorizationOptions.alert

objc.app

Property

Syntax

objc.app

The UIApplication’s sharedApplication.

objc.viewer

Property

Syntax

objc.viewer

The runtime UIViewController.

objc.info

Property

Syntax

objc.info

Exposes the info dictionary keys and values.

For better readability, all keys have their Apple prefix removed.

For example, to get the value of NSBundleIdentifier, use objc.info.bundleIdentifier.

Test if running in an exported project

isStandalone = objc.info.bundleIdentifier ~= "com.twolivesleft.Codify"

objc.insets(top, left, bottom, right)

Function

Syntax

objc.insets(top, left, bottom, right)

Create a UIEdgeInsets.

Parameters

top number.

top value of the UIEdgeInsets

left number.

left value of the UIEdgeInsets

bottom number.

bottom value of the UIEdgeInsets

right number.

right value of the UIEdgeInsets

Returns

objc.private.insets

The UIEdgeInsets struct.

objc.log(message)

Function

Syntax

objc.log(message)

Log a message using NSLog instead of the Codea console.

Parameters

message string.

Message to display.

objc.inspect(class)

Function

Syntax

objc.inspect(class)

Inspect an Objective-C class, listing its variables, properties, methods and protocols.

Returns a table with the following information:

  • super: the superclass which can be used as if it was accessed through objc
  • variables: array of instance variables
  • name: name of the variable
  • typeEncoding: see Type Encoding
  • type: user-friendly name of the variable type
  • properties: array of instance properties
  • name: name of the property
  • attributes: see Property Type String
  • type: user-friendly name of the property type
  • methods: array of instance methods
  • name: name of the methods
  • returnType: user-friendly name of the method’s return type
  • arguments: array of method arguments
  • name: name of the arguments
  • typeEncoding: see Type Encoding
  • type: user-friendly name of the argument’s type
  • protocols: array of instance protocols
  • name: name of the protocol

Class members are accessible by prefixing with class., for example using objc.inspect(myClass).class.variables to list the class variables of myClass.

Parameters

class table.

Objective-C class to inspect.

Returns

table

A table with the class information

Inspect the SFSpeechRecognizer class

inspect = objc.inspect(objc.SFSpeechRecognizer)
print("Class has " .. #inspect.methods .. " instance methods.")

objc.async(function)

Function

Syntax

objc.async(function)

Parameters

function function.

Parameterless function to run on the target thread.

objc.point(x, y)

Function

Syntax

objc.point(x, y)

Create a CGPoint.

Parameters

x number.

x position of the CGPoint

y number.

y position of the CGPoint

Returns

table

The CGPoint struct.

Create a CGPoint

point = objc.point(100, 200)

objc.rect(x, y, width, height)

Function

Syntax

objc.rect(x, y, width, height)

Create a CGRect.

Parameters

x number.

x position of the CGRect

y number.

y position of the CGRect

width number.

width of the CGRect

height number.

height of the CGRect

Returns

table

The CGRect struct.

Create a CGRect

rect = objc.rect(100, 200, 300, 400)

objc.size(width, height)

Function

Syntax

objc.size(width, height)

Create a CGSize.

Parameters

width number.

width of the CGSize

height number.

height of the CGSize

Returns

table

The CGSize struct.

Create a CGSize

size = objc.size(300, 400)

objc.range(loc, len)

Function

Syntax

objc.range(loc, len)

Create a NSRange.

Parameters

loc number.

location of the NSRange

len number.

length of the NSRange

Returns

table

The NSRange struct.

Create a NSRange

range = objc.range(10, 20)

objc.color(r, g, b, a)

Function

Syntax

objc.color(r, g, b, a)

Create a CGColor. For UIColor, use the Codea Color type instead.

Parameters

r number.

red value of the CGColor

g number.

green value of the CGColor

b number.

blue value of the CGColor

a number.

alpha value of the CGColor

Returns

table

The CGColor struct.

Create a CGColor

color = objc.color(1, 0, 0, 1)

objc.vector(dx, dy)

Function

Syntax

objc.vector(dx, dy)

Create a CGVector.

Parameters

dx number.

x direction of the CGVector

dy number.

y direction of the CGVector

Returns

table

The CGVector struct.

Create a CGVector

vector = objc.vector(1, 0)

objc.affineTransform()

Function

Syntax

objc.affineTransform(a, b, c, d, tx, ty)

Returns

table

The CGAffineTransform struct.

Create a CGAffineTransform

affineTransform = objc.affineTransform(1, 0, 0, 1, 100, 200)

Loading Frameworks top ↑

Overview

Here are some of the frameworks included with the Codea runtime.

Refer to Apple’s documentation for how to interact with them.

For a more exhaustive list, use the example code below.

List all included system Frameworks

-- list all included system Frameworks
local bundles = objc.NSBundle.allFrameworks
local systemBundles = {}

for i, b in ipairs(bundles) do
    if string.find(
        b.bundlePath,
        "System/Library/Frameworks/") then
        table.insert(systemBundles, b.bundlePath)
    end
end

table.sort(systemBundles)

for i, bundle in ipairs(systemBundles) do
    print(bundle)
end