Easily linking any AppKit or UIKit class with SwiftUI

After spending an embarrassing amount of hours trying to get TextField to do what I needed on macOS I decided to try NSViewRepresentable + NSTextField.

I was fully prepared to spend the night on this… Yet I got it done in 30m 😮 

Lesson learned! Going forward, when a SwiftUI object is not mature enough I will not try to hack it with modifiers… Instead,  I will go straight to AppKit or UIKit. I know I’m loosing portability – which is why I spend so much time trying to do it all in SwiftUI – but, especially on macOS, SwiftUI sometimes is just not yet there. 

So, about linking to AppKit or UIKit… Online there’s a lot of, shall we say, over-engineered examples? Frankly, I believe it puts off a lot of people -including myself-, even though its actually very easy. 

Below is the leanest approach I found to link to any AppKit/UIKit class and their respective delegate. For this example I’m linking to NSTextField, but you could do it just as easily with any class.

Here’s how you call it from SwiftUI once implemented:

OSXTextField(textOfWhatToDisplay: someContent)

And here is the wrapper code for NSTextField, all self contained within a Struct:

struct OSXTextField: NSViewRepresentable {

 

    var textOfWhatToDisplay: String

    

    func makeNSView(context:Context) -> NSTextField{

        let osTextView = NSTextField(string: textOfWhatToDisplay)

        osTextView.delegate = context.coordinator       

//      osTextView.isBordered = false //example of calling NSTextField 

//      osTextView.drawsBackground = true //

        return osTextView

    }

    

    func updateNSView(_ nsView: NSTextField, context: Context) {

 

    }

    

    func makeCoordinator() -> Coordinator { //Called automatically by SwiftUI if implemented, used to trigger delegate

        Coordinator(parent: self)

    }

    

    class Coordinator: NSObject, NSTextFieldDelegate {

        

        var parent: OSXTextField

        

        init(parent: OSXTextField) {

            self.parent = parent

        }

        

        //NSTextFieldDelegate methods would go here

        

    }

}

 

This experience, all the hours I put into getting TextField to work, has also been a great reminder of the sunk-cost fallacy.

Comments / Feedback? Find me at mastodon.social/@MarcMasVi or @MarcMasVi

Marc