- Java/Objective-C Bridge
- Features
- An Example
- Requirements
- License
- Source Code and Downloads
- Contact
- Getting Started
- Installation
- The Basics
- The Low-Level API
- The Mid-Level API
- High-Level API (Object Oriented API)
- Writing a Delegate Class In Java
- ca.weblite : java-objc-bridge
- Java-Objective-C-Bridge · A thin bridge that allows for two-way communication from Java to Objective-C.
- Latest Version
- All Versions
- Choose a version of ca.weblite : java-objc-bridge to add to Maven or Gradle — All Versions:
- java-objc-bridge-1.2
- java-objc-bridge-1.1
- java-objc-bridge-1.0.0
- How to add a dependency to Maven
- How to add a dependency to Gradle
- How to add a dependency to SBT Scala
Java/Objective-C Bridge
This class library enables you to use any Objective-C library directly from Java without having to generate any Java class stubs. It is built on top of the powerful JNA library, and interacts with Objective-C through the C functions in the Objective-C runtime.
Features
- Access To All Native Libraries and Frameworks
- No need to generate class stubs
- Two-Way Communication — Objective-C can use your Java objects just as your Java objects can use Objective-C objects.
- Annotations to mark Java methods that should be callable by Objective-C
- Very Thin Wrapper — so you don’t have to learn a whole new way of working with Objective-C.
An Example
The following example shows the bridge being used to display an NSOpenPanel, and add a Java class as its delegate to listen for changes to the user selection:
Requirements
License
Source Code and Downloads
Contact
Getting Started
Installation
- Add the ObjCBridge.jar and jna.jar files into your class path
- Add libjcocoa.dylib to your java.library.path
The Basics
The Java Cocoa bridge can be used at three levels of abstraction:
- Low level : Direct calling of Objective-C runtime functions. E.g. objc_msgSend(), object_getClass(), etc. See Apple’s documentation on the Objective-C runtime for details of what these methods do. You would use the Runtime.INSTANCE singleton object to access these functions through the Java Cocoa Bridge.
- Mid level : Calling simplified procedural wrappers around the Objective-C runtime functions via the RuntimeUtils class and its set of static methods.
- High level : Using the object-oriented API to interact with Objective-C objects and classes via Java wrapper objects. This method makes use of the Client , Proxy , and NSObject classes primarily.
The Low-Level API
The low-level API is nothing more than a JNA interface for the Objective-C runtime functions. If you are familiar with these functions, and you are comfortable calling them directly, then you are free to call this API directly. In most cases, however, you will likely want to use a higher level API that handles a bit more of the plumbing.
The following is a sample from the RuntimeTest Unit test that makes use of this low-level API:
A few things to comment on here:
- The com.sun.jna.Pointer class is used throughout this API to represent a C pointer.
- Many of the low-level API functions take a Pointer as a parameter and return pointers, however the objc_msgSend() method and its variants return a long rather than a Pointer. This is because the value returned by this method can be either a value or a pointer.
- Messages that take C strings (i.e. const *char ) expect to receive a java string as a parameter. Messages that take NSStrings, however, will not accept Strings of this type. You need to provide a pointer to an NSString object in these cases.
The Mid-Level API
The mid-level API consists of a set of static convenience methods around commonly used functions of the Objective-C runtime API. These include:
- Getting Selectors using the RuntimeUtils.sel() and RuntimeUtils.selName() methods.
- Getting Classes using the RuntimeUtils.cls() and RuntimeUtils.clsName() methods.
- Sending Messages using one of the RuntimeUtils.msg() variants.
Even if your are working with the high-level API, you will still likely need to use the mid-level API in some instances. For convenience, you may want to use a static import of all of the RuntimeUtils static methods so that you can access them quickly and easily:
import static ca.weblite.objc.RuntimeUtils.*;
The following sample code is taken from one of the unit tests and gives a good example of how to use the mid-level API to interact with the Objective-C runtime:
In order to provide a contrast with the low-level API, this example performs the same logic as the low-level example above. It makes use of the RuntimeUtils.sel() function to obtain a selector rather than longer Runtime.INSTANCE.sel_getUid(). It also uses the cls() and clsName() functions to go back and forth between a class object and its class name.
However, we can shorten this still more. The msg() function includes a variant that accepts String s as their first two arguments. If the first argument is a String , then it is assumed to be a class name. If the second argument is a String , then it is assumed to be a selector name. Using this knowledge we can compress the above code significantly:
Using Type-Mapping
The RuntimeUtils class also includes a few methods that act as a link and foundation to the higher level APIs. It includes a variant of the msg() function that can optionally perform type mapping of input arguments and their return values.
The method signature is as follows:
Object msg(boolean coerceOutput, boolean coerceInput, Pointer receiver, Pointer selector, Object. args)
Note, that there are variants that allow you to specify the receiver or selector as strings also.
If you pass true for both coerceOutput and coerceInput , then the method will perform type mapping on the input args and the return value so that you are able to pass more familiar Java objects in. It will also perform type-checking to ensure that the correct variant of objc_msgSend() is sent to the Objective-C runtime (e.g. messages that return float s and doubles need to use the objc_msgSend_fret() function, and messages that return structures need to use the objc_msgSend_sret() function).
The type mapping is minimal and non-complex. There are just a few key mappings that are performed by this method:
- Converts Java String objects to Objective-C NSString objects for arguments that are passed to messages that are expecting NSStrings . It uses the method signature of the method to determine if the transformation needs to be made. I.e. if a Java String is passed as an argument to a message that is expecting an Objective-C object (i.e. type encoding «@»), then the string will be converted to an NSString automatically.
- Converts Proxy objects into Pointer objects for arguments that are expecting a Pointer or an Objective-C object.
- Automatically determines the correct variant of objc_msgSend* depending on the return type of the message (as specified in the Objective-C message signature).
- Converts return NSString return values to Java String objects.
- Wraps NSObject return values in Proxy objects so that they can be used more easily. It performs this conversion in a smart way using the Proxy.load() method to make sure that there is ever only one instance of a Java proxy for each instance of an Objective-C object. I.e. If a Proxy object already exists for a particular Objective-C object, then this method will look up the existing Proxy object and return that. Otherwise it creates a new Proxy object to wrap the return value and registers this proxy with the central proxy registry.
- Messages that return Structures will return a subclass of com.sun.jna.Structure
The following is some sample code taken from one of the unit tests to demonstrate how this variant of msg() can be used to enable automatic type mapping in the return values of Objective-c messages.
High-Level API (Object Oriented API)
Now that you see the foundation upon which the Java/Objective-C bridge sits, we can jump straight into the higher-level object oriented API. This API consists of three main classes that you’ll wish to use.:
- Client — An object that allows you to send messages to the Objective-C runtime. You’ll generally use this to instantiate new Objective-C objects and obtain Proxies to them.
- Proxy — A wrapper around an Objective-C object that enables you to easily send messages to the native object. It includes convenience methods for sending messages that return various types. E.g. the sentInt() method sends a message to which you expect an int to be returned. It returns a Java int so that you don’t need to perform any ugly type casting.
- NSObject — A Java wrapper around an Objective-C object that enables two-way communication. Objective-C can call methods on this Java class, and you are able to send messages to the object’s native Peer from Java. This is a subclass of Proxy so that it includes all of the same capabilities for sending messages. Typically you would subclass NSObject if you need to create a delegate for an objective-C object. You can define Java methods in your subclass that can be used in Objective-C via the Msg annotation.
The following snippet is taken from a unit test. It shows the use of the Client class and Proxy class to create an NSMutable array and add some strings to it.
Writing a Delegate Class In Java
The NSObject class can be extended to define your own Java classes that can be called from Objective-C. You specify which methods can handle Objective-C messages using the Msg annotation. There is an example at the top of the page that uses this method to add a delegate to the NSOpenPanel dialog.
ca.weblite : java-objc-bridge
Java-Objective-C-Bridge · A thin bridge that allows for two-way communication from Java to Objective-C.
dependency> groupId>ca.weblitegroupId> artifactId>java-objc-bridgeartifactId> version>1.2version> dependency>
// https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridgeimplementation group: 'ca.weblite', name: 'java-objc-bridge', version: '1.2'
// https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridgeimplementation 'ca.weblite:java-objc-bridge:1.2'
// https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridgeimplementation("ca.weblite:java-objc-bridge:1.2")
// https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridgelibraryDependencies += "ca.weblite" % "java-objc-bridge" % "1.2"
// https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridge@Grapes( @Grab(group='ca.weblite', module='java-objc-bridge', version='1.2') )
;; https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridge[ca.weblite/java-objc-bridge "1.2"]
# https://mavenlibs.com/maven/dependency/ca.weblite/java-objc-bridge'ca.weblite:java-objc-bridge:jar:1.2'
Latest Version
Choose a version of ca.weblite : java-objc-bridge to add to Maven or Gradle — Latest Versions:
All Versions
Choose a version of ca.weblite : java-objc-bridge to add to Maven or Gradle — All Versions:
java-objc-bridge-1.2
java-objc-bridge-1.1
java-objc-bridge-1.0.0
How to add a dependency to Maven
Add the following ca.weblite : java-objc-bridge maven dependency to the pom.xml file with your favorite IDE (IntelliJ / Eclipse / Netbeans) :
dependency> groupId>ca.weblitegroupId> artifactId>java-objc-bridgeartifactId> version>1.2version> dependency>
How to add a dependency to Gradle
Gradle Groovy DSL: Add the following ca.weblite : java-objc-bridge gradle dependency to your build.gradle file:
implementation 'ca.weblite:java-objc-bridge:1.2'
Gradle Kotlin DSL: Add the following ca.weblite : java-objc-bridge gradle kotlin dependency to your build.gradle.kts file:
implementation("ca.weblite:java-objc-bridge:1.2")
How to add a dependency to SBT Scala
SBT Scala: Add the following ca.weblite : java-objc-bridge sbt scala dependency to your build.sbt file:
libraryDependencies += "ca.weblite" % "java-objc-bridge" % "1.2"