Fix: Could not find an overload for ‘init’ in UIAlertView

Fix: Could not find an overload for 'init' in UIAlertView

Written by

in

Quick answer: UIAlertView is deprecated and its variadic initializer cannot take nil for otherButtonTitles, so no init overload matches. Replace it with UIAlertController(preferredStyle: .alert), add UIAlertAction buttons, and call present(_:animated:) on a view controller that is on screen.

Why this happens

UIAlertView is a pre-iOS 8 class. Apple deprecated it in iOS 9, and the header comment says exactly what to do instead: use UIAlertController with a preferredStyle of .alert. So the error you are seeing is not really a syntax puzzle — it is the compiler telling you that you are calling an API that has been on its way out for years.

The immediate cause is how the old initializer is imported into Swift. In Objective-C the signature ends with a nil-terminated variadic list of extra button titles. Swift imports that as a variadic parameter, and a variadic parameter cannot be nil. When you write otherButtonTitles: nil, no overload matches, and instead of pointing at the nil the compiler reports:

Could not find an overload for 'init' that accepts the supplied arguments

There is a second trap on the same line: delegate: self. That only type-checks if the enclosing class conforms to UIAlertViewDelegate. If it does not, the initializer fails to match for that reason as well, and the error message is identical. show() is deprecated alongside the class, so even a version that compiled would be dead code you have to rewrite later.

The fix is to stop trying to satisfy the old initializer and move to UIAlertController, which handles both alerts and action sheets in one class.

How to fix it

1. Replace the call with a single-button alert

This is the direct replacement for the line in the question. Create the controller, add one action, present it.

import UIKit

class ViewController: UIViewController {

    @IBAction func showAlertButtonTapped(_ sender: UIButton) {
        let alert = UIAlertController(
            title: "Title",
            message: "Message",
            preferredStyle: .alert
        )

        alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))

        present(alert, animated: true, completion: nil)
    }
}

Two things changed compared with the original code. otherButtonTitles is gone — buttons are added one at a time with addAction(_:). And show() is gone — you present the controller like any other view controller.

2. Add more buttons and handle the taps

Each UIAlertAction carries its own handler, so you no longer need a delegate method with a buttonIndex switch. The handler receives the action that was tapped.

let alert = UIAlertController(
    title: "Delete project?",
    message: "This cannot be undone.",
    preferredStyle: .alert
)

alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

alert.addAction(UIAlertAction(title: "Delete", style: .destructive) { action in
    print("tapped \(action.title ?? "")")
    // perform the delete here
})

present(alert, animated: true, completion: nil)

If you want one handler for several buttons, compare action.style instead of keeping an index:

let handler: (UIAlertAction) -> Void = { action in
    switch action.style {
    case .default:     print("default")
    case .cancel:      print("cancel")
    case .destructive: print("destructive")
    @unknown default:  break
    }
}

Use .cancel for the button that means “do nothing” and .destructive for anything that deletes or discards. iOS styles them for you and, on alerts, always places Cancel in the expected position.

3. Present from a view controller that is actually on screen

present(_:animated:completion:) must be called on a controller whose view is in the window hierarchy. Calling it from a controller that has been dismissed, or from a background callback after the user navigated away, produces a runtime warning about presenting on a controller whose view is not in the window hierarchy, and nothing appears.

If the alert is triggered from an async callback, hop back to the main actor first and present from the current top controller:

@MainActor
func topViewController(from root: UIViewController? = nil) -> UIViewController? {
    let base = root ?? view.window?.rootViewController
    if let nav = base as? UINavigationController { return topViewController(from: nav.visibleViewController) }
    if let tab = base as? UITabBarController { return topViewController(from: tab.selectedViewController) }
    if let presented = base?.presentedViewController { return topViewController(from: presented) }
    return base
}

Then call topViewController()?.present(alert, animated: true).

4. Use the same class for action sheets

UIAlertController replaced UIActionSheet too. Change the style and the presentation adapts:

let sheet = UIAlertController(title: nil, message: nil, preferredStyle: .actionSheet)
sheet.addAction(UIAlertAction(title: "Save Draft", style: .default, handler: nil))
sheet.addAction(UIAlertAction(title: "Discard", style: .destructive, handler: nil))
sheet.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))

// Required on iPad, or the presentation crashes.
if let popover = sheet.popoverPresentationController {
    popover.sourceView = sender
    popover.sourceRect = sender.bounds
}

present(sheet, animated: true, completion: nil)

On iPhone an action sheet slides up from the bottom; on iPad it becomes a popover and needs an anchor. Forgetting sourceView and sourceRect is the most common crash after migrating from UIActionSheet.

5. In SwiftUI, use the alert modifier

There is no UIAlertController in SwiftUI. Use the alert modifier with a binding instead:

import SwiftUI

struct ContentView: View {
    @State private var showingAlert = false

    var body: some View {
        Button("Show Alert") { showingAlert = true }
            .alert("Title", isPresented: $showingAlert) {
                Button("Cancel", role: .cancel) { }
                Button("Delete", role: .destructive) { }
            } message: {
                Text("Message")
            }
    }
}

For a bottom sheet of choices, use confirmationDialog(_:isPresented:titleVisibility:actions:) instead.

How to prevent it

  • Treat deprecation warnings as work items, not noise. UIAlertView has been deprecated since iOS 9; the warning was the early notice that the initializer would stop matching.
  • Never pass nil to a variadic parameter. If an initializer ends in otherButtonTitles:, pass nothing or pass strings.
  • Keep one small helper (or a UIViewController extension) that builds and presents alerts, so a future API change is a one-file edit.
  • When presenting from async code, always return to the main actor and resolve the top-most view controller before calling present.
  • Remember the iPad popover anchor whenever you use .actionSheet.

FAQ

Why does otherButtonTitles: nil fail but otherButtonTitles: "OK" also fail?

Because the parameter is variadic, nil is not a valid argument at all, and a single string still has to match the rest of the signature — including a delegate whose type your class must conform to. Rather than patching the call, move to UIAlertController, where buttons are added with addAction(_:).

Is UIAlertView removed from the SDK?

It has been deprecated since iOS 9. Deprecated API can disappear in a future SDK, and it does not exist in SwiftUI at all, so treat any remaining UIAlertView call as migration debt rather than something to keep alive.

How do I know which button the user tapped?

Each UIAlertAction has its own handler closure that receives the tapped action, so you can attach the work directly to the button. If several buttons share one handler, switch on action.style (.default, .cancel, .destructive) or compare action.title.

Sources & credits

Parts of this guide are adapted from content licensed under CC BY-SA 3.0; this article is shared under the same license.