Description
🚀 The feature, motivation and pitch
Problem
Currently, ExecuTorch provides a C++ API for running exported models on Apple platforms. While functional, this presents a significant hurdle for developers working primarily in Objective-C and Swift. The documentation suggests bridging the C++ APIs manually, which is complex, error-prone, and introduces a significant maintenance burden. It also requires deep knowledge of both C++ and the intricacies of ExecuTorch's internals. This negatively impacts developer productivity and increases the barrier to entry for using ExecuTorch within the Apple ecosystem. The only currently supported API in Objective-C/Swift are the logging functions.
Motivation
We aim to make ExecuTorch easily accessible to the vast majority of iOS, macOS, and other Apple platform developers who work primarily in Objective-C and Swift. A native API will significantly improve the developer experience, making integration simpler, safer, and more efficient.
Pitch
We propose creating a set of native Objective-C wrappers around the core ExecuTorch C++ APIs. These wrappers will be designed for seamless bridging into Swift, providing a natural and idiomatic experience for Swift developers. This will drastically simplify the process of loading and executing ExecuTorch models within Apple platform apps. The user experience will be similar to the current one, but offering the native API: users will still integrate ExecuTorch using Swift Package Manager, import the ExecuTorch
module, but will now have access to classes like Module
, Tensor
, and Value
directly in Objective-C and Swift.
Example
Objective-C:
#import <ExecuTorch/ExecuTorch.h>
NSError *error;
ExecuTorchModule *module = [[ExecuTorchModule alloc] initWithFilePath:modelPath];
ExecuTorchTensor *tensor = [[ExecuTorchTensor alloc] initWithScalars:@[@1.0, @2.0, @3.0]];
NSArray<ExecuTorchValue *> *outputs = [module forwardWithTensors:@[tensor] error:&error];
if (outputs) {
NSLog(@"Output: %@", outputs.firstObject.tensorValue);
} else {
NSLog(@"Error: %@", error);
}
Swift:
import ExecuTorch
do {
let module = Module(filePath: "path/to/model.pte")
let tensor = Tensor([1.0, 2.0, 3.0])
let outputs = try module.forward([tensor])
print(outputs[0].tensor)
} catch {
print("Error: \(error)")
}
Alternatives
- Continue with manual C++ bridging: This is the current recommendation, but it's not sustainable for widespread adoption due to its complexity.
- Rely on C++/Swift interoperability: While Swift can interoperate with C++, but this approach is limited. ExecuTorch heavily relies on C++ templates, unions and some advanced language features, such as within the EValue class, which are not directly supported by C++/Swift interoperability as of today. This makes direct interop impractical for many core ExecuTorch components.
- Pure Swift Implementation: This is not a good initial solution because it would be difficult to guarantee proper memory management and layout for interaction with the C++ core, e.g. create a tensor with memory owned by a Swift Array or Data. Plus, Swift code would need to be bridged into Objective-C. The standard practice for providing Swift APIs that wrap C++ is through Objective-C wrappers, leveraging the compiler's automatic bridging capabilities.
Additional context
- Performance: The Objective-C wrappers must be lightweight and introduce minimal performance overhead. This means avoiding unnecessary data copies (especially for tensors), minimizing heap allocations, and leveraging Objective-C runtime features like tagged pointers where appropriate.
- Binary Size: The additional code introduced by the wrappers should have a minimal impact on the overall binary size of the executorch.xcframework.
- API Design: The Objective-C and Swift APIs should follow Apple's established conventions for naming, memory management, and error handling. This may necessitate some deviations from the C++ API names to ensure an idiomatic experience.
- Core Components: The initial focus will be on wrapping the essential components needed for model execution:
ExecuTorchModule
(bridged asModule
in Swift): For loading and executing models.ExecuTorchValue
(bridged asValue
in Swift): For representing inputs and outputs (including tensors, scalars, and lists).ExecuTorchTensor
(bridged asTensor
in Swift): For handling tensor data.
We'll use ExecuTorch prefix for Objective-C classes to avoid naming collisions and follow the naming scheme we've already adopted for the logging API.
- Future-Proofing: While the initial implementation will focus on core functionality, the design should be extensible to allow for future addition of more advanced features (e.g., tracing, custom memory allocators, extra helper methods).
- Distribution: The Objective-C/Swift bindings will be distributed as part of the existing executorch.xcframework.
RFC (Optional)
Proposed Design
-
Wrapper Implementation:
We will create Objective‑C wrapper classes that internally own (or reference) the native C++ objects. For example, theExecuTorchModule
wrapper will internally hold astd::unique_ptr<executorch::extension::Module>
. Similar patterns will be applied for Value (wrapping the C++EValue
variant) and Tensor (wrapping aTensorPtr
). -
Data Conversion:
Conversion between Objective‑C types (e.g.NSArray
,NSNumber
,NSString
) and their C++ counterparts will be designed to be lightweight, using direct pointer references or minimal copying where possible. -
Swift Bridging:
UsingNS_SWIFT_NAME
annotations will allow the Objective‑C APIs to be automatically imported into Swift with natural naming (e.g. the method- (BOOL)load:(NSError **)error
will become a Swift throwing method). This guarantees a native feel on both languages. -
Future Extensions:
While the initial implementation will cover the main execution APIs (loading models, executing methods, managing inputs/outputs via Value and Tensor), the design will be flexible enough to later include support for more specialized features (such as detailed event tracing or method metadata).
This RFC lays out the overall scope and rationale for the bindings. More detailed design and API refinements (for each of the Module
, Value
, and Tensor
interfaces) will be proposed in follow‑up issues.
Sub-issues
Metadata
Metadata
Assignees
Labels
Type
Projects
Status