Skip to content

[RFC] Objective-C/Swift APIs for ExecuTorch #8360

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
shoumikhin opened this issue Feb 11, 2025 · 17 comments
Open

[RFC] Objective-C/Swift APIs for ExecuTorch #8360

shoumikhin opened this issue Feb 11, 2025 · 17 comments
Assignees
Labels
module: ios Issues related to iOS code, build, and execution module: user experience Issues related to reducing friction for users rfc Request for comment and feedback on a post, proposal, etc. triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module
Milestone

Comments

@shoumikhin
Copy link
Contributor

shoumikhin commented Feb 11, 2025

🚀 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

  1. Continue with manual C++ bridging: This is the current recommendation, but it's not sustainable for widespread adoption due to its complexity.
  2. 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.
  3. 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 as Module in Swift): For loading and executing models.
    • ExecuTorchValue (bridged as Value in Swift): For representing inputs and outputs (including tensors, scalars, and lists).
    • ExecuTorchTensor (bridged as Tensor 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, the ExecuTorchModule wrapper will internally hold a std::unique_ptr<executorch::extension::Module>. Similar patterns will be applied for Value (wrapping the C++ EValue variant) and Tensor (wrapping a TensorPtr).

  • 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:
    Using NS_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.

Expected files layout:
Image

cc @cbilgin @mergennachin @byjlw

@shoumikhin shoumikhin added triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module module: ios Issues related to iOS code, build, and execution feature labels Feb 11, 2025
@shoumikhin shoumikhin self-assigned this Feb 11, 2025
@mergennachin mergennachin added the module: user experience Issues related to reducing friction for users label Feb 11, 2025
@github-project-automation github-project-automation bot moved this to To triage in ExecuTorch DevX Feb 11, 2025
@mergennachin mergennachin moved this from To triage to Incoming in ExecuTorch DevX Feb 11, 2025
@mergennachin mergennachin moved this from Incoming to In progress in ExecuTorch DevX Feb 11, 2025
@mergennachin mergennachin added this to the 0.6.0 milestone Feb 11, 2025
@mergennachin
Copy link
Contributor

@shoumikhin - Thanks for creating this issue.

Regarding error code propagation:

@mergennachin
Copy link
Contributor

mergennachin commented Feb 11, 2025

cc @swolchok - what are your thoughts on @shoumikhin's design for ObjC/Swift bindings above?

@shoumikhin
Copy link
Contributor Author

@mergennachin ObjC/Swift use NSError type that contains the domain (ie. a hardcoded text label to convey what this error is related to), error code, text description and an arbitrary dictionary of extra info. In our case the error code will be directly populated by runtime::Error returned from a C++ API.

@swolchok
Copy link
Contributor

swolchok commented Feb 11, 2025

unions and some advanced language features, such as within the EValue class

this in particular is fixable with std::variant now that we have C++17

@swolchok - what are your thoughts

I haven't paid much attention to Swift in the past 5 to 7 years. With that disclaimer out of the way, I'm mildly surprised anybody still wants Objective-C.

@shoumikhin
Copy link
Contributor Author

@swolchok for us the main issue is bridging C++ into Swift properly. About a year ago Swift introduced some interoperability with C++, but it's still pretty limited to be able to automatically convert our user-facing C++ API into Swift (e.g. std::variant isn't supported, and template support is pretty limited), thus some bridging is still needed, either to C or Objective-C, which can both be converted into Swift pretty well by the compiler since the initial language release. Of course, Objective-C wrappers are more preferable in that context, also because we do have some internal customers who still use Objective-C(++) mostly for the same reason - an ability to work with other C++ libs directly.

@mergennachin
Copy link
Contributor

also because we do have some internal customers who still use Objective-C(++) mostly for the same reason - an ability to work with other C++ libs directly.

Yeah, this is a good point!

cc @dbort - if you have comments regarding our API footprint in both core and extensions.

@byjlw byjlw changed the title Objective-C/Swift APIs for ExecuTorch [RFC] Objective-C/Swift APIs for ExecuTorch Feb 12, 2025
@byjlw
Copy link
Contributor

byjlw commented Feb 12, 2025

This will definitely help a segment of iOS developers, so I think it's worth doing. Question the wrappers be in ExecuTorch or in a separate repo?

I could go either way but I love what PyTorch Lightning did having a separate abstractions library.

@mergennachin
Copy link
Contributor

mergennachin commented Feb 12, 2025

This should just be part of executorch/extensions/ especially since iOS and Android users are one of the three target personas.

@dbort
Copy link
Contributor

dbort commented Feb 13, 2025

This seems like a good step. And since it doesn't seem to require modifying the core runtime, there's room to experiment here.

Consider marking these new APIs as EXPERIMENTAL to begin with if you think they might change.

@f-meloni
Copy link
Contributor

I agree this would be a good step.
Having Objc/Swift support would also make more appealing for developers the https://github.com/pytorch/executorch/tree/main/backends/apple/coreml backend, which allows to use ExecuTorch together with native CoreML.

@bsoyluoglu
Copy link
Contributor

@mergennachin

Is it intentional that we're returning an error in ObjC case but throwing an exception for Swift? Is this the convention?

Yes, it is a swift convention. ObjC error parameters are translated as throwing to Swift. https://developer.apple.com/documentation/swift/handling-cocoa-errors-in-swift#Catch-Errors

@cbilgin cbilgin added the rfc Request for comment and feedback on a post, proposal, etc. label Feb 14, 2025
@jackzhxng jackzhxng removed the feature label Feb 21, 2025
@mergennachin
Copy link
Contributor

@bsoyluoglu @shoumikhin do you have updates on this? It'd be great to have this for 0.6 release, which the branch cut for code is on March 18

@bsoyluoglu
Copy link
Contributor

@mergennachin I am actively working on it, and I having it for that date isn't looking risky right now. If anything changes I will keep you posted

@mergennachin
Copy link
Contributor

@bsoyluoglu Great thank you.

Just for completeness, as part of this work, it would be good to also update the documentation with the new bindings too in our iOS page.

https://pytorch.org/executorch/main/using-executorch-ios.html#runtime-api

@mergennachin
Copy link
Contributor

First skeleton PR: #8826

@mergennachin mergennachin moved this from In progress to Done in ExecuTorch DevX Mar 27, 2025
@mergennachin mergennachin closed this as completed by moving to Done in ExecuTorch DevX Mar 27, 2025
@mergennachin
Copy link
Contributor

@shoumikhin okay to close or keep it for any missing features?

@shoumikhin
Copy link
Contributor Author

@mergennachin not everything is implemented there, so we could keep those RFC open for now

@mergennachin mergennachin reopened this Mar 27, 2025
@github-project-automation github-project-automation bot moved this from Done to Backlog in ExecuTorch DevX Mar 27, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
module: ios Issues related to iOS code, build, and execution module: user experience Issues related to reducing friction for users rfc Request for comment and feedback on a post, proposal, etc. triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module
Projects
Status: Backlog
Development

No branches or pull requests

9 participants