FREE TRIAL: Accelerate your app development career with Hacking with Swift+! >>

Extending existing types to support ObservableObject

Paul Hudson    @twostraws   

Users can now drop pins on our MapView, but they can’t do anything with them – they can’t attach their own title and subtitle. Fixing this requires a little bit of thinking, because MKPointAnnotation uses optional strings for title and subtitle, and SwiftUI doesn’t let us bind optionals to text fields.

There are a couple of ways of fixing this, but the easiest one by far is writing an extension to MKPointAnnotation that adds computed properties around title and subtitle, which means we can then make the class conform to ObservableObject without any further work. You can call these computed properties whatever you want – name, info, details, etc – but you’ll probably find that marking them as simple wrappers works out easier to remember in the long term, which is why I’m going to use the names wrappedTitle and wrappedSubtitle.

Create a new Swift file called MKPointAnnotation-ObservableObject.swift, change its Foundation import for MapKit, then give it this code:

extension MKPointAnnotation: ObservableObject {
    public var wrappedTitle: String {
        get {
            self.title ?? "Unknown value"

        set {
            title = newValue

    public var wrappedSubtitle: String {
        get {
            self.subtitle ?? "Unknown value"

        set {
            subtitle = newValue

Notice how I haven’t marked those computed properties as @Published? This is OK here because we won’t actually be reading the properties as they are being changed, so there’s no need to keep refreshing the view as the user types.

With that new extension in place, we have two properties on MKPointAnnotation that aren’t optional, which means we can now bind some UI controls to them in a SwiftUI view – we can create a UI for editing place marks.

As always we’re going to start small and work our way up, so please create a new SwiftUI view called “EditView”, add an import for MapKit, then give it this code:

struct EditView: View {
    @Environment(\.presentationMode) var presentationMode
    @ObservedObject var placemark: MKPointAnnotation

    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Place name", text: $placemark.wrappedTitle)
                    TextField("Description", text: $placemark.wrappedSubtitle)
            .navigationBarTitle("Edit place")
            .navigationBarItems(trailing: Button("Done") {

Make sure you update the preview code so that it passes in our example MKPointAnnotation, like this:

struct EditView_Previews: PreviewProvider {
    static var previews: some View {
        EditView(placemark: MKPointAnnotation.example)

We want to display that in two places, both in ContentView: when the user adds a place we want them to immediately edit it, and when they press the Edit button in our pin alert.

Both of these will be triggered by a Boolean condition, so start by adding this @State property to ContentView:

@State private var showingEditScreen = false

That should be set to true when the user taps Edit in our alert, which means replacing the // edit this place comment with this:

self.showingEditScreen = true

And it also means setting it to true when they just added a new place to the map, but we also need to set the selectedPlace property so our code knows which place should be edited. So, put this below the self.locations.append(newLocation) line:

self.selectedPlace = newLocation
self.showingEditScreen = true

And finally, we need to bind showingEditScreen to a sheet, so our EditView struct gets presented with a place mark at the right time. Remember, we can’t use if let here to unwrap the selectedPlace optional, so we’ll do a simple check then force unwrap – it’s just as safe.

Please attach this sheet() modifier to ContentView, after the existing alert:

.sheet(isPresented: $showingEditScreen) {
    if self.selectedPlace != nil {
        EditView(placemark: self.selectedPlace!)

That’s the next step of our app done, and it’s now almost useful – you can browse the map, tap to drop pins, then give them a meaningful title and subtitle.

Hacking with Swift is sponsored by Stream

SPONSORED Stream’s latest iOS Chat SDK release provides a better developer experience with new docs, customizable attachments, and UI components, and under-the-hood performance improvements.

Learn more

Sponsor Hacking with Swift and reach the world's largest Swift community!

Buy Pro Swift Buy Swift Design Patterns Buy Testing Swift Buy Hacking with iOS Buy Swift Coding Challenges Buy Swift on Sundays Volume One Buy Server-Side Swift (Vapor Edition) Buy Advanced iOS Volume One Buy Advanced iOS Volume Two Buy Advanced iOS Volume Three Buy Hacking with watchOS Buy Hacking with tvOS Buy Hacking with macOS Buy Dive Into SpriteKit Buy Swift in Sixty Seconds Buy Objective-C for Swift Developers Buy Server-Side Swift (Kitura Edition) Buy Beyond Code

Was this page useful? Let us know!

Average rating: 4.8/5

Unknown user

You are not logged in

Log in or create account

Link copied to your pasteboard.