objc-js 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,312 +1,54 @@
1
1
  # objc-js
2
2
 
3
- > [!WARNING]
4
- > This is not production ready.
5
-
6
3
  **objc-js** is an Objective-C bridge for Node.js. This is a fork of [nobjc](https://github.com/nmggithub/nobjc) by [Noah Gregory](https://github.com/nmggithub).
7
4
 
8
- ## Usage
9
-
10
- ### Basic Usage
11
-
12
- ```typescript
13
- import { NobjcLibrary } from "objc-js";
14
-
15
- // Load a framework
16
- const foundation = new NobjcLibrary("/System/Library/Frameworks/Foundation.framework/Foundation");
17
-
18
- // Get a class and call methods
19
- const NSString = foundation["NSString"];
20
- const str = NSString.stringWithUTF8String$("Hello, World!");
21
- console.log(str.toString());
22
- ```
23
-
24
- ### Subclassing Objective-C Classes
25
-
26
- **objc-js** supports defining new Objective-C classes and subclassing existing ones from JavaScript. This allows you to override methods, implement custom behavior, and integrate deeply with macOS frameworks.
27
-
28
- See [Subclassing Documentation](./docs/subclassing.md) for detailed usage and examples.
29
-
30
- #### Quick Example
31
-
32
- ```typescript
33
- import { NobjcClass } from "objc-js";
34
-
35
- const MyClass = NobjcClass.define({
36
- name: "MyCustomClass",
37
- superclass: "NSObject",
38
- methods: {
39
- description: {
40
- types: "@@:", // returns NSString
41
- implementation: (self) => {
42
- const superDesc = NobjcClass.super(self, "description");
43
- const prefix = NSString.stringWithUTF8String$("Custom: ");
44
- return prefix.stringByAppendingString$(superDesc);
45
- }
46
- }
47
- }
48
- });
49
-
50
- const instance = MyClass.alloc().init();
51
- console.log(instance.description().toString()); // "Custom: <MyCustomClass: 0x...>"
52
- ```
53
-
54
- ### Protocol Implementation
55
-
56
- **objc-js** supports creating Objective-C protocol implementations from JavaScript. This allows you to create delegate objects that can be passed to Objective-C APIs.
57
-
58
- #### Creating a Protocol Implementation
59
-
60
- Use `NobjcProtocol.implement()` to create an object that implements a protocol:
61
-
62
- ```typescript
63
- import { NobjcProtocol } from "objc-js";
64
-
65
- const delegate = NobjcProtocol.implement("ASAuthorizationControllerDelegate", {
66
- authorizationController$didCompleteWithAuthorization$: (controller, authorization) => {
67
- console.log("Authorization completed successfully!");
68
- console.log("Authorization:", authorization);
69
- },
70
- authorizationController$didCompleteWithError$: (controller, error) => {
71
- console.error("Authorization failed:", error);
72
- }
73
- });
74
-
75
- // Pass the delegate to an Objective-C API
76
- authController.setDelegate$(delegate);
77
- ```
78
-
79
- #### Method Naming Convention
80
-
81
- Method names use the `$` notation to represent colons in Objective-C selectors:
82
-
83
- - Objective-C: `authorizationController:didCompleteWithAuthorization:`
84
- - JavaScript: `authorizationController$didCompleteWithAuthorization$`
85
-
86
- #### Argument and Return Value Marshalling
87
-
88
- Arguments are automatically converted between JavaScript and Objective-C:
89
-
90
- - **Primitives**: Numbers, booleans, and strings are automatically converted
91
- - **Objects**: Objective-C objects are wrapped in `NobjcObject` instances
92
- - **null/nil**: JavaScript `null` maps to Objective-C `nil` and vice versa
93
-
94
- #### Memory Management
95
-
96
- Memory is automatically managed:
97
-
98
- - JavaScript callbacks are kept alive as long as the delegate object exists
99
- - When the Objective-C object is deallocated, the callbacks are automatically released
100
- - No manual cleanup is required
101
-
102
- #### Example: WebAuthn/Passkeys with AuthenticationServices
103
-
104
- ```typescript
105
- import { NobjcLibrary, NobjcProtocol } from "objc-js";
106
-
107
- const authServices = new NobjcLibrary(
108
- "/System/Library/Frameworks/AuthenticationServices.framework/AuthenticationServices"
109
- );
110
-
111
- // Create authorization requests
112
- const ASAuthorizationController = authServices["ASAuthorizationController"];
113
- const controller = ASAuthorizationController.alloc().initWithAuthorizationRequests$(requests);
114
-
115
- // Create a delegate
116
- const delegate = NobjcProtocol.implement("ASAuthorizationControllerDelegate", {
117
- authorizationController$didCompleteWithAuthorization$: (controller, authorization) => {
118
- // Handle successful authorization
119
- const credential = authorization.credential();
120
- console.log("Credential:", credential);
121
- },
122
- authorizationController$didCompleteWithError$: (controller, error) => {
123
- // Handle error
124
- console.error("Authorization error:", error.localizedDescription());
125
- }
126
- });
127
-
128
- // Set the delegate and perform requests
129
- controller.setDelegate$(delegate);
130
- controller.performRequests();
131
- ```
132
-
133
- #### Notes
134
-
135
- - Protocol implementations are created at runtime using the Objective-C runtime APIs
136
- - If a protocol is not found, a class is still created (useful for informal protocols)
137
- - Method signatures are inferred from the protocol or from the method name
138
- - Thread safety: Currently assumes single-threaded (main thread) usage
139
-
140
- ### API Reference
141
-
142
- #### `NobjcLibrary`
143
-
144
- Creates a proxy for accessing Objective-C classes from a framework.
145
-
146
- ```typescript
147
- const framework = new NobjcLibrary(path: string);
148
- ```
149
-
150
- #### `NobjcObject`
151
-
152
- Wrapper for Objective-C objects. Methods can be called using the `$` notation.
5
+ ## Installation
153
6
 
154
- ```typescript
155
- const result = object.methodName$arg1$arg2$(arg1, arg2);
156
- ```
157
-
158
- #### `NobjcClass`
159
-
160
- Static class for defining new Objective-C classes and subclassing existing ones.
161
-
162
- ```typescript
163
- NobjcClass.define(definition: ClassDefinition): NobjcObject
164
- ```
165
-
166
- **Parameters:**
7
+ ### Prerequisites
167
8
 
168
- - `definition.name`: The name of the new Objective-C class (must be unique)
169
- - `definition.superclass`: The name of the superclass (e.g., "NSObject", "NSView")
170
- - `definition.protocols`: (Optional) Array of protocol names to conform to
171
- - `definition.methods`: Object mapping selector names to method definitions
9
+ - Node.js / Bun
10
+ - Xcode Command Line Tools (Run `xcode-select --install` to install)
11
+ - `pkg-config` from Homebrew (Run `brew install pkgconf` to install)
172
12
 
173
- **Returns:** The new class object (can call `.alloc().init()` on it)
13
+ > [!NOTE]
14
+ > **Why are these prerequisites required?**
15
+ >
16
+ > These are required to rebuild the native code for your system.
174
17
 
175
- See [Subclassing Documentation](./docs/subclassing.md) for more details.
18
+ ### Install using npm
176
19
 
177
- ```typescript
178
- NobjcClass.super(self: NobjcObject, selector: string, ...args: any[]): any
179
- ```
180
-
181
- Call the superclass implementation of a method. Supports methods with any number of arguments, including methods with out-parameters (like `NSError**`).
182
-
183
- #### `NobjcProtocol`
184
-
185
- Static class for creating protocol implementations.
186
-
187
- ```typescript
188
- NobjcProtocol.implement(
189
- protocolName: string,
190
- methodImplementations: Record<string, (...args: any[]) => any>
191
- ): NobjcObject
20
+ ```bash
21
+ npm install objc-js
192
22
  ```
193
23
 
194
- **Parameters:**
24
+ ### Install using bun
195
25
 
196
- - `protocolName`: The name of the Objective-C protocol (e.g., "NSCopying", "ASAuthorizationControllerDelegate")
197
- - `methodImplementations`: An object mapping method names (using `$` notation) to JavaScript functions
198
-
199
- **Returns:** A `NobjcObject` that can be passed to Objective-C APIs expecting the protocol
200
-
201
- #### `getPointer()`
202
-
203
- Get the raw native pointer for a NobjcObject as a Node Buffer. This is useful for passing Objective-C objects to native APIs that expect raw pointers, such as Electron's native window handles.
204
-
205
- ```typescript
206
- function getPointer(obj: NobjcObject): Buffer;
26
+ ```bash
27
+ bun add objc-js
28
+ # and `bun pm trust -a` to run the rebuild if needed
207
29
  ```
208
30
 
209
- **Parameters:**
31
+ ## Documentation
210
32
 
211
- - `obj`: The NobjcObject to get the pointer from
33
+ The documentation is organized into several guides:
212
34
 
213
- **Returns:** A Buffer containing the pointer address in little-endian format (8 bytes on 64-bit macOS)
35
+ - **[Basic Usage](./docs/basic-usage.md)** - Getting started with loading frameworks and calling methods
36
+ - **[Subclassing Objective-C Classes](./docs/subclassing.md)** - Creating and subclassing Objective-C classes from JavaScript
37
+ - **[Protocol Implementation](./docs/protocol-implementation.md)** - Creating delegate objects that implement protocols
38
+ - **[API Reference](./docs/api-reference.md)** - Complete API documentation for all classes and functions
214
39
 
215
- **Example: Getting an NSView pointer**
40
+ ## Quick Start
216
41
 
217
42
  ```typescript
218
- import { NobjcLibrary, getPointer } from "objc-js";
219
-
220
- // Load AppKit framework
221
- const appKit = new NobjcLibrary("/System/Library/Frameworks/AppKit.framework/AppKit");
222
-
223
- // Get an NSWindow and its content view
224
- const NSApplication = appKit["NSApplication"];
225
- const app = NSApplication.sharedApplication();
226
- const window = app.mainWindow();
227
- const view = window.contentView();
228
-
229
- // Get the raw pointer as a Buffer
230
- const pointerBuffer = getPointer(view);
231
-
232
- // Read the pointer as a BigInt (64-bit unsigned integer)
233
- const pointer = pointerBuffer.readBigUInt64LE(0);
234
- console.log(`NSView pointer: 0x${pointer.toString(16)}`);
235
-
236
- // Use with Electron or other native APIs
237
- // const { BrowserWindow } = require('electron');
238
- // const win = BrowserWindow.fromId(pointer);
239
- ```
240
-
241
- **Note:** The pointer is returned as a Buffer in little-endian format. Use `readBigUInt64LE(0)` to read it as a 64-bit unsigned integer, which is the standard pointer size on macOS.
242
-
243
- #### `fromPointer()`
244
-
245
- Create a NobjcObject from a raw native pointer. This is the inverse of `getPointer()` and allows you to reconstruct an Objective-C object from a pointer address.
246
-
247
- ```typescript
248
- function fromPointer(pointer: Buffer | bigint): NobjcObject;
249
- ```
250
-
251
- **Parameters:**
252
-
253
- - `pointer`: A Buffer (8 bytes in little-endian format) or BigInt containing the pointer address
254
-
255
- **Returns:** A NobjcObject wrapping the native Objective-C object
256
-
257
- **Example: Round-trip pointer conversion**
258
-
259
- ```typescript
260
- import { NobjcLibrary, getPointer, fromPointer } from "objc-js";
43
+ import { NobjcLibrary } from "objc-js";
261
44
 
45
+ // Load a framework
262
46
  const foundation = new NobjcLibrary("/System/Library/Frameworks/Foundation.framework/Foundation");
263
- const NSString = foundation["NSString"];
264
-
265
- // Create an original object
266
- const original = NSString.stringWithUTF8String$("Hello, World!");
267
- console.log(original.toString()); // "Hello, World!"
268
-
269
- // Get the pointer
270
- const pointerBuffer = getPointer(original);
271
- const pointer = pointerBuffer.readBigUInt64LE(0);
272
- console.log(`Pointer: 0x${pointer.toString(16)}`);
273
-
274
- // Reconstruct the object from the pointer
275
- const reconstructed = fromPointer(pointer);
276
- // or: const reconstructed = fromPointer(pointerBuffer);
277
-
278
- console.log(reconstructed.toString()); // "Hello, World!"
279
47
 
280
- // Both objects reference the same underlying Objective-C object
281
- const ptr1 = getPointer(original).readBigUInt64LE(0);
282
- const ptr2 = getPointer(reconstructed).readBigUInt64LE(0);
283
- console.log(ptr1 === ptr2); // true
284
- ```
285
-
286
- **Example: Using with external native APIs**
287
-
288
- ```typescript
289
- // Receive a pointer from an external API
290
- const externalPointer = 0x12345678n; // Example pointer from native code
291
-
292
- // Convert it to a NobjcObject
293
- const nsObject = fromPointer(externalPointer);
294
-
295
- // Now you can call Objective-C methods on it
296
- console.log(nsObject.description());
48
+ // Get a class and call methods
49
+ const NSString = foundation["NSString"];
50
+ const str = NSString.stringWithUTF8String$("Hello, World!");
51
+ console.log(str.toString());
297
52
  ```
298
53
 
299
- **⚠️ Safety Warning:**
300
-
301
- This function is **inherently unsafe** and should be used with extreme caution:
302
-
303
- - **Invalid pointers will crash your program**: The pointer must point to a valid Objective-C object
304
- - **Dangling pointers**: The object must still be alive (not deallocated). Accessing a deallocated object will crash
305
- - **No type checking**: There's no way to verify the pointer points to the expected type of object
306
- - **Memory management**: Be aware of Objective-C reference counting. The object must remain valid for the lifetime of your usage
307
-
308
- Only use this function when:
309
-
310
- - You received the pointer from `getPointer()` and the object is still alive
311
- - You received the pointer from a trusted native API that guarantees the object's validity
312
- - You're interfacing with external native code that provides valid Objective-C object pointers
54
+ For more examples and detailed guides, see the [documentation](./docs/basic-usage.md).
package/binding.gyp CHANGED
@@ -16,13 +16,13 @@
16
16
  ],
17
17
  "include_dirs": [
18
18
  "<!@(node -p \"require('node-addon-api').include\")",
19
- "<!@(pkg-config --cflags-only-I libffi | sed 's/-I//g')"
19
+ "<!@(brew --prefix libffi)/include"
20
20
  ],
21
21
  "dependencies": [
22
22
  "<!(node -p \"require('node-addon-api').gyp\")"
23
23
  ],
24
24
  "libraries": [
25
- "-lffi"
25
+ "<!@(brew --prefix libffi)/lib/libffi.a"
26
26
  ],
27
27
  "xcode_settings": {
28
28
  "MACOSX_DEPLOYMENT_TARGET": "13.3",
package/dist/native.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { createRequire } from "node:module";
2
2
  const require = createRequire(import.meta.url);
3
- const binding = require("#nobjc_native");
3
+ import path from "node:path";
4
+ const binding = require("node-gyp-build")(path.join(__dirname, ".."));
4
5
  const { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper } = binding;
5
6
  export { LoadLibrary, GetClassObject, ObjcObject, GetPointer, FromPointer, CreateProtocolImplementation, DefineClass, CallSuper };
package/package.json CHANGED
@@ -18,12 +18,12 @@
18
18
  },
19
19
  "files": [
20
20
  "dist/",
21
- "build/Release/nobjc_native.node",
21
+ "prebuilds/",
22
22
  "binding.gyp",
23
23
  "src/native"
24
24
  ],
25
25
  "scripts": {
26
- "install": "node-gyp rebuild",
26
+ "install": "node-gyp-build",
27
27
  "build-native": "node-gyp build",
28
28
  "build-scripts": "tsc --project scripts/tsconfig.json",
29
29
  "build-source": "tsc --project src/ts/tsconfig.json",
@@ -38,18 +38,23 @@
38
38
  "test:protocol-implementation": "bun test tests/test-protocol-implementation.test.ts",
39
39
  "make-clangd-config": "node ./scripts/make-clangd-config.js",
40
40
  "format": "prettier --write \"**/*.{ts,js,json,md}\"",
41
- "preinstall-disabled": "npm run build-scripts && npm run make-clangd-config"
41
+ "preinstall-disabled": "npm run build-scripts && npm run make-clangd-config",
42
+ "prebuild:arm64": "prebuildify --napi --strip --arch arm64",
43
+ "prebuild:x64": "prebuildify --napi --strip --arch x64",
44
+ "prebuild:all": "bun run prebuild:arm64 && bun run prebuild:x64"
42
45
  },
43
- "version": "1.0.0",
46
+ "version": "1.0.2",
44
47
  "description": "Objective-C bridge for Node.js",
45
48
  "main": "dist/index.js",
46
49
  "dependencies": {
47
- "node-addon-api": "^8.5.0"
50
+ "node-addon-api": "^8.5.0",
51
+ "node-gyp-build": "^4.8.4"
48
52
  },
49
53
  "devDependencies": {
50
54
  "@types/bun": "latest",
51
55
  "@types/node": "^20.0.0",
52
- "node-gyp": "^11.4.2",
56
+ "node-gyp": "^12.1.0",
57
+ "prebuildify": "^6.0.1",
53
58
  "prettier": "^3.7.4",
54
59
  "typescript": "^5.9.3"
55
60
  },
Binary file