Be careful when using .onTapGesture()


You’re more of a video kind of person? I’ve got you covered! Here’s a video with the same content than this article 🍿


If you’ve been using SwiftUI, there’s a good chance that you’ve called the modifier .onTapGesture().

This modifier is very convenient, because it allows you to define a closure that will be called when the user taps on the View that the modifier has been attached to.

However, you want to be careful when you use this modifier, because it can easily turn into a really bad practice!

So let’s take a look at a couple of use cases.

In this first use case, we use the modifier .onTapGesture() to start pre-fetching some data over the network when the user taps on a View.

This is a good use case for the modifier .onTapGesture(), because it is used to trigger an action that will be completely transparent to the user 👍

Now let’s have a look at a second use case.

Here, the modifier .onTapGesture() is being used to present a View modally.

And in this use case, using the modifier .onTapGesture() is actually a very bad practice!

The reason is that presenting a View modally is a user-facing action and for user-facing actions it’s much better to use a Button than to use the modifier .onTapGesture().

The reason why is because using a Button will bring a lot of desirable side-effects.

For example, the action will become visible to the accessibility layer and the view will have a highlighted state that shows a visual feedback when the user presses the Button.

So remember, even though it can be tempting to call a modifier rather than to wrap a complex View inside a Button, if you’re implementing an action that’s visible to the user, then you definitely want to use a Button.

That’s all for this article, I hope you’ve enjoyed this new format!

Here’s the code if you want to experiment with it:

// First Use Case

import SwiftUI

struct ContentView: View {
    
    @StateObject var viewModel = ViewModel()
  
    var body: some View {
        VStack {
            // ...
        }
        .onTapGesture {
           viewModel.prefetchData()
        }
    }
}

// Second Use Case (Bad)

import SwiftUI

struct ContentView: View {
       
    @State var showModal = false
    
    var body: some View {
        VStack {
            // ...
        }
        .sheet(isPresented: $showModal){
            // ...
        }
        .onTapGesture {
            showModal = true
        }
    }
}

// Second Use Case (Good)

import SwiftUI

struct ContentView: View {
       
    @State var showModal = false
    
    var body: some View {
        Button {
            showModal = true
        } label: {
            VStack {
                // ...
            }
        }
        .sheet(isPresented: $showModal){
            // ...
        }
    }
}
Previous
Previous

I can teach you the basics of UIKit in 2 hours 👩🏽‍🎓👨🏻‍🎓

Next
Next

How to easily test In-App Purchases 🛍️