Skip to content

Native Objective‑C/Swift Wrapper for the ExecuTorch Module #8363

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 · 0 comments
Open

Native Objective‑C/Swift Wrapper for the ExecuTorch Module #8363

shoumikhin opened this issue Feb 11, 2025 · 0 comments
Assignees
Labels
module: ios Issues related to iOS code, build, and execution module: user experience Issues related to reducing friction for users triaged This issue has been looked at a team member, and triaged and prioritized into an appropriate module

Comments

@shoumikhin
Copy link
Contributor

shoumikhin commented Feb 11, 2025

🚀 The feature, motivation and pitch

Introduce a native Objective‑C wrapper (ExecuTorchModule) for the C++ Module class, enabling model loading, execution, and basic input/output management from Objective‑C and Swift. In Swift, the wrapper appears as Module thanks to NS_SWIFT_NAME annotations.

Alternatives

See the parent issue #8360 for motivation and alternatives overview.

Additional context

Some capabilities from the underlying C++ Module are intentionally omitted in this initial wrapper to keep the minimal API subset. For example:

  • Method Metadata (e.g. method_meta)

  • Per‑Method Event Tracer

  • Custom Memory Allocators
    These advanced features require additional wrap classes or more granular control that is not yet part of this proposal. We may add them in future iterations if there is demand.

Usage Examples:

Objective‑C:

#import <ExecuTorch/ExecuTorch.h>

NSError *error = nil;
ExecuTorchModule *module = [[ExecuTorchModule alloc] initWithFilePath:@"/path/to/model.pte"];
BOOL didLoad = [module loadMethod:@"forward" error:&error];
if (!didLoad) {
  NSLog(@"Error: %@", error);
  return;
}
NSArray<ExecuTorchValue *> *inputs = ...; // Define input values.
NSArray<ExecuTorchValue *> *outputs = [module forwardWithInputs:inputs error:&error];
if (outputs) {
  NSLog(@"Result: %@", outputs);
} else {
  NSLog(@"Error: %@", error);
}

Swift (auto‑bridged):

import ExecuTorch

do {
  let module = Module(filePath: "/path/to/model.pte")
  try module.load("forward")

  let inputs = ... // Define input values.
  let outputs = try module.forward(inputs)
  print("Outputs:", outputs)
} catch {
  print("Error:", error)
}

Conversions between NSArray of ExecuTorchValue and std::vector<EValue> are implemented with minimal overhead. All bridging logic resides in the wrapper, so user code remains concise.

RFC (Optional)

Internal Representation

std::unique_ptr<executorch::extension::Module> _module;

The wrapper keeps a unique pointer to the native Module. Methods like -load: and -executeMethod:withInputs:error: directly call corresponding C++ functions (_module->load(), _module->execute()) and convert results or error codes into Objective‑C types.

Key Methods:

  • Initialization:

Constructs a Module from a file path and an optional load mode (file vs. mmap, etc.).

Objective-C

- (instancetype)initWithFilePath:(NSString *)filePath;
- (instancetype)initWithFilePath:(NSString *)filePath
                        loadMode:(ExecuTorchModuleLoadMode)loadMode NS_DESIGNATED_INITIALIZER;

Swift:

let module = Module(filePath: "path/to/model.pte")
let moduleWithLoadMode = Module(filePath: "path/to/model.pte", loadMode: .mmap)
  • Loading:

Invokes corresponding Module methods, e.g. _module->load(); and returns YES on success, and on failure populates error.

Objective-C

- (BOOL)load:(NSError **)error;
- (BOOL)isLoaded;
- (BOOL)loadMethod:(NSString *)methodName
             error:(NSError **)error NS_SWIFT_NAME(load(_:));
- (BOOL)isMethodLoaded:(NSString *)methodName NS_SWIFT_NAME(isLoaded(_:));

Swift:

try module.load("forward")
  • Get Method Names
- (nullable NSSet<NSString *> *)methodNames:(NSError **)error;
  • Method Execution:

Converts input ExecuTorchValues to C++ EValues, calls _module->execute(methodName, inputs), and wraps outputs back into Objective‑C objects.

Objective-C

- (nullable NSArray<ExecuTorchValue *> *)executeMethod:(NSString *)methodName
                                                 error:(NSError **)error NS_SWIFT_NAME(execute(_:));
- (nullable NSArray<ExecuTorchValue *> *)executeMethod:(NSString *)methodName
                                             withInput:(ExecuTorchValue *)value
                                                 error:(NSError **)error NS_SWIFT_NAME(execute(_:_:));
- (nullable NSArray<ExecuTorchValue *> *)executeMethod:(NSString *)methodName
                                            withInputs:(NSArray<ExecuTorchValue *> *)values
                                                 error:(NSError **)error NS_SWIFT_NAME(execute(_:_:));

Swift:

let outputs1 = try module.execute("myMethod")
let inputValue = Value(42)
let outputs2 = try module.execute("myMethod", inputValue)
let inputValues: [Value] = [Value(1), Value(2)]
let outputs3 = try module.execute("myMethod", inputValues)
  • Forward:

Convenience variations for calling the “forward” method.

Objective-C

- (nullable NSArray<ExecuTorchValue *> *)forward:(NSError **)error;
- (nullable NSArray<ExecuTorchValue *> *)forwardWithInput:(ExecuTorchValue *)value
                                                    error:(NSError **)error NS_SWIFT_NAME(forward(_:));
- (nullable NSArray<ExecuTorchValue *> *)forwardWithInputs:(NSArray<ExecuTorchValue *> *)values
                                                     error:(NSError **)error NS_SWIFT_NAME(forward(_:));
- (nullable NSArray<ExecuTorchValue *> *)forwardWithTensors:(NSArray<ExecuTorchTensor *> *)tensors
                                                      error:(NSError **)error NS_SWIFT_NAME(forward(_:));

Swift:

let outputs1 = try module.forward()
let input = Value(3.14)
let outputs2 = try module.forward(input)
let inputs: [Value] = [Value(1), Value(2)]
let outputs3 = try module.forward(inputs)
let tensor = Tensor([1, 2, 3])
let outputs4 = try module.forward([tensor])
  • Set/Get Inputs/Outputs:

For advanced usage, allows specifying input/output EValues on a per‑index basis (e.g. _module->set_input(...)).

Objective-C

- (BOOL)setInput:(ExecuTorchValue *)value
         atIndex:(NSUInteger)index
           error:(NSError **)error NS_SWIFT_NAME(setInput(_:at:));
- (BOOL)setInputs:(NSArray<ExecuTorchValue *> *)values
            error:(NSError **)error NS_SWIFT_NAME(setInputs(_:));
- (BOOL)setInput:(ExecuTorchValue *)value
       forMethod:(NSString *)methodName
         atIndex:(NSUInteger)index
           error:(NSError **)error NS_SWIFT_NAME(setInput(_:for:at:));
- (BOOL)setInputs:(NSArray<ExecuTorchValue *> *)values
        forMethod:(NSString *)methodName
            error:(NSError **)error NS_SWIFT_NAME(setInputs(_:for:));
- (BOOL)setOutput:(ExecuTorchValue *)value
            error:(NSError **)error NS_SWIFT_NAME(setOutput(_:));
- (BOOL)setOutput:(ExecuTorchValue *)value
          atIndex:(NSUInteger)index
            error:(NSError **)error NS_SWIFT_NAME(setOutput(_:at:));
- (BOOL)setOutput:(ExecuTorchValue *)value
        forMethod:(NSString *)methodName
            error:(NSError **)error NS_SWIFT_NAME(setOutput(_:for:));
- (BOOL)setOutput:(ExecuTorchValue *)value
        forMethod:(NSString *)methodName
          atIndex:(NSUInteger)index
            error:(NSError **)error NS_SWIFT_NAME(setOutput(_:for:at:));

Swift:

let input = Value(10)
try module.setInput(input, for: "forward", at: 0)
let inputs: [Value] = [Value(1), Value(2)]
try module.setInputs(inputs)  // Assuming "forward" method.
let output = Value(Tensor(0))
try module.setOutput(output, for: "forward", at: 0)
  • Error Handling & Reporting:

    • C++ return codes of type ::executorch::runtime::Error are mapped to NSError instances (using domain ExecuTorchErrorDomain).
    • Swift sees these as throwing errors, ensuring a natural Swift error‑handling flow.

Example of a method throwing an error:

- (BOOL)load:(NSError **)error {
  const auto errorCode = _module->load();
  if (errorCode != ::executorch::runtime::Error::Ok) {
    if (error) {
      *error = [NSError errorWithDomain:ExecuTorchErrorDomain
                                   code:(NSInteger)errorCode
                               userInfo:nil];
    }
    return NO;
  }
  return YES;
}
  • Value Conversion

To achieve the minimal overhead during value conversions, Module can use something like the following helper functions internally:

static inline
::executorch::runtime::EValue ConvertValue(ExecuTorchValue *value) {
  if (value.isTensor) {
    auto *nativeTensorPtr = value.tensorValue.nativeInstance;
    ET_CHECK(nativeTensorPtr);
    auto nativeTensor = *reinterpret_cast<::executorch::extension::TensorPtr *>(nativeTensorPtr);
    ET_CHECK(nativeTensor);
    return *nativeTensor;
  }
  // Handle other value types.
  return ::executorch::runtime::EValue();
}

static inline
ExecuTorchValue *ConstructValue(::executorch::runtime::EValue value) {
  if (value.isTensor()) {
    auto nativeInstance = ::executorch::extension::make_tensor_ptr(value.toTensor());
    return [ExecuTorchValue valueWithTensor:[[ExecuTorchTensor alloc] initWithNativeInstance:&nativeInstance]];
  }
  // Handle other value types.
  return [ExecuTorchValue new];
}

cc @mergennachin @byjlw

@shoumikhin shoumikhin added feature 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 labels Feb 11, 2025
@shoumikhin shoumikhin self-assigned this Feb 11, 2025
@github-project-automation github-project-automation bot moved this to To triage in ExecuTorch DevX Feb 11, 2025
@mergennachin mergennachin added the module: user experience Issues related to reducing friction for users label 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 Backlog in ExecuTorch DevX Feb 11, 2025
@jackzhxng jackzhxng removed the feature label Feb 21, 2025
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71915597
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71916550
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71916550

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71917236
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71917236

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71918193
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71918193

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71919658
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71919658

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71921054
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71921054

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71924237
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71924237

Co-authored-by: Anthony Shoumikhin <[email protected]>
facebook-github-bot pushed a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71936820
kirklandsign added a commit that referenced this issue Mar 27, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71936820

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71916550

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71917236

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71918193

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71919658

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71921054

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: mergennachin

Differential Revision: D71924237

Co-authored-by: Anthony Shoumikhin <[email protected]>
kirklandsign added a commit that referenced this issue Apr 11, 2025
Summary: #8363

Reviewed By: bsoyluoglu

Differential Revision: D71936820

Co-authored-by: Anthony Shoumikhin <[email protected]>
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 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

3 participants