πŸš€ 8.9 Released! β†’ ⚑️ New Node-API Engine Preview, πŸ“² ns widget ios, πŸ’… Tailwind v4 and more...
Read Announcement

View on GitHub

@nativescript/swift-ui ​

Use SwiftUI with NativeScript.

Contents ​

Installation ​

cli
npm install @nativescript/swift-ui

Note

you will need to target iOS 13 at a minimum.

For example, you can add this line to your App_Resources/iOS/build.xcconfig:

IPHONEOS_DEPLOYMENT_TARGET = 13.0

Note

If you would like to use NativeScriptView inside SwiftUI, you should target 14.0 minimum.

Usage ​

SwiftUI Source Files Example

1. Create your SwiftUI view ​

This can be any SwiftUI view you'd like to create.

swift
importSwiftUI

structSampleView: View {

var body: some View {
VStack {
Text("Hello World")
 .padding()
 }
 }
}

2. Create your SwiftUI view Provider ​

This will prepare your SwiftUI for two-way data bindings to NativeScript and follows the plugins' SwiftUIProvider protocol to standardize all SwiftUI bindings.

ts
import SwiftUI

@objc
class SampleViewProvider: UIViewController, SwiftUIProvider {

// MARK: INIT

 required init?(coder aDecoder: NSCoder) {
 super.init(coder: aDecoder)
 }

 required public init() {
 super.init(nibName: nil, bundle: nil)
 }

 public override func viewDidLoad() {
 super.viewDidLoad()
 setupSwiftUIView(content: swiftUIView)
 }

// MARK: PRIVATE

 private var swiftUIView = SampleView()

/// Receive data from NativeScript
 func updateData(data: NSDictionary) {
// can be empty
 }

/// Allow sending of data to NativeScript
 var onEvent: ((NSDictionary) -> ())?
}

3. Register your SwiftUI with an identifier and use it in markup ​

This can be done in the bootstrap file (often app.ts or main.ts) or even the view component that needs it.

Core ​

typescript
import { registerSwiftUI, UIDataDriver } from'@nativescript/swift-ui'

// A. You can generate types for your own Swift Provider with 'ns typings ios'
// B. Otherwise you can ignore by declaring the class name you know you provided
declareconstSampleViewProvider:any

registerSwiftUI(
'sampleView',
 (view) =>newUIDataDriver(SampleViewProvider.alloc().init(), view),
)

registerSwiftUI(
'barChart',
 (view) =>newUIDataDriver(BarChartProvider.alloc().init(), view),
)

Then insert it in any layout as follows:

xml
<Pagexmlns="http://schemas.nativescript.org/tns.xsd"navigatingTo="navigatingTo"class="page"xmlns:sw="@nativescript/swift-ui">
 <StackLayout>
 <sw:SwiftUIswiftId="sampleView"height="150" />
 </StackLayout>
</Page>

Generate Types ​

  1. To generate types for your SwiftUI code, run ns typings ios.

  2. Locate the objc!nsswiftsupport.d.ts file in typings/x86_64 and reference it in the references.d.ts.

SwiftUI with Angular ​

Register SwiftUI follows:

ts
import { registerElement } from'@nativescript/angular'
import { SwiftUI } from'@nativescript/swift-ui'

registerElement('SwiftUI', () => SwiftUI)

registerSwiftUI(
'sampleView',
 (view) =>newUIDataDriver(SampleViewProvider.alloc().init(), view),
)
registerSwiftUI(
'barChart',
 (view) =>newUIDataDriver(BarChartProvider.alloc().init(), view),
)

It can now be used within any Angular component, eg:

html
<StackLayoutclass="p-20">
 <SwiftUIswiftId="sampleView"height="150"></SwiftUI>
</StackLayout>

SwiftUI with Vue ​

Register SwiftUI follows:

ts
registerElement('SwiftUIView', () =>require('@nativescript/swift-ui').SwiftUI)

registerSwiftUI(
'sampleView',
 (view) =>newUIDataDriver(SampleViewProvider.alloc().init(), view),
)
registerSwiftUI(
'barChart',
 (view) =>newUIDataDriver(BarChartProvider.alloc().init(), view),
)

Then use it in markup as follows:

xml
<StackLayout>

 <SwiftUIViewswiftId="sampleView"height="200" />
 <SwiftUIViewheight="500"swiftId="barChart"margin="30" />

</StackLayout>

SwiftUI with React ​

Register SwiftUI follows:

ts
registerSwiftUI(
'sampleView',
 (view) =>newUIDataDriver(SampleViewProvider.alloc().init(), view),
)

registerSwiftUI(
'barChart',
 (view) =>newUIDataDriver(BarChartProvider.alloc().init(), view),
)

interfaceSwiftUIViewAttributesextendsViewAttributes {
swiftId:string
}

declare global {
moduleJSX {
interfaceIntrinsicElements {
swiftUIView:NativeScriptProps<SwiftUIViewAttributes, SwiftUI>
 }
 }
}

registerElement('swiftUIView', () =>require('@nativescript/swift-ui').SwiftUI)

Then use it in markup as follows:

xml
<stackLayout>

 <swiftUIViewswiftId="sampleView"height="200" />
 <swiftUIViewheight="500"swiftId="barChart"margin="30" />

</stackLayout>

Open Multiple Scenes ​

When using a SwiftUI App Lifecycle setup for your NativeScript app, the default with visionOS development, you can enable multiple scenes in your Info.plist with the following:

xml
<key>UIApplicationSceneManifest</key>
<dict>
 <key>UIApplicationPreferredDefaultSceneSessionRole</key>
 <string>UIWindowSceneSessionRoleApplication</string>
 <key>UIApplicationSupportsMultipleScenes</key>
 <true/>
 <key>UISceneConfigurations</key>
 <dict/>
</dict>

You can now use WindowManager (for use with standard Windows) or XR (for use with immersive spaces) to interact with multiple scenes, for example:

swift
@main
structNativeScriptApp: App {
@Stateprivatevar immersionStyle: ImmersionStyle = .mixed

var body: some Scene {
NativeScriptMainWindow()

WindowGroup(id: "NeatView") {
NeatView()
 }
 .windowStyle(.plain)

ImmersiveSpace(id: "NeatImmersive") {
NeatImmersive()
 }
 .immersionStyle(selection: $immersionStyle, in: .mixed, .full)
 }
}

You could open the WindowGroup with:

ts
import { WindowManager } from"@nativescript/swift-ui";

WindowManager.getWindow("NeatView").open();
});

And you could open the ImmersiveSpace with:

ts
import { XR } from'@nativescript/swift-ui'

XR.requestSession('NeatImmersive')

You could update either scene with:

ts
import { WindowManager } from'@nativescript/swift-ui'

// Option A: inline
WindowManager.getWindow('NeatView').update({
 title: 'Updated Title',
})

// Option B: reference
constneatView= WindowManager.getWindow('NeatView')

neatView.update({
 title: 'Updated Title',
})

// Both options work with XR/Immersive Spaces as well, for example:
WindowManager.getWindow('NeatImmersive').update({
 salutation: 'Hello World',
})

Passing contextual data to scenes ​

You can use the onReceive modifier in SwiftUI to handle any data passed to your windows.

For example, anytime WindowManager.getWindow("Window_ID").update(...) is called, a Notification is dispatched which can be picked up for data to be handled:

swift
structNeatView: View {
@Statevar context: NativeScriptWindowContext?

var body: some View {
ZStack {
// more neat views here
 }.onReceive(NotificationCenter.default.publisher(for: NSNotification.Name("NativeScriptWindowUpdate")), perform: { obj in
 context = NativeScriptWindowFactory.shared.getContextForId(id: "NeatView")

let title = context!.data["title"] as!String

// use your updated title!
 })
 }
}

Closing windows ​

WindowManager.getWindow("NeatView").close() for a Window which is already open will close it.

XR.endSession() for an Immersive Space which is already open will close it.

Use NativeScriptView inside SwiftUI ​

You can also use NativeScript view layouts and components inside SwiftUI when targeting iOS 14.0 minimum.

Add this line to your App_Resources/iOS/build.xcconfig:

IPHONEOS_DEPLOYMENT_TARGET = 14.0

You can now register as many NativeScript views by an id for usage:

ts
import { SwiftUIManager } from'@nativescript/swift-ui'

SwiftUIManager.registerNativeScriptViews({
 Video: SceneVideoComponent,
})

This will allow SceneVideoComponent, a NativeScript view component, to be used inside any SwiftUI component:

swift
structContentView: View {

var body: some View {

ZStack {
NativeScriptView(id: "Video")
 }
 }
}

Credits ​

  • WindowManager and XR APIs were established with the Callstack team. Shoutout to: Oskar KwaΕ›niewski.
Valor Software

NativeScript is proudly supported by Valor Software as an official partner. We are proud to offer guidance, consulting, and development assistance in all things NativeScript.

Contact Valor for assistance.

License ​

MIT

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /