Search code examples
iosoverlayios10eventkit

Dialog box displayed by EKEventStore.authorizationStatus(for:) apparently hidden behind overlay


My app's application(_:didFinishLaunchingWithOptions:launchOptions:) is creating an overlay like this:

window.makeKeyAndVisible()
let launchStoryboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
let overlayView = launchStoryboard.instantiateInitialViewController()!.view!
window.addSubview(overlayView)

The app's initial view controller as defined in Main.storyboard is a UITabBarController whose 1st tab contains a UINavigationController. viewDidLoad() of its root view controller calls EKEventStore.authorizationStatus(for:).

At this point the app seemingly stalls and only the overlay is visible. When I stop the app the dialog box that asks whether the app should be allowed to access the calendar appears. It was apparently hidden below the overlay.

How can I ensure that the dialog box appears at the very front of the screen so the user gets a chance to answer its actual question?

UPDATE Here is more information about the general context in response to questions: The reason for the code in the app's delegate is that -- unless it has occurred on a previous launch -- I need to prompt the user for server credentials, obtain data from the server and store some of this data in EKEvents. The overlay supports presenting the view controller for obtaining the user’s credentials (see here). The overlay will be removed when data has been received from the server. The call to makeKeyAndVisible is necessary in order for UIApplication.shared.keyWindow to receive a value despite the early stage in the app’s life cycle. That value is required for presenting the said view controller for obtaining credentials.


Solution

  • The problem was not that the dialog box was hidden behind the overlay but that I called EKEventStore's requestAccess(to:completion:) synchronously (by waiting after the call for a semaphore that was signaled inside the completion handler) inside application(_:didFinishLaunchingWithOptions:launchOptions:). Hence the entire app stalled and the dialog box was not even displayed.

    The solution consisted in requesting access asynchronously. The following scheme is currently good enough:

    • instance variable calendarEvent is nil if access to an EKCalendar was not (yet) granted
    • requestAccess(to:completion:) is called from inside application(_:didFinishLaunchingWithOptions:launchOptions:)
    • calendarEvent is set to this EKCalendar in completion handler of requestAccess(to:completion:)
    • before reading or writing to calendar app checks if calendarEvent != nil
    • during cycle of run loop with call to application(_:didFinishLaunchingWithOptions:launchOptions:) calendarEvent remains nil; app behaves as if calendar were empty
    • during later cycles of run loop calendarEvent has been set; app actually writes to calendar only during those later cycles