Skip to content

Conversation

krodak
Copy link
Member

@krodak krodak commented Sep 19, 2025

Introduction

This PR adds support for Swift static / class functions and properties when exporting Swift funcionality to JS / TS using BridgeJS plugin.

Overview

Both static / class functions are translated to static in JS.
Everything beside willSet / didSet is supported similarly to instance properties.
Static functions in namespaced enums are translated to globalThis namespaced functions.
Examples can be found in added documentation.

Additional changes

  • Abstracted ABI naming
  • Simplified and tidied up some parts of namespacing
  • Fixed issue with nested namespaces - in case of nested namespaces, some elements like functions and properties would be gathered only from most-nested enum

Resolves

Testing

Added tests for different scenarios for all supported optional data types, including parameters, properties and return value usage.

Documentation

Extended current documentation with new

@krodak krodak self-assigned this Sep 19, 2025
Copy link
Member

@kateinoigakukun kateinoigakukun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, looks good to me, modulo the enum static properties and methods APIs. Thanks!

Comment on lines 265 to 268
Calculator.square = function(value) {
const ret = instance.exports.bjs_Calculator_static_square(value);
return ret;
};
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think BridgeJS should support multiple instantiations as long as the global namespace feature is not used in principle, so I'd like to keep any APIs accessing an instance in the Exports set returned by an instantiation.

So I expect the generated TS interface would have something like below (we need a better naming convention for the constant value type and the type holding static methods though 😅):

export const Calculator: {
    readonly Scientific: 0;
    readonly Basic: 1;
};
export type CalculatorConst = typeof Calculator[keyof typeof Calculator];

export type Exports = {
    Calculator: {
        square(value: number): number;
    }
}

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds reasonable 👌 I’ll update PR after next week though, so hang in there 🙏

What about ‚namespace enums’, are you fine with globalThis namespaces properties and functions as implemented now?

Copy link
Member

@kateinoigakukun kateinoigakukun Sep 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks!

For namespace enums, I have been thinking more about that feature, but we might need to split the "namespacing" feature and the "exposing to global" feature for better consistency.

@JS enum Utils {
    @JS enum String {
        @JS static func uppercase(_ text: String) -> String {
            return text.uppercased()
        }
    }
}
@JS(namespace: "Networking.APIV2")
enum Internal {
    @JS enum SupportedMethod {
        case get
        case post
        static func describe(_ method: SupportedMethod) -> String { ... }
    }
}

I expect the above code will generate the following interface by default:

namespace Networking {
    namespace APIV2 {
        namespace Internal {
            type SupportedMethod = typeof Networking.APIV2.Internal.SupportedMethod[keyof typeof Networking.APIV2.Internal.SupportedMethod];
        }
    }
}

export const Networking = {
    APIV2: {
        Internal {
            SupportedMethod: {
                Get: 0;
                Post: 1;
            }
        }
    }
}

export type Exports = {
    Utils: {
        String: {
            uppercase(text: string): string;
        }
    },
    Networking: {
        APIV2 {
            Internal: {
                SupportedMethod: {
                    describe(method: Networking.APIV2.Internal.SupportedMethod): string;
                },
            }
        }
    }
}

Then if we set { "exposeToGlobal": true } in bridge-js.config.json, then it will add:

+namespace global {
     namespace Networking {
         namespace APIV2 {
             namespace Internal {
                 type SupportedMethod = typeof Networking.APIV2.Internal.SupportedMethod[keyof typeof Networking.APIV2.Internal.SupportedMethod];
             }
         }
     }
+}
 
 export const Networking = {
     APIV2: {
         Internal {
             SupportedMethod: {
                 Get: 0;
                 Post: 1;
+                describe(method: Networking.APIV2.Internal.SupportedMethod): string;
             }
         }
     }
 }

But I think that change would be out of scope in this PR, so I'm fine with keeping it as is for now.

@krodak
Copy link
Member Author

krodak commented Sep 30, 2025

@kateinoigakukun addressed feedback, for namespace and global started new issue: https://github.com/swiftwasm/JavaScriptKit/issues

As for current changes, as agreed, I went with following approach:

export const CalculatorValues: {
    readonly Scientific: 0;
    readonly Basic: 1;
};
export type CalculatorTag = typeof CalculatorValues[keyof typeof CalculatorValues];

export type CalculatorObject = typeof CalculatorValues & {
    square(value: number): number;
};

export type Exports = {
  
    Calculator: CalculatorObject
    APIResult: APIResultObject
}

I started with applying this pattern only to enums with static properties / functions, but eventually decided to apply this for all enums, to keep everything consistent and avoid API changes if enum would be extended by static content later on 🙏🏻
All documentation is adjusted with the changes as well.

@krodak krodak force-pushed the feat/static-support branch from 0828f80 to f286bd8 Compare September 30, 2025 10:00
Copy link
Member

@kateinoigakukun kateinoigakukun left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks so much!

@kateinoigakukun kateinoigakukun merged commit 2bd6e38 into swiftwasm:main Sep 30, 2025
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants