{"id":27515726,"url":"https://github.com/ikhvorost/reactbridge","last_synced_at":"2026-03-06T13:04:11.116Z","repository":{"id":183402775,"uuid":"670042828","full_name":"ikhvorost/ReactBridge","owner":"ikhvorost","description":"Swift Macros for React Native","archived":false,"fork":false,"pushed_at":"2025-03-03T21:14:09.000Z","size":120,"stargazers_count":44,"open_issues_count":1,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-01T15:09:27.482Z","etag":null,"topics":["javascript","react-native","swift","swift-macros","swift-package-manager","xcode"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ikhvorost.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-07-24T07:04:57.000Z","updated_at":"2025-03-25T23:35:00.000Z","dependencies_parsed_at":null,"dependency_job_id":"eb18f060-c874-4b97-839d-e05c61f021de","html_url":"https://github.com/ikhvorost/ReactBridge","commit_stats":null,"previous_names":["ikhvorost/reactbridge"],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikhvorost%2FReactBridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikhvorost%2FReactBridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikhvorost%2FReactBridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ikhvorost%2FReactBridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ikhvorost","download_url":"https://codeload.github.com/ikhvorost/ReactBridge/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249404402,"owners_count":21265806,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","react-native","swift","swift-macros","swift-package-manager","xcode"],"created_at":"2025-04-18T01:18:29.689Z","updated_at":"2026-03-06T13:04:10.961Z","avatar_url":"https://github.com/ikhvorost.png","language":"Swift","funding_links":["https://www.paypal.com/donate/?hosted_button_id=TSPDD3ZAAH24C"],"categories":[],"sub_categories":[],"readme":"# ReactBridge\n\n[![Swift 6.2, 6.1, 6.0](https://img.shields.io/badge/Swift-6.2%20|%206.1%20|%206.0-f48041.svg?style=flat\u0026logo=swift)](https://developer.apple.com/swift)\n![Platforms: iOS, macOS, tvOS, visionOS, watchOS](https://img.shields.io/badge/Platforms-iOS%20|%20macOS%20|%20tvOS%20|%20visionOS%20|%20watchOS%20-blue.svg?style=flat\u0026logo=apple)\n[![Swift Package Manager: compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-4BC51D.svg?style=flat\u0026logo=apple)](https://swift.org/package-manager/)\n[![React Native: 0.60](https://img.shields.io/badge/React%20Native-0.60-61dafb.svg?style=flat\u0026logo=react)](https://reactnative.dev/)\n[![Build](https://github.com/ikhvorost/ReactBridge/actions/workflows/swift.yml/badge.svg)](https://github.com/ikhvorost/ReactBridge/actions/workflows/swift.yml)\n[![codecov](https://codecov.io/gh/ikhvorost/ReactBridge/graph/badge.svg?token=0MNGNA5W90)](https://codecov.io/gh/ikhvorost/ReactBridge)\n[![Swift Doc Coverage](https://img.shields.io/badge/Swift%20Doc%20Coverage-100%25-f39f37?logo=google-docs\u0026logoColor=white)](https://github.com/ikhvorost/swift-doc-coverage)\n\n[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/donate/?hosted_button_id=TSPDD3ZAAH24C)\n\n`ReactBridge` provides Swift macros for React Native to expose Native Modules and UI Components to JavaScript.\n\n- [Getting Started](#getting-started)\n  - [Native Module](#native-module)\n  - [Native UI Component](#native-ui-component)\n- [Documentation](#documentation)\n  - [`@ReactModule`](#reactmodule)\n  - [`@ReactMethod`](#reactmethod)\n  - [`@ReactView`](#reactview)\n  - [`@ReactProperty`](#reactproperty)\n- [Requirements](#requirements)\n- [Installation](#installation)\n  - [XCode](#xcode)\n  - [Swift Package](#swift-package)\n- [Licensing](#licensing)\n\n## Getting Started\n\n### Native Module\n\nAttach `@ReactModule` macro to your class definition and it exports and registers the native module class with React Native and that will allow you to access its code from JavaScript:\n\n``` swift\nimport React\nimport ReactBridge\n\n@ReactModule\nclass CalendarModule: NSObject, RCTBridgeModule {\n}\n```\n\n\u003e **Note**\n\u003e `ReactBridge` requires to import `React` library.\n\u003e Swift class must be inherited from `NSObject` and must conform to `RCTBridgeModule` protocol.\n\nThe `@ReactModule` macro also takes optional `jsName` argument that specifies the name that will be accessible as in your JavaScript code:\n\n``` swift\n@ReactModule(jsName: \"Calendar\")\nclass CalendarModule: NSObject, RCTBridgeModule {\n}\n```\n\n\u003e **Note**\n\u003e If you do not specify a name, the JavaScript module name will match the Swift class name.\n\nNow the native module can then be accessed in JavaScript like this:\n\n``` js\nimport { NativeModules } from 'react-native';\nconst { Calendar } = NativeModules;\n```\n\n**Methods**\n\nReact Native will not expose any methods in a native module to JavaScript unless explicitly told to. This can be done using the `@ReactMethod` macro:\n\n``` swift\n@ReactModule(jsName: \"Calendar\")\nclass CalendarModule: NSObject, RCTBridgeModule {\n  \n  @ReactMethod\n  @objc func createEvent(title: String, location: String) {\n    print(\"Create event '\\(title)' at '\\(location)'\")\n  }\n}\n```\n\n\u003e **Note**\n\u003e The exported method must be marked with `@objc` attribute.\n\nNow that you have the native module available, you can invoke your native method `createEvent()`:\n\n``` js\nCalendar.createEvent('Wedding', 'Las Vegas');\n```\n\n**Callbacks**\n\nMethods marked with `@ReactMethod` macro are asynchronous by default but if it's needed to pass data from Swift to JavaScript you can use the callback parameter with `RCTResponseSenderBlock` type:\n\n``` swift\n@ReactMethod\n@objc func createEvent(title: String, location: String, callback: RCTResponseSenderBlock) {\n  print(\"Create event '\\(title)' at '\\(location)'\")\n  let eventId = 10;\n  callback([eventId])\n}\n```\n\nThis method could then be accessed in JavaScript using the following:\n\n``` js\nCalendar.createEvent('Wedding', 'Las Vegas', eventId =\u003e {\n  console.log(`Created a new event with id ${eventId}`);\n});\n```\n\n**Promises**\n\nNative modules can also fulfill promises, which can simplify your JavaScript, especially when using async/await syntax:\n\n``` swift\n@ReactMethod\n@objc func createEvent(title: String, location: String, resolve: RCTPromiseResolveBlock, reject: RCTPromiseRejectBlock) {\n  do {\n    let eventId = try createEvent(title: title, location: location)\n    resolve(eventId)\n  }\n  catch let error as NSError {\n    reject(\"\\(error.code)\", error.localizedDescription, error)\n  }\n}\n```\n\nThe JavaScript counterpart of this method returns a promise:\n\n``` js\nCalendar.createEvent('Wedding', 'Las Vegas')\n  .then(eventId =\u003e {\n    console.log(`Created a new event with id ${eventId}`);\n  })\n  .catch(error =\u003e {\n    console.error(`Error: ${error}`);\n  });\n```\n\n**Inheritance**\n\nIt's possible to inherit an other native module (which implements `RCTBridgeModule` protocol) and override existing or append additional functionality. For instance, to signal events to JavaScript you can subclass `RCTEventEmitter`:\n\n``` swift\n@ReactModule\nclass EventEmitter: RCTEventEmitter {\n  \n  static private(set) var shared: EventEmitter?\n  \n  override init() {\n    super.init()\n    Self.shared = self\n  }\n  \n  override func supportedEvents() -\u003e [String]! {\n    [\"EventReminder\"]\n  }\n  \n  func sendReminderEvent(title: String) {\n    sendEvent(withName: \"EventReminder\", body: [\"title\" : title])\n  }\n}\n\n...\n\nEventEmitter.shared?.sendReminderEvent(title: \"Dinner Party\")\n```\n\nThen in JavaScript you can create `NativeEventEmitter` with your module and subscribe to a particular event:\n\n``` js\nconst { EventEmitter } = NativeModules;\n\nthis.eventEmitter = new NativeEventEmitter(EventEmitter);\nthis.emitterSubscription = this.eventEmitter.addListener('EventReminder', event =\u003e {\n  console.log(event); // Prints: { title: 'Dinner Party' }\n});\n```\n\nFor more details about Native Modules, see: https://reactnative.dev/docs/native-modules-ios.\n\n### Native UI Component\n\nTo expose a native view you should attach `@ReactView` macro to a subclass of `RCTViewManager` that is also typically the delegate for the view, sending events back to JavaScript via the bridge.\n\n``` swift\nimport React\nimport ReactBridge\nimport MapKit\n\n@ReactView\nclass MapView: RCTViewManager {\n\n  override func view() -\u003e UIView {\n    MKMapView()\n  }\n}\n```\n\nThen you need a little bit of JavaScript to make this a usable React component:\n\n``` js\nimport {requireNativeComponent} from 'react-native';\n\nconst MapView = requireNativeComponent('MapView');\n\n... \nrender() {\n  return \u003cMapView style={{flex: 1}} /\u003e;\n}\n```\n\n**Properties**\n\nTo bridge over some native properties of a native view we can declare properties with the same name on our view manager class and mark them with `@ReactProperty` macro. Let's say we want to be able to disable zooming:\n\n``` swift\n@ReactView\nclass MapView: RCTViewManager {\n\n  @ReactProperty\n  var zoomEnabled: Bool?\n\n  override func view() -\u003e UIView {\n    MKMapView()\n  }\n}\n```\n\n\u003e **Note**\n\u003e The target properties of a view must be visible for Objective-C.\n\nNow to actually disable zooming, we set the property in JavaScript:\n\n``` js\n\u003cMapView style={{flex: 1}} zoomEnabled={false} /\u003e\n```\n\nFor more complex properties you can pass `json` from JavaScript directly to native properties of your view (if they are implemented) or use `isCustom` argument to inform React Native that a custom setter is implemented on your view manager:\n\n``` swift\n@ReactView\nclass MapView: RCTViewManager {\n\n  @ReactProperty\n  var zoomEnabled: Bool?\n  \n  @ReactProperty(isCustom: true)\n  var region: [String : Double]?\n  \n  @objc \n  func set_region(_ json: [String : Double]?, forView: MKMapView?, withDefaultView: MKMapView?) {\n    guard let latitude = json?[\"latitude\"],\n          let latitudeDelta = json?[\"latitudeDelta\"],\n          let longitude = json?[\"longitude\"],\n          let longitudeDelta = json?[\"longitudeDelta\"]\n    else {\n      return\n    }\n    \n    let region = MKCoordinateRegion(\n      center: CLLocationCoordinate2D(latitude: latitude, longitude: longitude), \n      span: MKCoordinateSpan(latitudeDelta: latitudeDelta, longitudeDelta: longitudeDelta)\n    )\n    forView?.setRegion(region, animated: true)\n  }\n  \n  override func view() -\u003e UIView {\n    MKMapView()\n  }\n}\n```\n\n\u003e **Note**\n\u003e The custom setter must have the following signature: `@objc func set_Name(_ value: Type, forView: ViewType?, withDefaultView: ViewType?)`\n\nJavaScript code with `region` property:\n\n``` js\n\u003cMapView\n  style={{flex: 1}}\n  zoomEnabled={false}\n  region={{\n    latitude: 37.48,\n    longitude: -122.1,\n    latitudeDelta: 0.1,\n    longitudeDelta: 0.1,\n  }}\n/\u003e\n```\n\n**Events**\n\nTo deal with events from the user like changing the visible region we can map input event handlers from JavaScript to native view properties with `RCTBubblingEventBlock` type.\n\nLets add new `onRegionChange` property to a subclass of MKMapView:\n\n``` swift\nclass NativeMapView: MKMapView {\n  @objc var onRegionChange: RCTBubblingEventBlock?\n}\n\n@ReactView\nclass MapView: RCTViewManager {\n  \n  @ReactProperty\n  var onRegionChange: RCTBubblingEventBlock?\n  \n  override func view() -\u003e UIView {\n    let mapView = NativeMapView()\n    mapView.delegate = self\n    return mapView\n  }\n}\n\nextension MapView: MKMapViewDelegate {\n  \n  func mapView(_ mapView: MKMapView, regionDidChangeAnimated animated: Bool) {\n    guard let mapView = mapView as? NativeMapView else {\n      return\n    }\n    \n    let region = mapView.region\n    mapView.onRegionChange?([\n      \"latitude\": region.center.latitude,\n      \"longitude\": region.center.longitude,\n      \"latitudeDelta\": region.span.latitudeDelta,\n      \"longitudeDelta\": region.span.longitudeDelta,\n    ])\n  }\n}\n```\n\n\u003e **Note**\n\u003e All properties with `RCTBubblingEventBlock` must be prefixed with `on` and marked with `@objc`.\n\nCalling the `onRegionChange` event handler property results in calling the same callback property in JavaScript:\n\n``` js\nfunction App(): JSX.Element {\n  ...\n\n  this.onRegionChange = event =\u003e {\n    const region = event.nativeEvent;\n    console.log(region.latitude)\n  };\n\n  return (\n    \u003cMapView\n      style={{flex: 1}}\n      onRegionChange={this.onRegionChange}\n    /\u003e\n  );\n}\n```\n\n\n\nFor more details about Native UI Components, see: https://reactnative.dev/docs/native-components-ios.\n\n## Documentation\n\n### `@ReactModule`\n\nThe macro exports and registers a class as a native module for React Native.\n\n``` swift\n@ReactModule(jsName: String? = nil, requiresMainQueueSetup: Bool = false, methodQueue: DispatchQueue? = nil)\n```\n\n**Parameters**\n\n- **jsName**: JavaScript module name. If omitted, the JavaScript module name will match the class name.\n- **requiresMainQueueSetup**: Let React Native know if your module needs to be initialized on the main queue, before any JavaScript code executes. If value is `false` an class initializer will be called on a global queue. Defaults to `false`.\n- **methodQueue**: The queue that will be used to call all exported methods. By default exported methods will call on a global queue.\n\n### `@ReactMethod`\n\nThe macro exposes a method of a native module to JavaScript.\n\n``` swift\n@ReactMethod(jsName: String? = nil, isSync: Bool = false)\n```\n\n**Parameters**\n- **jsName**: JavaScript method name. If omitted, the JavaScript method name will match the method name.\n- **isSync**: Calling the method asynchronously or synchronously. If value is `true` the method is called from JavaScript synchronously on the JavaScript thread. Defaults to `false`.\n\n\u003e **Note**\n\u003e If you choose to use a method synchronously, your app can no longer use the Google Chrome debugger. This is because synchronous methods require the JS VM to share memory with the app. For the Google Chrome debugger, React Native runs inside the JS VM in Google Chrome, and communicates asynchronously with the mobile devices via WebSockets.\n\n### `@ReactView`\n\nThe macro exports and registers a class as a native UI component for React Native.\n\n``` swift\n@ReactView(jsName: String? = nil)\n```\n\n**Parameters**\n- **jsName**: JavaScript UI component name. If omitted, the JavaScript UI component name will match the class name.\n\n### `@ReactProperty`\n\nThe macro exports a property of a native view to JavaScript.\n\n``` swift\n@ReactProperty(keyPath: String? = nil, isCustom: Bool = false)\n```\n\n**Parameters**\n- **keyPath**: An arbitrary key path in the view to set a value.\n- **isCustom**: Handling a property with a custom setter `@objc func set_Name(_ value: Type, forView: ViewType?, withDefaultView: ViewType?)` on a native UI component. Defaults to `false`.\n\n## Requirements\n\n- Xcode 15 or later.\n- Swift 5.9 or later.\n- React Native 0.60 or later.\n\n## Installation\n\n### XCode\n\n1. Select `File \u003e Add Package Dependencies...`. (Note: The menu options may vary depending on the version of Xcode being used.)\n2. Enter the URL for the the package repository: `https://github.com/ikhvorost/ReactBridge.git`\n3. Input a specific version or a range of versions for `Dependency Rule` and a need target for `Add to Project`.\n4. Import the package in your source files: \n```\nimport React\nimport ReactBridge\n...\n```\n\n### Swift Package\n\nFor a swift package you can add `ReactBridge` directly to your dependencies in your `Package.swift` file:\n\n```swift\nlet package = Package(\n    ...\n    dependencies: [\n        .package(url: \"https://github.com/ikhvorost/ReactBridge.git\", from: \"1.0.0\")\n    ],\n    targets: [\n        .target(name: \"YourPackage\",\n            dependencies: [\n                .product(name: \"ReactBridge\", package: \"ReactBridge\")\n            ]\n        ),\n        ...\n    ...\n)\n```\n\n## Licensing\n\nThis project is licensed under the MIT License. See [LICENSE](LICENSE) for more information.\n\n[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://www.paypal.com/donate/?hosted_button_id=TSPDD3ZAAH24C)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikhvorost%2Freactbridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fikhvorost%2Freactbridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fikhvorost%2Freactbridge/lists"}