This class library enables you to use any Objective-C library directly from Java without
6 | having to generate any Java class stubs. It is built on top of the powerful JNA library,
7 | and interacts with Objective-C through the C functions in the Objective-C runtime.
8 |
9 |
10 |
Features
11 |
12 |
13 |
Access To All Native Libraries and Frameworks
14 |
No need to generate class stubs
15 |
Two-Way Communication - Objective-C can use your Java objects
16 | just as your Java objects can use Objective-C objects.
17 |
Annotations to mark Java methods that should be callable by Objective-C
18 |
Very Thin Wrapper - so you don't have to learn a whole new way of working
19 | with Objective-C.
20 |
21 |
22 |
An Example
23 |
24 |
The following example shows the bridge being used to display an NSOpenPanel,
25 | and add a Java class as its delegate to listen for changes to the user selection:
Add the ObjCBridge.jar and jna.jar files into your class path
52 |
Add libjcocoa.dylib to your java.library.path
53 |
54 |
55 |
The Basics
56 |
57 |
The Java Cocoa bridge can be used at three levels of abstraction:
58 |
59 |
Low level : Direct calling of Objective-C runtime functions. E.g. objc_msgSend(), object_getClass(), etc...
60 | See Apple's documentation
61 | on the Objective-C runtime for details of what these methods do. You would use the {@link ca.weblite.Runtime Runtime.INSTANCE} singleton object
62 | to access these functions through the Java Cocoa Bridge.
63 |
64 |
65 |
Mid level : Calling simplified procedural wrappers
66 | around the Objective-C runtime functions via the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class and its
67 | set of static methods.
68 |
69 |
70 |
High level : Using the object-oriented API to interact with
71 | Objective-C objects and classes via Java wrapper objects. This method makes
72 | use of the {@link ca.weblite.objc.Client Client}, {@link ca.weblite.objc.Proxy Proxy},
73 | and {@link ca.weblite.objc.NSObject NSObject} classes primarily.
74 |
75 |
76 |
77 |
78 |
The Low-Level API
79 |
80 |
The low-level API is nothing more than a JNA interface
81 | for the Objective-C runtime functions.
82 | If you are familiar with these functions, and you are comfortable calling them directly, then you are free to call this API directly.
83 | In most cases, however, you will likely want to use a higher level API that handles a bit more of the plumbing.
84 |
85 |
86 |
The following is a sample from the {@link ca.weblite.objc.RuntimeTest RuntimeTest} Unit test that makes use of this low-level API:
Many of the low-level API functions take a Pointer as a parameter and return pointers, however the objc_msgSend() method
95 | and its variants return a long rather than a Pointer. This is because the value returned by this method can be
96 | either a value or a pointer.
97 |
98 |
99 | Messages that take C strings (i.e. {@code const *char}) expect to receive a java string as a parameter. Messages that
100 | take NSStrings, however, will not accept Strings of this type. You need to provide a pointer to an NSString object
101 | in these cases.
102 |
103 |
104 |
105 |
The Mid-Level API
106 |
107 |
The mid-level API consists of a set of static convenience methods around commonly used functions of the Objective-C runtime API.
108 | These include:
109 |
110 |
Getting Selectors using the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} and {@link ca.weblite.objc.RuntimeUtils#selName RuntimeUtils.selName()}
111 | methods.
112 |
113 |
Getting Classes using the {@link ca.weblite.objc.RuntimeUtils#cls RuntimeUtils.cls()} and {@link ca.weblite.objc.RuntimeUtils#clsName RuntimeUtils.clsName()}
114 | methods.
115 |
116 |
Sending Messages using one of the {@link ca.weblite.objc.RuntimeUtils#msg RuntimeUtils.msg()} variants.
117 |
118 |
119 |
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
120 | use a static import of all of the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} static methods so that you can access them quickly and easily:
121 |
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
125 | the Objective-C runtime:
126 |
127 |
128 |
129 |
In order to provide a contrast with the low-level API, this example performs the same logic as the low-level
130 | example above. It makes use of the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} function
131 | to obtain a selector rather than longer Runtime.INSTANCE.sel_getUid(). It also uses the cls() and clsName()
132 | functions to go back and forth between a class object and its class name.
133 |
134 |
However, we can shorten this still more. The {@code msg()} function includes a variant that accepts {@code String}s
135 | as their first two arguments. If the first argument is a {@code String}, then it is assumed to be a class name. If
136 | the second argument is a {@code String}, then it is assumed to be a selector name. Using this knowledge we can
137 | compress the above code significantly:
138 |
139 |
140 |
141 |
142 |
Using Type-Mapping
143 |
144 |
The {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class also includes a few methods that act as a link and foundation
145 | to the higher level APIs. It includes a variant of the {@code msg()} function that can optionally perform type mapping
146 | of input arguments and their return values.
Note, that there are variants that allow you to specify the receiver or selector as strings also.
155 |
156 |
If you pass {@code true} for both {@code coerceOutput} and {@code coerceInput}, then the method will
157 | perform type mapping on the input {@code args} and the return value so that you are able to pass
158 | more familiar Java objects in. It will also perform type-checking to ensure that the correct
159 | variant of {@code objc_msgSend()} is sent to the Objective-C runtime (e.g. messages that return {@code float}s
160 | and {@code doubles} need to use the {@code objc_msgSend_fret()} function, and messages
161 | that return structures need to use the {@code objc_msgSend_sret()} function).
162 |
The type mapping is minimal and non-complex. There are just a few key mappings that are performed by
163 | this method:
164 |
165 |
Converts Java {@code String} objects to Objective-C {@code NSString} objects for arguments
166 | that are passed to messages that are expecting {@code NSStrings}. It uses the method signature
167 | of the method to determine if the transformation needs to be made. I.e. if a Java {@code String}
168 | is passed as an argument to a message that is expecting an Objective-C object (i.e. type encoding "{@literal @}"),
169 | then the string will be converted to an {@code NSString} automatically.
170 |
171 |
Converts {@link ca.weblite.objc.Proxy Proxy} objects into {@code Pointer} objects for arguments
172 | that are expecting a Pointer or an Objective-C object.
173 |
174 |
175 | Automatically determines the correct variant of {@code objc_msgSend*} depending on the return type of
176 | the message (as specified in the Objective-C message signature).
177 |
182 | Wraps {@code NSObject} return values in {@link ca.weblite.objc.Proxy Proxy} objects so that
183 | they can be used more easily. It performs this conversion in a smart way using the {@link ca.weblite.objc.Proxy#load Proxy.load()}
184 | method to make sure that there is ever only one instance of a Java proxy for each instance of an Objective-C
185 | object. I.e. If a {@code Proxy} object already exists for a particular Objective-C object, then this method
186 | will look up the existing {@code Proxy} object and return that. Otherwise it creates a new {@code Proxy} object
187 | to wrap the return value and registers this proxy with the central proxy registry.
188 |
The following is some sample code taken from one of the unit tests to demonstrate how this variant of {@code msg()} can
195 | be used to enable automatic type mapping in the return values of Objective-c messages.
196 |
197 |
198 |
199 |
High-Level API (Object Oriented API)
200 |
201 |
Now that you see the foundation upon which the Java/Objective-C bridge sits, we can jump straight into the
202 | higher-level object oriented API. This API consists of three main classes that you'll wish to use.:
203 |
204 |
{@link ca.weblite.objc.Client Client} - An object that allows you to send messages to the Objective-C runtime. You'll generally
205 | use this to instantiate new Objective-C objects and obtain {@link ca.weblite.objc.Proxy Proxies} to them.
206 |
207 |
208 |
{@link ca.weblite.objc.Proxy Proxy} - A wrapper around an Objective-C object that enables you to easily send messages
209 | to the native object. It includes convenience methods for sending messages that return various types. E.g. the {@link ca.weblite.objc.Proxy#sendInt sentInt()}
210 | method sends a message to which you expect an {@code int} to be returned. It returns a Java {@code int} so that
211 | you don't need to perform any ugly type casting.
212 |
213 |
214 |
{@link ca.weblite.objc.NSObject NSObject} - A Java wrapper around an Objective-C object that enables two-way communication. Objective-C
215 | 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
216 | of {@link ca.weblite.objc.Proxy Proxy} so that it includes all of the same capabilities for sending messages. Typically you would subclass
217 | {@link ca.weblite.objc.NSObject NSObject} if you need to create a delegate for an objective-C object. You can define Java methods
218 | in your subclass that can be used in Objective-C via the {@link ca.weblite.objc.annotations.Msg Msg} annotation.
219 |
220 |
221 |
222 |
The following snippet is taken from a unit test. It shows the use of the {@link ca.weblite.objc.Client Client} class and {@link ca.weblite.objc.Proxy}
223 | class to create an NSMutable array and add some strings to it.
224 |
225 |
226 |
227 |
Writing a Delegate Class In Java
228 |
229 |
The {@link ca.weblite.objc.NSObject NSObject} class can be extended to define your own Java classes that can be called from Objective-C.
230 | You specify which methods can handle Objective-C messages using the {@link ca.weblite.objc.annotations.Msg Msg} annotation. There
231 | is an example at the top of the page that uses this method to add a delegate to the {@code NSOpenPanel} dialog.
232 |
233 |
234 |
235 |
236 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
4 | 4.0.0
5 | ca.weblite
6 | java-objc-bridge
7 | 1.3-SNAPSHOT
8 | Java-Objective-C-Bridge
9 | A thin bridge that allows for two-way communication from Java to Objective-C.
10 |
11 |
12 |
13 |
14 |
15 | release
16 |
17 |
18 |
19 | Release
20 |
21 |
22 |
23 |
24 | org.apache.maven.plugins
25 | maven-javadoc-plugin
26 |
27 |
28 |
29 | org.apache.maven.plugins
30 | maven-source-plugin
31 |
32 |
33 | org.apache.maven.plugins
34 | maven-gpg-plugin
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | UTF-8
43 | 11
44 | Debug
45 |
46 |
47 |
48 |
49 | Steve Hannah
50 |
51 | Creator
52 |
53 | https://github.com/shannah
54 |
55 |
56 | Matthias Fuchs
57 |
58 | Developer
59 |
60 | https://github.com/mojo2012
61 |
62 |
63 |
64 |
65 |
66 | Apache License, Version 2.0
67 | https://www.apache.org/licenses/LICENSE-2.0.txt
68 | repo
69 |
70 |
71 |
72 | 2012
73 |
74 |
75 | Web Lite Solutions Corp.
76 | https://solutions.weblite.ca/
77 |
78 |
79 |
80 | github
81 | https://github.com/shannah/Java-Objective-C-Bridge/issues
82 |
83 |
84 | https://github.com/shannah/Java-Objective-C-Bridge
85 |
86 |
87 | scm:git:https://github.com/shannah/Java-Objective-C-Bridge.git
88 | scm:git:https://github.com/shannah/Java-Objective-C-Bridge.git
89 | HEAD
90 | https://github.com/shannah/Java-Objective-C-Bridge
91 |
92 |
93 |
94 |
95 | nexus-staging
96 | Nexus Release Repository
97 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
98 |
99 |
100 |
101 |
102 |
103 |
104 | org.apache.maven.wagon
105 | wagon-ssh
106 | 3.4.2
107 |
108 |
109 |
110 |
111 |
112 |
113 | org.codehaus.mojo
114 | exec-maven-plugin
115 | 3.0.0
116 |
117 |
118 | xcodebuild
119 | generate-resources
120 |
121 | exec
122 |
123 |
124 |
125 |
126 | xcodebuild
127 |
128 | -scheme
129 | libjcocoa
130 | -configuration
131 | ${xcodeScheme}
132 | CONFIGURATION_BUILD_DIR=${project.build.outputDirectory}
133 | build
134 |
135 |
136 |
137 |
138 |
139 |
140 | org.apache.maven.plugins
141 | maven-jar-plugin
142 | 3.2.0
143 |
144 |
145 |
146 | javaobjectivecbridge
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 | com.github.spotbugs
155 | spotbugs-maven-plugin
156 | 4.2.0
157 |
158 | more
159 | --add-opens java.base/java.lang=ALL-UNNAMED
160 | --illegal-access=deny
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | org.apache.maven.plugins
181 | maven-compiler-plugin
182 | 3.8.1
183 |
184 |
185 | org.apache.maven.plugins
186 | maven-surefire-plugin
187 | 3.0.0-M5
188 |
189 | false
190 |
191 |
192 |
193 |
194 | org.apache.maven.plugins
195 | maven-source-plugin
196 | 3.2.1
197 |
198 |
199 | attach-sources
200 |
201 | jar-no-fork
202 |
203 |
204 |
205 |
206 |
207 |
208 | org.apache.maven.plugins
209 | maven-javadoc-plugin
210 | 3.2.0
211 |
212 | false
213 |
214 | --allow-script-in-comments
215 | false
216 |
217 | true
218 |
219 | true
220 |
221 | false
222 | false
223 | all,-html,-syntax
224 | false
225 | true
226 | false
227 | mojo2012
228 | 1.0
229 | public
230 |
231 |
232 |
233 | attach-javadocs
234 |
235 | jar
236 |
237 |
238 |
239 |
240 |
241 |
242 | org.apache.maven.plugins
243 | maven-gpg-plugin
244 | 1.6
245 |
246 |
247 |
248 | --pinentry-mode
249 | loopback
250 |
251 |
252 |
253 |
254 | sign-artifacts
255 | verify
256 |
257 | sign
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 | net.java.dev.jna
269 | jna
270 | 5.10.0
271 |
272 |
273 |
274 | org.junit.jupiter
275 | junit-jupiter
276 | 5.7.0
277 | test
278 |
279 |
280 |
281 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/nativeutils/NativeUtils.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.nativeutils;
2 |
3 | import java.io.FileNotFoundException;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.OutputStream;
7 | import java.nio.file.Files;
8 | import java.nio.file.Path;
9 | import java.nio.file.StandardOpenOption;
10 | import java.util.HashMap;
11 | import java.util.Map;
12 |
13 | /**
14 | * Simple library class for working with JNI (Java Native Interface)
15 | *
16 | * @see http://frommyplayground.com/how-to-load-native-jni-library-from-jar
17 | * @author Adam Heirnich ,
18 | * @version $Id: $Id
19 | * @since 1.1
20 | */
21 | public class NativeUtils {
22 |
23 | /**
24 | * Private constructor - this class will never be instanced
25 | */
26 | private NativeUtils() {
27 | }
28 |
29 | /**
30 | * Loads a library from current JAR archive, using the class loader of the {@code NativeUtils}
31 | * class to find the resource in the JAR.
32 | *
33 | * @param path a {@link java.lang.String} object.
34 | * @throws java.io.IOException if any.
35 | * @throws UnsatisfiedLinkError if loading the native library fails.
36 | */
37 | public static void loadLibraryFromJar(String path) throws IOException {
38 | loadLibraryFromJar(path, NativeUtils.class);
39 | }
40 |
41 | /**
42 | * Loads a library from current JAR archive.
43 | *
44 | * The file from JAR is copied into system temporary directory and then loaded. The temporary file is deleted after exiting.
45 | * Method uses String as filename because the pathname is "abstract", not system-dependent.
46 | *
47 | * @throws java.lang.IllegalArgumentException If the path is not absolute or if the filename is shorter than three characters (restriction of @see File#createTempFile(java.lang.String, java.lang.String)).
48 | * @param path a {@link java.lang.String} object.
49 | * @param source {@code Class} whose class loader should be used to look up the resource in the JAR file
50 | * @throws java.io.IOException if any.
51 | * @throws UnsatisfiedLinkError if loading the native library fails.
52 | */
53 | public static void loadLibraryFromJar(String path, Class> source) throws IOException, UnsatisfiedLinkError {
54 | // Finally, load the library
55 | System.load(extractFromJar(path, source).toAbsolutePath().toString());
56 | }
57 |
58 | /**
59 | * Extracts a resource from the JAR and stores it as temporary file
60 | * in the file system.
61 | *
62 | * @param path path of the resource, must begin with {@code '/'}, see {@link Class#getResourceAsStream(String)}
63 | * @param source {@code Class} whose class loader should be used to look up the resource in the JAR file
64 | * @return file path of the temporary file extracted from this JAR
65 | * @throws java.io.IOException if any.
66 | */
67 | public static Path extractFromJar(String path, Class> source) throws IOException {
68 | if (!path.startsWith("/")) {
69 | throw new IllegalArgumentException("The path has to be absolute (start with '/').");
70 | }
71 |
72 | String filename = path.substring(path.lastIndexOf('/') + 1);
73 |
74 | // Split filename to prefix and suffix (extension)
75 | String prefix;
76 | String suffix;
77 | int lastDot = filename.lastIndexOf('.');
78 | if (lastDot == -1) {
79 | // No file extension; use complete filename as prefix
80 | prefix = filename;
81 | suffix = null;
82 | } else {
83 | prefix = filename.substring(0, lastDot);
84 | suffix = filename.substring(lastDot);
85 | }
86 |
87 | // Check if the filename is okay
88 | if (prefix.length() < 3) {
89 | throw new IllegalArgumentException("The filename has to be at least 3 characters long.");
90 | }
91 |
92 | // Prepare temporary file
93 | Path temp = Files.createTempFile(prefix, suffix);
94 | temp.toFile().deleteOnExit();
95 |
96 | // Open and check input stream
97 | InputStream is = source.getResourceAsStream(path);
98 | if (is == null) {
99 | throw new FileNotFoundException("File " + path + " was not found inside JAR.");
100 | }
101 |
102 | try (is; OutputStream out = Files.newOutputStream(temp, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING)) {
103 | is.transferTo(out);
104 | }
105 |
106 | return temp;
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/Message.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 |
6 | import com.sun.jna.Pointer;
7 |
8 | /**
9 | * A structure the encapsulates a message. This is an optional alternative
10 | * way of sending messages to the Objective-C runtime.
11 | *
12 | * @see Client#send(Message...)
13 | * @see RuntimeUtils#msg(Message...)
14 | * @see Proxy#send(Message...)
15 | * @author shannah
16 | * @version $Id: $Id
17 | * @since 1.1
18 | */
19 | public class Message {
20 |
21 | /**
22 | * Status identifier of a message to indicate that it has been skipped.
23 | *
24 | */
25 | public static final int STATUS_SKIPPED=1;
26 |
27 | /**
28 | * Status identifier of a message to indicate that is has been cancelled.
29 | */
30 | public static final int STATUS_CANCELLED=2;
31 |
32 | /**
33 | * Status identifier of a message to indicated that it has been completed.
34 | */
35 | public static final int STATUS_COMPLETED=3;
36 |
37 | /**
38 | * Status identifier of a message to indicate that it is ready to be sent.
39 | */
40 | public static final int STATUS_READY=0;
41 |
42 | /**
43 | * The target of the message.
44 | */
45 | public Pointer receiver;
46 |
47 | /**
48 | * The selector of the message.
49 | */
50 | public Pointer selector;
51 |
52 | /**
53 | * List of arguments to pass to the method invocation.
54 | */
55 | public List args = new ArrayList();
56 |
57 | /**
58 | * Placeholder for the result of the message. (i.e. return value).
59 | */
60 | public Object result;
61 |
62 | /**
63 | * If there was en error in the message handling, the error will be saved
64 | * here.
65 | */
66 | public Exception error;
67 |
68 | /**
69 | * The current status of the message. Before running, its status should
70 | * be STATUS_READY, and after running, it should be STATUS_COMPLETED. If,
71 | * for some reason it has been cancelled or skipped, then it could have
72 | * those statuses also.
73 | */
74 | public int status = 0;
75 |
76 | /**
77 | * Whether to coerce the input of the message.
78 | */
79 | public boolean coerceInput;
80 | /**
81 | * Whether to coerce the output of the message.
82 | */
83 | public boolean coerceOutput;
84 |
85 |
86 | /**
87 | * Whether the input was, in fact coerced. Set when the message
88 | * is run.
89 | */
90 | public boolean inputWasCoerced;
91 | /**
92 | * Whether the output was, in fact, coerced. Set when the message
93 | * is run.
94 | */
95 | public boolean outputWasCoerced;
96 |
97 | /**
98 | * Reference to the next message in the message chain.
99 | */
100 | public Message next;
101 | /**
102 | * Reference to the previous message in the message chain.
103 | */
104 | public Message previous;
105 |
106 |
107 | /**
108 | * Method that is called just before the message is sent. This can be
109 | * overridden to change the parameters, skip the message, or cancel the message
110 | * chain altogether.
111 | */
112 | public void beforeRequest(){
113 |
114 | }
115 |
116 | /**
117 | * Method that is called just after the message is send and response received.
118 | * This can be overridden to do post processing, like changing the settings
119 | * of subsequent messages in the chain or doing processing based on the
120 | * output of the message.
121 | */
122 | public void afterResponse(){
123 |
124 | }
125 |
126 |
127 | }
128 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/NSMessageInvocationException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Web Lite Solutions Corp..
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | package ca.weblite.objc;
17 |
18 | import java.lang.reflect.Method;
19 |
20 | /**
21 | * Exception that may be thrown when failure occurs while trying to invoke
22 | * a method in response to a selector.
23 | * @author shannah
24 | */
25 | public class NSMessageInvocationException extends RuntimeException {
26 | private static final long serialVersionUID = 1L;
27 |
28 | private final String selectorName;
29 | private final Method method;
30 |
31 | /**
32 | * Creates a new instance of this exception.
33 | * @param selectorName The objective-c selector that was being handled.
34 | * @param method The method that was being executed.
35 | * @param cause The cause of the exception.
36 | */
37 | public NSMessageInvocationException(String selectorName, Method method, Throwable cause) {
38 | super(String.format("Method invocation for selector %s caused exception. Method: %s", selectorName, method), cause);
39 | this.selectorName = selectorName;
40 | this.method = method;
41 | }
42 |
43 | /**
44 | * The name of the Objective-C selector that was being called.
45 | * @return
46 | */
47 | public String getSelectorName() {
48 | return selectorName;
49 | }
50 |
51 | /**
52 | * The method that was being executed when this exception occurred.
53 | * @return
54 | */
55 | public Method getMethod() {
56 | return method;
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/Peerable.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | import com.sun.jna.Pointer;
4 |
5 | /**
6 | * An interface for an object that has an Objective-C peer.
7 | *
8 | * @author shannah
9 | * @version $Id: $Id
10 | * @since 1.1
11 | */
12 | public interface Peerable {
13 | /**
14 | *
21 | *
22 | * @param peer a {@link com.sun.jna.Pointer} object.
23 | */
24 | public void setPeer(Pointer peer);
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/PeerableRecipient.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | /**
4 | * An interface for objects that have an Objective-C peer, and that can
5 | * be called from objective-C.
6 | *
7 | * @author shannah
8 | * @version $Id: $Id
9 | * @since 1.1
10 | */
11 | public interface PeerableRecipient extends Peerable, Recipient {
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/Recipient.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 |
4 | /**
5 | * An interface for an object that can receive messages from the Objective-C
6 | * runtime. In order to receive messages, the object should be passed to the
7 | * RuntimeUtils.createProxy() method.
8 | *
9 | *
The NSObject class is a concrete implementation of this interface that
10 | * contains all of the plumbing necessary to operate in the world of the
11 | * Objective-C runtime. It is probably best to just subclass NSObject rather
12 | * than implement your own Recipient class.
13 | *
14 | * @author shannah
15 | * @see NSObject
16 | * @see NSProxy Class Reference
17 | * @version $Id: $Id
18 | * @since 1.1
19 | */
20 | public interface Recipient {
21 |
22 | /**
23 | * Returns the method signature for a specified selector.
24 | *
25 | * @param selector The pointer to the selector to check.
26 | * @return Pointer to the NSMethodSignature object for the specified selector.
27 | * @see NSMethodSignature Class Reference
28 | * @see forwardInvocation: Method reference (from NSProxy)
29 | */
30 | public long methodSignatureForSelector(long selector);
31 |
32 | /**
33 | * Handles the invocation of a method on the recipient. Typically this should
34 | * either be handled by a java method, or routed to some parent object that
35 | * is being proxied.
36 | *
37 | * @param invocation The NSInvocation object.
38 | * @see NSInvocation Class Reference
39 | * @see forwardInvocation: Method reference (from NSProxy)
40 | * @see NSObject#methodSignatureForSelector(long) For a concrete imlementation.
41 | */
42 | public void forwardInvocation(long invocation);
43 |
44 | /**
45 | * Checks to see if this object responds to the specified selector.
46 | *
47 | * @param selector a long.
48 | * @return True if the object responds to the specified selector.
49 | */
50 | public boolean respondsToSelector(long selector);
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/RuntmeArm64Extensions.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | import com.sun.jna.Library;
4 | import com.sun.jna.Native;
5 | import com.sun.jna.Pointer;
6 |
7 | public interface RuntmeArm64Extensions extends Library {
8 | public static RuntmeArm64Extensions INSTANCE = (RuntmeArm64Extensions) Native.loadLibrary("objc.A", RuntmeArm64Extensions.class);
9 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector);
10 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg);
11 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2);
12 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3);
13 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4);
14 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5);
15 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6);
16 | public double objc_msgSend(Pointer theReceiver, Pointer theSelector,Object arg, Object arg2, Object arg3, Object arg4, Object arg5, Object arg6, Object arg7);
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/TypeMapper.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | import ca.weblite.objc.mappers.NSObjectMapping;
4 | import ca.weblite.objc.mappers.PointerMapping;
5 | import ca.weblite.objc.mappers.ScalarMapping;
6 | import ca.weblite.objc.mappers.StringMapping;
7 | import ca.weblite.objc.mappers.StructureMapping;
8 |
9 | /**
10 | *
11 | * Maps Objective-C types to Java types. This provides automatic conversion
12 | * to message inputs and outputs (unless coercion is disabled in the message
13 | * request). In many cases, it just passes the values straight through (e.g.
14 | * primitive types. Notably, Java Strings are mapped to NSStrings
15 | * if the signature of the argument context is an NSString, and NSObjects
16 | * are mapped as Proxy wrapper objects.
17 | *
18 | * @author shannah
19 | * @version $Id: $Id
20 | * @since 1.1
21 | */
22 | public class TypeMapper implements TypeMapping {
23 | /**
24 | * Singleton instance of the TypeMapper
25 | */
26 | public static final TypeMapper INSTANCE = new TypeMapper();
27 |
28 | /**
29 | * Obtains the singleton instance of the TypeMapper, i.e. {@link #INSTANCE}.
30 | *
31 | * @return singleton {@code TypeMapper} object.
32 | */
33 | public static TypeMapper getInstance(){
34 | return INSTANCE;
35 | }
36 |
37 | private TypeMapper() { }
38 |
39 | /**
40 | * Maps signatures to the corresponding TypeMapping object. Signatures
41 | * are Objective-C type encodings.
42 | */
43 | private static TypeMapping getMapping(char typeChar) {
44 | switch (typeChar) {
45 | case 'c': case 'C':
46 | case 'i': case 'I':
47 | case 's': case 'S':
48 | case 'f': case 'd':
49 | case 'l': case 'L':
50 | case 'q': case 'Q':
51 | case 'b': case 'B':
52 | case '[': case ':': case '?': case '#': case 'v':
53 | return ScalarMapping.INSTANCE;
54 | case '*':
55 | return StringMapping.INSTANCE;
56 | case '^':
57 | return PointerMapping.INSTANCE;
58 | case '@':
59 | return NSObjectMapping.INSTANCE;
60 | case '{':
61 | return StructureMapping.INSTANCE;
62 | default:
63 | throw new IllegalArgumentException("Unknown type: " + typeChar);
64 | }
65 | }
66 |
67 | /**
68 | * {@inheritDoc}
69 | *
70 | * Converts a C variable to the corresponding Java type based on the
71 | * specified signature. By default, this will map scalars straight
72 | * across without change. Strings are mapped to NSStrings.
73 | */
74 | @Override
75 | public Object cToJ(Object cVar, String signature, TypeMapping root) {
76 | String prefixes = "rnNoORV";
77 | int offset = 0;
78 | while ( prefixes.indexOf(signature.charAt(offset)) != -1 ){
79 | offset++;
80 | if ( offset > signature.length()-1 ){
81 | break;
82 | }
83 | }
84 | if ( offset > 0 ){
85 | signature = signature.substring(offset);
86 | }
87 |
88 | char typeChar = signature.charAt(0);
89 | TypeMapping mapping = getMapping(typeChar);
90 | return mapping.cToJ(cVar, signature, root);
91 | }
92 |
93 | /**
94 | * {@inheritDoc}
95 | *
96 | * Converts a Java variable to the corresponding C type based on the
97 | * specified signature. By default, this will map scalars straight
98 | * across without change. Strings are mapped to NSStrings.
99 | *
100 | *
Example Usage
101 | *
The following is a modified snippet from the NSObject class that shows
102 | * (roughly) how the jToC method is used to take the output of a Java method
103 | * and set the return value in an NSInvocation object to a corresponding
104 | * C type.:
105 | *
106 | */
107 | @Override
108 | public Object jToC(Object jVar, String signature, TypeMapping root) {
109 | String prefixes = "rnNoORV";
110 | int offset = 0;
111 | while ( prefixes.indexOf(signature.charAt(offset)) != -1 ){
112 | offset++;
113 | if ( offset > signature.length()-1 ){
114 | break;
115 | }
116 | }
117 | if ( offset > 0 ){
118 | signature = signature.substring(offset);
119 | }
120 |
121 | char typeChar = signature.charAt(0);
122 | TypeMapping mapping = getMapping(typeChar);
123 | return mapping.jToC(jVar, signature, root);
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/TypeMapping.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc;
2 |
3 | /**
4 | * Interface to be implemented by objects that map Java types to Objective-C
5 | * types.
6 | *
7 | * @author shannah
8 | * @version $Id: $Id
9 | * @since 1.1
10 | */
11 | public interface TypeMapping {
12 | /**
13 | * Converts a C variable to the corresponding Java variable given the
14 | * context of the specified signature.
15 | *
16 | * @param cVar The C variable to be converted.
17 | * @param signature The signature that tells what type of variable we are dealing with according to Objective-C Type Encoding conventions.
18 | * @param root The root TypeMapping object
19 | * @return The converted Java object.
20 | */
21 | public Object cToJ(Object cVar, String signature, TypeMapping root);
22 |
23 | /**
24 | * Converts a Java variable to the corresponding Java variable given the
25 | * context of the specified signature.
26 | *
27 | * @param jVar The Java variable to be converted.
28 | * @param signature The signature that tells what type of variable we are dealing with according to Objective-C Type Encoding conventions.
29 | * @param root a {@link ca.weblite.objc.TypeMapping} object.
30 | * @return The converted C variable
31 | */
32 | public Object jToC(Object jVar, String signature, TypeMapping root);
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/annotations/Msg.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * An annotation that allows a Java method to receive and process Objective-C
10 | * messages. This annotation is only useful for subclasses of ca.weblite.objc.NSObject.
11 | *
12 | *
13 | *
Example Class Using Annotation TO Handle Objective-C messages
14 | *
15 | *
16 | *
17 | *
The following is some test code that uses the TestClass. Notice that this
18 | * class can be interacted with the same as if it was an Objective-C object. Indeed,
19 | * Objective-C can call any of the methods marked by the {@literal @}Msg annotation
20 | * by simply sending it the appropriate message.
21 | *
22 | *
23 | *
24 | * @author shannah
25 | * @version $Id: $Id
26 | * @since 1.1
27 | */
28 | @Retention(RetentionPolicy.RUNTIME)
29 | @Target(ElementType.METHOD)
30 | public @interface Msg {
31 |
32 | /**
33 | * The name of the objective-c selector that can be used to target this method.
34 | * Selectors should be the full method name, including colons. If you're overriding
35 | * the NSSavePanel's -setTitle: message, then the selector would be "setTitle:".
36 | * The -title message, on the other hand would just be "title" (without colon)
37 | * because it takes no parameters.
38 | * @return the selector name
39 | */
40 | String selector();
41 |
42 | /**
43 | * The signature of this message. This should be a valid Objective-C type
44 | * encoding. If this parameter is omitted, then you can optionally use
45 | * the like() directive specify that the signature should be the same
46 | * as another Class' method.
47 | *
48 | * @return The signature of the message.
49 | *
50 | *
51 | *
52 | *
Example Signatures
53 | *
54 | *
55 | *
56 | *
Method Type
57 | *
Signature
58 | *
Explanation
59 | *
60 | *
61 | *
62 | *
63 | *
Returns NSString, takes no parameters
64 | *
{@literal @@}:
65 | *
The first "{@literal @}" indicates the return value, which is an Object,
66 | * The second "{@literal @}" is a mandatory hidden parameter that refers
67 | * to the receiver of the message. The ":" is a hidden parameter
68 | * that refers to the selector of the message.
69 | *
70 | *
71 | *
72 | *
Returns void, takes an NSString as a parameter
73 | *
{@literal v@:@}
74 | *
The "v" indicates that there is no return value (i.e. void).
75 | * The first "{@literal @}" marks the hidden parameter that is the target
76 | * of the message. The ":" marks the hidden selector. The final
77 | * "{@literal @}" indicates that it takes an object as a parameter (specifically
78 | * an NSString, but the signature doesn't distinguish.
79 | *
80 | *
81 | *
82 | *
Returns void, takes an C String (i.e. *char) as a parameter
83 | *
{@literal v@:*}
84 | *
The "v" indicates that there is no return value (i.e. void).
85 | * The first "{@literal @}" marks the hidden parameter that is the target
86 | * of the message. The ":" marks the hidden selector. The final
87 | * "{@literal *}" indicates that it takes a C String as the first parameter.
88 | *
89 | *
90 | *
91 | *
92 | * @see Objective-C Type Encodings
93 | */
94 | String signature() default "";
95 |
96 | /**
97 | * Specifies that the signature should match that of another object's method.
98 | * This can make it easier for you to define methods without having to know
99 | * how to formulate the signature.
100 | *
101 | *
E.g., if you have a method that takes no input, and returns an int,
102 | * you might just say that your method is like the NSArray.count selector:
103 | * {@code
104 | * {@literal @}Msg(selector="myIntMessage", like="NSArray.count")
105 | * }
106 | *
107 | *
108 | * @return The name of a method whose signature that this message should
109 | * copy.
110 | */
111 | String like() default "";
112 | }
113 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/foundation/NSRange.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc.foundation;
2 |
3 | import com.sun.jna.Structure;
4 |
5 | import java.util.List;
6 |
7 | /**
8 | * A structure mapping to the Foundation structure NSRange. If you need to call a method that
9 | * receives an NSRange as an input, you can use this structure.
10 | */
11 | public class NSRange extends Structure {
12 |
13 | public static class ByReference extends NSRange implements Structure.ByReference{
14 | }
15 | public static class ByValue extends NSRange implements Structure.ByValue {
16 | }
17 |
18 | /**
19 | * The location. WARNING: This stores an unsigned integer value. Use {@link #getLocation()} and {@link #setLocation(int)}
20 | * to properly convert to/from Java's signed ints.
21 | */
22 | public long location;
23 |
24 | /**
25 | * The length. WARNING: This stores an unsigned integer value. Use {@link #getLength()} and {@link #setLength(int)}
26 | * to properly convert to/from Java's signed ints.
27 | */
28 | public long length;
29 |
30 | /**
31 | * Sets the location of the range.
32 | * @param loc The location.
33 | */
34 | public void setLocation(int loc) {
35 | location = Integer.toUnsignedLong(loc);
36 | }
37 |
38 | /**
39 | * Sets the length of the range.
40 | * @param len The length of the range.
41 | */
42 | public void setLength(int len) {
43 | length = Integer.toUnsignedLong(len);
44 | }
45 |
46 | /**
47 | * Gets the location. Prefer this accessor to direct access of {@link #location} because {@link #location} stores
48 | * is unsigned, so the value will seem nonsensical.
49 | * @return The location as a signed int.
50 | */
51 | public int getLocation() {
52 | return (int)Integer.toUnsignedLong((int)location);
53 | }
54 |
55 | /**
56 | * Gets the length as a signed int.
57 | * @return The length as a signed int.
58 | */
59 | public int getLength() {
60 | return (int)Integer.toUnsignedLong((int)length);
61 | }
62 |
63 | @Override
64 | protected List getFieldOrder() {
65 | return List.of("location","length");
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/ca/weblite/objc/jna/PointerTool.java:
--------------------------------------------------------------------------------
1 | package ca.weblite.objc.jna;
2 |
3 | import com.sun.jna.Pointer;
4 |
5 | /**
6 | *
This class library enables you to use any Objective-C library directly from Java without
6 | having to generate any Java class stubs. It is built on top of the powerful JNA library,
7 | and interacts with Objective-C through the C functions in the Objective-C runtime.
8 |
9 |
10 |
Features
11 |
12 |
13 |
Access To All Native Libraries and Frameworks
14 |
No need to generate class stubs
15 |
Two-Way Communication - Objective-C can use your Java objects
16 | just as your Java objects can use Objective-C objects.
17 |
Annotations to mark Java methods that should be callable by Objective-C
18 |
Very Thin Wrapper - so you don't have to learn a whole new way of working
19 | with Objective-C.
20 |
21 |
22 |
An Example
23 |
24 |
The following example shows the bridge being used to display an NSOpenPanel,
25 | and add a Java class as its delegate to listen for changes to the user selection:
Add the ObjCBridge.jar and jna.jar files into your class path
52 |
Add libjcocoa.dylib to your java.library.path
53 |
54 |
55 |
The Basics
56 |
57 |
The Java Cocoa bridge can be used at three levels of abstraction:
58 |
59 |
Low level : Direct calling of Objective-C runtime functions. E.g. objc_msgSend(), object_getClass(), etc...
60 | See Apple's documentation
61 | on the Objective-C runtime for details of what these methods do. You would use the {@link ca.weblite.Runtime Runtime.INSTANCE} singleton object
62 | to access these functions through the Java Cocoa Bridge.
63 |
64 |
65 |
Mid level : Calling simplified procedural wrappers
66 | around the Objective-C runtime functions via the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class and its
67 | set of static methods.
68 |
69 |
70 |
High level : Using the object-oriented API to interact with
71 | Objective-C objects and classes via Java wrapper objects. This method makes
72 | use of the {@link ca.weblite.objc.Client Client}, {@link ca.weblite.objc.Proxy Proxy},
73 | and {@link ca.weblite.objc.NSObject NSObject} classes primarily.
74 |
75 |
76 |
77 |
78 |
The Low-Level API
79 |
80 |
The low-level API is nothing more than a JNA interface
81 | for the Objective-C runtime functions.
82 | If you are familiar with these functions, and you are comfortable calling them directly, then you are free to call this API directly.
83 | In most cases, however, you will likely want to use a higher level API that handles a bit more of the plumbing.
84 |
85 |
86 |
The following is a sample from the {@link ca.weblite.objc.RuntimeTest RuntimeTest} Unit test that makes use of this low-level API:
Many of the low-level API functions take a Pointer as a parameter and return pointers, however the objc_msgSend() method
95 | and its variants return a long rather than a Pointer. This is because the value returned by this method can be
96 | either a value or a pointer.
97 |
98 |
99 | Messages that take C strings (i.e. {@code const *char}) expect to receive a java string as a parameter. Messages that
100 | take NSStrings, however, will not accept Strings of this type. You need to provide a pointer to an NSString object
101 | in these cases.
102 |
103 |
104 |
105 |
The Mid-Level API
106 |
107 |
The mid-level API consists of a set of static convenience methods around commonly used functions of the Objective-C runtime API.
108 | These include:
109 |
110 |
Getting Selectors using the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} and {@link ca.weblite.objc.RuntimeUtils#selName RuntimeUtils.selName()}
111 | methods.
112 |
113 |
Getting Classes using the {@link ca.weblite.objc.RuntimeUtils#cls RuntimeUtils.cls()} and {@link ca.weblite.objc.RuntimeUtils#clsName RuntimeUtils.clsName()}
114 | methods.
115 |
116 |
Sending Messages using one of the {@link ca.weblite.objc.RuntimeUtils#msg RuntimeUtils.msg()} variants.
117 |
118 |
119 |
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
120 | use a static import of all of the {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} static methods so that you can access them quickly and easily:
121 |
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
125 | the Objective-C runtime:
126 |
127 |
128 |
129 |
In order to provide a contrast with the low-level API, this example performs the same logic as the low-level
130 | example above. It makes use of the {@link ca.weblite.objc.RuntimeUtils#sel RuntimeUtils.sel()} function
131 | to obtain a selector rather than longer Runtime.INSTANCE.sel_getUid(). It also uses the cls() and clsName()
132 | functions to go back and forth between a class object and its class name.
133 |
134 |
However, we can shorten this still more. The {@code msg()} function includes a variant that accepts {@code String}s
135 | as their first two arguments. If the first argument is a {@code String}, then it is assumed to be a class name. If
136 | the second argument is a {@code String}, then it is assumed to be a selector name. Using this knowledge we can
137 | compress the above code significantly:
138 |
139 |
140 |
141 |
142 |
Using Type-Mapping
143 |
144 |
The {@link ca.weblite.objc.RuntimeUtils RuntimeUtils} class also includes a few methods that act as a link and foundation
145 | to the higher level APIs. It includes a variant of the {@code msg()} function that can optionally perform type mapping
146 | of input arguments and their return values.
Note, that there are variants that allow you to specify the receiver or selector as strings also.
155 |
156 |
If you pass {@code true} for both {@code coerceOutput} and {@code coerceInput}, then the method will
157 | perform type mapping on the input {@code args} and the return value so that you are able to pass
158 | more familiar Java objects in. It will also perform type-checking to ensure that the correct
159 | variant of {@code objc_msgSend()} is sent to the Objective-C runtime (e.g. messages that return {@code float}s
160 | and {@code doubles} need to use the {@code objc_msgSend_fret()} function, and messages
161 | that return structures need to use the {@code objc_msgSend_sret()} function).
162 |
The type mapping is minimal and non-complex. There are just a few key mappings that are performed by
163 | this method:
164 |
165 |
Converts Java {@code String} objects to Objective-C {@code NSString} objects for arguments
166 | that are passed to messages that are expecting {@code NSStrings}. It uses the method signature
167 | of the method to determine if the transformation needs to be made. I.e. if a Java {@code String}
168 | is passed as an argument to a message that is expecting an Objective-C object (i.e. type encoding "{@literal @}"),
169 | then the string will be converted to an {@code NSString} automatically.
170 |
171 |
Converts {@link ca.weblite.objc.Proxy Proxy} objects into {@code Pointer} objects for arguments
172 | that are expecting a Pointer or an Objective-C object.
173 |
174 |
175 | Automatically determines the correct variant of {@code objc_msgSend*} depending on the return type of
176 | the message (as specified in the Objective-C message signature).
177 |
182 | Wraps {@code NSObject} return values in {@link ca.weblite.objc.Proxy Proxy} objects so that
183 | they can be used more easily. It performs this conversion in a smart way using the {@link ca.weblite.objc.Proxy#load Proxy.load()}
184 | method to make sure that there is ever only one instance of a Java proxy for each instance of an Objective-C
185 | object. I.e. If a {@code Proxy} object already exists for a particular Objective-C object, then this method
186 | will look up the existing {@code Proxy} object and return that. Otherwise it creates a new {@code Proxy} object
187 | to wrap the return value and registers this proxy with the central proxy registry.
188 |
The following is some sample code taken from one of the unit tests to demonstrate how this variant of {@code msg()} can
195 | be used to enable automatic type mapping in the return values of Objective-c messages.
196 |
197 |
198 |
199 |
High-Level API (Object Oriented API)
200 |
201 |
Now that you see the foundation upon which the Java/Objective-C bridge sits, we can jump straight into the
202 | higher-level object oriented API. This API consists of three main classes that you'll wish to use.:
203 |
204 |
{@link ca.weblite.objc.Client Client} - An object that allows you to send messages to the Objective-C runtime. You'll generally
205 | use this to instantiate new Objective-C objects and obtain {@link ca.weblite.objc.Proxy Proxies} to them.
206 |
207 |
208 |
{@link ca.weblite.objc.Proxy Proxy} - A wrapper around an Objective-C object that enables you to easily send messages
209 | to the native object. It includes convenience methods for sending messages that return various types. E.g. the {@link ca.weblite.objc.Proxy#sendInt sentInt()}
210 | method sends a message to which you expect an {@code int} to be returned. It returns a Java {@code int} so that
211 | you don't need to perform any ugly type casting.
212 |
213 |
214 |
{@link ca.weblite.objc.NSObject NSObject} - A Java wrapper around an Objective-C object that enables two-way communication. Objective-C
215 | 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
216 | of {@link ca.weblite.objc.Proxy Proxy} so that it includes all of the same capabilities for sending messages. Typically you would subclass
217 | {@link ca.weblite.objc.NSObject NSObject} if you need to create a delegate for an objective-C object. You can define Java methods
218 | in your subclass that can be used in Objective-C via the {@link ca.weblite.objc.annotations.Msg Msg} annotation.
219 |
220 |
221 |
222 |
The following snippet is taken from a unit test. It shows the use of the {@link ca.weblite.objc.Client Client} class and {@link ca.weblite.objc.Proxy}
223 | class to create an NSMutable array and add some strings to it.
224 |
225 |
226 |
227 |
Writing a Delegate Class In Java
228 |
229 |
The {@link ca.weblite.objc.NSObject NSObject} class can be extended to define your own Java classes that can be called from Objective-C.
230 | You specify which methods can handle Objective-C messages using the {@link ca.weblite.objc.annotations.Msg Msg} annotation. There
231 | is an example at the top of the page that uses this method to add a delegate to the {@code NSOpenPanel} dialog.
232 |