What is NSViewControllerRepresentable
In SwiftUI, there are no View Controllers, only Views. So what should we do if want to use something related to View Controllers? Here comes NSViewControllerRepresentable/UIViewControllerRepresentable. Basically, we can use instance of View Controller Representable as View, contains a view controller.
Since view controllers have their own view, ViewControllerRepresentable has not view body.
protocol NSViewControllerRepresentable : View where Self.Body == Never
A Simple Sample of NSViewControllerRepresentable
- Create a new SwiftUI app.
- Add a new file, choose Cocoa Class, name it as TextViewController.swift.
Choose create xib file as well.
- Add a close button and label.

- TextViewController.swift
//
// TextViewController.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import Cocoa
class TextViewController: NSViewController {
@IBOutlet weak var label: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func closeButtonClicked(_ sender: Any) {
self.dismiss(sender)
}
}
- TextView
//
// TextView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import AppKit
import SwiftUI
struct TextView:NSViewControllerRepresentable {
func makeNSViewController(context: Context) -> TextViewController {
let controller = TextViewController()
return controller
}
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
}
}
- ContentView.swift
//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView()
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
The app ran as below, but the close button didn't work.

Coordinator
View Controllers are class, and View Representables are structs. We use Coordinator to pass changes to SwiftUI.
- TextView
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import AppKit
import SwiftUI
struct TextView:NSViewControllerRepresentable {
@Environment(\.presentationMode) var presentationMode
@Binding var title:String
class Coordinator: NSObject, TextViewControllerDelegate {
var parent: TextView
init(_ parent: TextView) {
self.parent = parent
}
func textView(_ tvc: TextViewController) {
self.parent.presentationMode.wrappedValue.dismiss()
}
}
func makeNSViewController(context: Context) -> TextViewController {
let controller = TextViewController()
controller.delegate = context.coordinator
return controller
}
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
nsViewController.label.stringValue = title
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
}
- TextViewController
//
// TextViewController.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import Cocoa
class TextViewController: NSViewController {
@IBOutlet weak var label: NSTextField!
weak var delegate:TextViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func closeButtonClicked(_ sender: Any) {
delegate?.textView(self)
}
}
protocol TextViewControllerDelegate:AnyObject {
func textView(_ textView:TextViewController)
}
- ContentView
//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView(title: $title)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}//
// ContentView.swift
// ViewControllerRepresentable Test
//
// Created by zhaoxin on 2022/6/12.
//
import SwiftUI
struct ContentView: View {
@State var title = ""
@State var showTextView = false
var body: some View {
HStack {
TextField("Title", text: $title)
Button("Show Text View") {
showTextView = true
}
.sheet(isPresented: $showTextView) {
TextView(title: $title)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
App ran like this and the close button worked.

Update Variables of NSViewController
You may aware that there was a func
func updateNSViewController(_ nsViewController: TextViewController, context: Context) {
nsViewController.label.stringValue = title
}
We couldn't call nsViewController.label.stringValue = title in makeNSViewController, as in the make function the View Controller was created, but the xib was not loaded. We can think this update as viewDidLoad function in View Controller.
Resources