Introduction

When developing an iOS or iPadOS app that requests access to contacts, encountering issues with the permission modal can be frustrating. Specifically, an issue you may face is when the modal appears under your main ViewController instead of on top of it. This article explores why this happens and provides a step-by-step solution to ensure that the Contacts permission modal is displayed correctly.

Understanding the Issue

The issue where the Contacts permission modal appears beneath the ViewController is often related to how UIKit handles view hierarchy and modal presentations. When you request access to contacts, the system generates a modal that should interrupt and be displayed over your current view. However, in certain scenarios, especially when attempting to trigger this access right in the viewWillAppear method, the modal may not present properly due to timing issues in the view lifecycle.

Why Does This Happen?

When you call the CNContactStore method to access contacts in viewWillAppear, the view controller is still in the process of its presentation. This can confuse UIKit and result in the modal being presented behind the main view. This is more likely seen when using Swift and Storyboards, as the default behavior of UI presentations can change based on the timing of view updates.

Step-by-Step Solution to Fix the Modal Display

To fix the presentation of the Contacts permission modal, you should avoid initiating the permission request immediately in the viewWillAppear method. Instead, you can wait until the view is fully loaded and on screen. Here’s how to do this with an example in Swift:

Updated Implementation

Here’s how to properly implement the code in your ViewController:

import UIKit
import Contacts

class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        // Additional setup if needed
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        checkContactPermission()
    }

    private func checkContactPermission() {
        print("Checking contacts permission")
        let store = CNContactStore()
        store.requestAccess(for: .contacts) { (granted, error) in
            if granted {
                print("Access granted")
            } else {
                print("Access denied")
            }
        }
    }
}

Explanation of the Code

  • viewDidAppear Method: By moving the permission request to viewDidAppear, you ensure the view controller is fully presented. This method is called after the view is presented, making it safer to request permissions.
  • requestAccess Method: Use requestAccess(for:) to ask the user for permission to access their contacts. This method handles the modal display appropriately, showing the permission request modal over the active view controller.

Additional Considerations

  • If you still encounter issues with modals appearing incorrectly, ensure that your ViewController hierarchy is set up correctly and that no other modals are currently being presented.
  • Try testing on a physical device if you notice consistent issues in the iOS simulator, as behavior can vary.

Frequently Asked Questions (FAQ)

Why is the permission modal not appearing?

This can happen due to timing issues in the view lifecycle. Always trigger permission requests in viewDidAppear instead of viewWillAppear.

Can I customize the permission modal?

No, the permissions modal is controlled by the system and cannot be customized. Ensure your purpose string in Info.plist clearly explains why you need access.

What should I include in the purpose string in Info.plist?

Make sure the NSContactsUsageDescription key in your Info.plist contains a clear and concise explanation. This helps users understand why the permission is necessary for your app's functionality.