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.
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.
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.UIScreenConstructor
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.5Methods
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:
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.
--- 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 nowobjc.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.5Request 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("DelegateName")
Returns a type which can be instantiated and used as an Objective-C delegate for the specified type.
| name | string. The name of the delegate. |
table |
A type to be used as an Objective-C delegate. |
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()
-- 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("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.
| name | string. The name of the class. |
table |
A type to be used as an Objective-C class. |
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("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.
| name | string. The name of the selector. |
table |
An Objective-C selector (or SEL). |
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({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.
| table | table. A Lua table. |
table |
An Objective-C NSSet. |
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.
| text | string. The text to convert to an NSString. |
table |
An Objective-C NSString. |
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.
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.
| body | function. The function to run with bridging disabled. |
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.
| value | any. The Objective-C value to convert. |
| value | The Codea equivalent, or the value unchanged. |
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.
opts =
objc.enum.UNAuthorizationOptions.badge |
objc.enum.UNAuthorizationOptions.sound |
objc.enum.UNAuthorizationOptions.alert
objc.app
The UIApplication’s sharedApplication.
objc.viewer
The runtime UIViewController.
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.
isStandalone = objc.info.bundleIdentifier ~= "com.twolivesleft.Codify"
objc.insets(top, left, bottom, right)
Create a UIEdgeInsets.
| 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 |
objc.private.insets |
The UIEdgeInsets struct. |
objc.log(message)
Log a message using NSLog instead of the Codea console.
| message | string. Message to display. |
objc.inspect(class)
Inspect an Objective-C class, listing its variables, properties, methods and protocols.
Returns a table with the following information:
objcClass members are accessible by prefixing with class., for example using objc.inspect(myClass).class.variables to list the class variables of myClass.
| class | table. Objective-C class to inspect. |
table |
A table with the class information |
inspect = objc.inspect(objc.SFSpeechRecognizer)
print("Class has " .. #inspect.methods .. " instance methods.")
objc.async(function)
| function | function. Parameterless function to run on the target thread. |
objc.point(x, y)
Create a CGPoint.
| x | number. x position of the CGPoint |
| y | number. y position of the CGPoint |
table |
The CGPoint struct. |
point = objc.point(100, 200)
objc.rect(x, y, width, height)
Create a CGRect.
| 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 |
table |
The CGRect struct. |
rect = objc.rect(100, 200, 300, 400)
objc.size(width, height)
Create a CGSize.
| width | number. width of the CGSize |
| height | number. height of the CGSize |
table |
The CGSize struct. |
size = objc.size(300, 400)
objc.range(loc, len)
Create a NSRange.
| loc | number. location of the NSRange |
| len | number. length of the NSRange |
table |
The NSRange struct. |
range = objc.range(10, 20)
objc.color(r, g, b, a)
Create a CGColor. For UIColor, use the Codea Color type instead.
| 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 |
table |
The CGColor struct. |
color = objc.color(1, 0, 0, 1)
objc.vector(dx, dy)
Create a CGVector.
| dx | number. x direction of the CGVector |
| dy | number. y direction of the CGVector |
table |
The CGVector struct. |
vector = objc.vector(1, 0)
objc.affineTransform(a, b, c, d, tx, ty)
Create a CGAffineTransform.
table |
The CGAffineTransform struct. |
affineTransform = objc.affineTransform(1, 0, 0, 1, 100, 200)
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