Uialertview Is Deprecated and Unavailable for Uiscene Based Applications, Please Use Uialertcontroller

UIAlertView is deprecated and unavailable for UIScene based applications, please use UIAlertController

I was also going through the same issue, i'm able to solve it getting help from a developer, scene base application that is new yet not support ui alert view so it crashes the app, we can remove the scene base application we can use window base app, follow these steps to resolve the issue:

First remove the SceneDelegate file
check this image for first step
Second remove UISceneSession Lifecycle two methods from AppDelegate file
check this image for second step
Third go to your info plist and remove Application Scene Manifest Entry from it
check this image for third step
Go ahead and build the project, run it now, make sure you have window property in AppDelegate

WKWebView Alert crash on iOS 13 SceneDelegate

I was able to reproduce the Error on the Simulator(version 13.2.2). I did a quick search and found that we can provide an alternate source for handling location permission and provide location updates to WKWebView. Below is the code that can get the job done.

ViewController.swift

import UIKit
import WebKit
class ViewController: UIViewController {

var navigatorGeolocation = NavigatorGeolocation();
var web: WKWebView!
override func viewDidLoad() {
super.viewDidLoad()

let webViewConfiguration = WKWebViewConfiguration();
navigatorGeolocation.setUserContentController(webViewConfiguration: webViewConfiguration);
web = WKWebView(frame:.zero , configuration: webViewConfiguration)
web.navigationDelegate = self;
navigatorGeolocation.setWebView(webView: web);
view.addSubview(web);
web.translatesAutoresizingMaskIntoConstraints = false
let constraints = [
web.topAnchor.constraint(equalTo: view.topAnchor),
web.leadingAnchor.constraint(equalTo: view.leadingAnchor),
web.bottomAnchor.constraint(equalTo: view.bottomAnchor),
web.trailingAnchor.constraint(equalTo: view.trailingAnchor)
]
NSLayoutConstraint.activate(constraints)
let url = URL(string: "https://www.google.com/maps")!
web.load(URLRequest(url: url))
}
}
extension ViewController: WKNavigationDelegate{
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
webView.evaluateJavaScript(navigatorGeolocation.getJavaScripToEvaluate());
}
}

NavigatorGeolocation.swift

import WebKit
import CoreLocation

class NavigatorGeolocation: NSObject, WKScriptMessageHandler, CLLocationManagerDelegate {

var locationManager = CLLocationManager();
var listenersCount = 0;
var webView: WKWebView!;
var controller: WKUserContentController?

override init() {
super.init();
locationManager.delegate = self;
}

func setUserContentController(webViewConfiguration: WKWebViewConfiguration) {
controller = WKUserContentController();
controller?.add(self, name: "listenerAdded")
controller?.add(self, name: "listenerRemoved")
webViewConfiguration.userContentController = controller!
}

func setWebView(webView: WKWebView) {
self.webView = webView;
}

func locationServicesIsEnabled() -> Bool {
return (CLLocationManager.locationServicesEnabled()) ? true : false;
}

func authorizationStatusNeedRequest(status: CLAuthorizationStatus) -> Bool {
return (status == .notDetermined) ? true : false;
}

func authorizationStatusIsGranted(status: CLAuthorizationStatus) -> Bool {
return (status == .authorizedAlways || status == .authorizedWhenInUse) ? true : false;
}

func authorizationStatusIsDenied(status: CLAuthorizationStatus) -> Bool {
return (status == .restricted || status == .denied) ? true : false;
}

func onLocationServicesIsDisabled() {
webView.evaluateJavaScript("navigator.geolocation.helper.error(2, 'Location services disabled');");
}

func onAuthorizationStatusNeedRequest() {
locationManager.requestWhenInUseAuthorization();
}

func onAuthorizationStatusIsGranted() {
locationManager.startUpdatingLocation();
}

func onAuthorizationStatusIsDenied() {
webView.evaluateJavaScript("navigator.geolocation.helper.error(1, 'App does not have location permission');");
}

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if (message.name == "listenerAdded") {
listenersCount += 1;

if (!locationServicesIsEnabled()) {
onLocationServicesIsDisabled();
}
else if (authorizationStatusIsDenied(status: CLLocationManager.authorizationStatus())) {
onAuthorizationStatusIsDenied();
}
else if (authorizationStatusNeedRequest(status: CLLocationManager.authorizationStatus())) {
onAuthorizationStatusNeedRequest();
}
else if (authorizationStatusIsGranted(status: CLLocationManager.authorizationStatus())) {
onAuthorizationStatusIsGranted();
}
}
else if (message.name == "listenerRemoved") {
listenersCount -= 1;
// no listener left in web view to wait for position
if (listenersCount == 0) {
locationManager.stopUpdatingLocation();
}
}
}

func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
// didChangeAuthorization is also called at app startup, so this condition checks listeners
// count before doing anything otherwise app will start location service without reason
if (listenersCount > 0) {
if (authorizationStatusIsDenied(status: status)) {
onAuthorizationStatusIsDenied();
}
else if (authorizationStatusIsGranted(status: status)) {
onAuthorizationStatusIsGranted();
}
}
}

func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.last {
webView.evaluateJavaScript("navigator.geolocation.helper.success('\(location.timestamp)', \(location.coordinate.latitude), \(location.coordinate.longitude), \(location.altitude), \(location.horizontalAccuracy), \(location.verticalAccuracy), \(location.course), \(location.speed));");
}
}

func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
webView.evaluateJavaScript("navigator.geolocation.helper.error(2, 'Failed to get position (\(error.localizedDescription))');");
}

func getJavaScripToEvaluate() -> String {
let javaScripToEvaluate = """
// management for success and error listeners and its calling
navigator.geolocation.helper = {
listeners: {},
noop: function() {},
id: function() {
var min = 1, max = 1000;
return Math.floor(Math.random() * (max - min + 1)) + min;
},
clear: function(isError) {
for (var id in this.listeners) {
if (isError || this.listeners[id].onetime) {
navigator.geolocation.clearWatch(id);
}
}
},
success: function(timestamp, latitude, longitude, altitude, accuracy, altitudeAccuracy, heading, speed) {
var position = {
timestamp: new Date(timestamp).getTime() || new Date().getTime(), // safari can not parse date format returned by swift e.g. 2019-12-27 15:46:59 +0000 (fallback used because we trust that safari will learn it in future because chrome knows that format)
coords: {
latitude: latitude,
longitude: longitude,
altitude: altitude,
accuracy: accuracy,
altitudeAccuracy: altitudeAccuracy,
heading: (heading > 0) ? heading : null,
speed: (speed > 0) ? speed : null
}
};
for (var id in this.listeners) {
this.listeners[id].success(position);
}
this.clear(false);
},
error: function(code, message) {
var error = {
PERMISSION_DENIED: 1,
POSITION_UNAVAILABLE: 2,
TIMEOUT: 3,
code: code,
message: message
};
for (var id in this.listeners) {
this.listeners[id].error(error);
}
this.clear(true);
}
};

// @override getCurrentPosition()
navigator.geolocation.getCurrentPosition = function(success, error, options) {
var id = this.helper.id();
this.helper.listeners[id] = { onetime: true, success: success || this.noop, error: error || this.noop };
window.webkit.messageHandlers.listenerAdded.postMessage("");
};

// @override watchPosition()
navigator.geolocation.watchPosition = function(success, error, options) {
var id = this.helper.id();
this.helper.listeners[id] = { onetime: false, success: success || this.noop, error: error || this.noop };
window.webkit.messageHandlers.listenerAdded.postMessage("");
return id;
};

// @override clearWatch()
navigator.geolocation.clearWatch = function(id) {
var idExists = (this.helper.listeners[id]) ? true : false;
if (idExists) {
this.helper.listeners[id] = null;
delete this.helper.listeners[id];
window.webkit.messageHandlers.listenerRemoved.postMessage("");
}
};
""";

return javaScripToEvaluate;
}
}

The NavigatorGeolocation code is taken from this Answer posted by mikep

(Tip: Read the comments also)

Output(Simulator->Debug->Location->Apple):
Sample Image

How do I migrate from UIAlertView (deprecated in iOS8)

You need to use UIAlertController instead. To class documentation is pretty straightforward, even containing an usage example in Listing 1 at the very beginning of the doc (sure it's in ObjC and not in Swift but it's quite similar).

So for your use case, here is how it translates to (with added comments):

let alert = UIAlertController(title: "Oops!", message:"This feature isn't available right now", preferredStyle: .alert)
let action = UIAlertAction(title: "OK", style: .default) { _ in
// Put here any code that you would like to execute when
// the user taps that OK button (may be empty in your case if that's just
// an informative alert)
}
alert.addAction(action)
self.presentViewController(alert, animated: true){}

So the compact code will look like:

let alert = UIAlertController(title: "Oops!", message:"This feature isn't available right now", preferredStyle: .Alert)
alert.addAction(UIAlertAction(title: "OK", style: .Default) { _ in })
self.present(alert, animated: true){}

Where self here is supposed to be your UIViewController.


Additional tip: if you need to call that code that displays the alert outside of the context of an UIViewController, (where self is not an UIViewController) you can always use the root VC of your app:

let rootVC = UIApplication.sharedApplication().keyWindow?.rootViewController
rootVC?.presentViewController(alert, animated: true){}

(But in general it's preferable to use a known UIViewController when you have one — and you generally present alerts from UIViewControllers anyway — or try to get the most suitable one depending on your context instead of relying on this tip)

UIAlertView first deprecated IOS 9

From iOS8 Apple provide new UIAlertController class which you can use instead of UIAlertView which is now deprecated, it is also stated in deprecation message:

UIAlertView is deprecated. Use UIAlertController with a preferredStyle
of UIAlertControllerStyleAlert instead

So you should use something like this

UIAlertController * alert = [UIAlertController
alertControllerWithTitle:@"Title"
message:@"Message"
preferredStyle:UIAlertControllerStyleAlert];



UIAlertAction* yesButton = [UIAlertAction
actionWithTitle:@"Yes, please"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//Handle your yes please button action here
}];

UIAlertAction* noButton = [UIAlertAction
actionWithTitle:@"No, thanks"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
//Handle no, thanks button
}];

[alert addAction:yesButton];
[alert addAction:noButton];

[self presentViewController:alert animated:YES completion:nil];

UIAlertView' was deprecated in iOS 9.0. Use UIAlertController with a preferredStyle of UIAlertControllerStyleAlert instead

See this Code Destructive and OK buttons in UIAlertController:

let alertController = UIAlertController(title: "Destructive", message: "Simple alertView demo with Destructive and Ok.", preferredStyle: UIAlertControllerStyle.alert) //Replace UIAlertControllerStyle.Alert by UIAlertControllerStyle.alert
let DestructiveAction = UIAlertAction(title: "Destructive", style: UIAlertActionStyle.Destructive) {
(result : UIAlertAction) -> Void in
print("Destructive")
}

// Replace UIAlertActionStyle.Default by UIAlertActionStyle.default
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
(result : UIAlertAction) -> Void in
print("OK")
}

alertController.addAction(DestructiveAction)
alertController.addAction(okAction)
self.presentViewController(alertController, animated: true, completion: nil)

Swift 3:

let alertController = UIAlertController(title: "Destructive", message: "Simple alertView demo with Destructive and Ok.", preferredStyle: UIAlertControllerStyle.alert) //Replace UIAlertControllerStyle.Alert by UIAlertControllerStyle.alert

let DestructiveAction = UIAlertAction(title: "Destructive", style: UIAlertActionStyle.destructive) {
(result : UIAlertAction) -> Void in
print("Destructive")
}

// Replace UIAlertActionStyle.Default by UIAlertActionStyle.default

let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.default) {
(result : UIAlertAction) -> Void in
print("OK")
}

alertController.addAction(DestructiveAction)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)

See Alert With Destructive and OK Button:

Sample Image

I keep getting this error 'UIAlertView' was deprecated in iOS 9.0

As the error states, UIAlertView was deprecated as of iOS 9.0. UIAlertController was added in iOS 8.0. Your code is correctly choosing to use UIAlertController under iOS 8.0 or later.

The problem is that your code tries to use UIAlertView for anything earlier than iOS 8.0. But your app's Deployment Target is not set for iOS 7 or earlier. Therefore the compiler is telling you that UIAlertView is deprecated.

Since you are not actually going to be supporting anything earlier than iOS 8.0, there is no need for the if #available(iOS 8.0, *) or for UIAlertView. Just write your code using UIAlertController and don't worry about UIAlertView.

All of the code you posted can be reduced to just the UIAlertController part:

let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: UIAlertControllerStyle.alert)

alert.addAction(UIAlertAction(title: alertOKTitle, style:.destructive, handler: { alertAction in
self.okButtonPressed()
alert.dismiss(animated: true, completion: nil)
}))

alert.addAction(UIAlertAction(title: alertCancelTitle, style:.cancel, handler:{ alertAction in
self.cancelButtonPressed()
alert.dismiss(animated: true, completion: nil)
}))

alert.addAction(UIAlertAction(title: alertRemindLaterTitle, style:.default, handler: { alertAction in
self.remindLaterButtonPressed()
alert.dismiss(animated: true, completion: nil)
}))

let appDelegate = UIApplication.shared.delegate as! AppDelegate
let controller = appDelegate.window?.rootViewController

controller?.present(alert, animated: true, completion: nil)

How would I create a UIAlertView in Swift?

From the UIAlertView class:

// UIAlertView is deprecated. Use UIAlertController with a
preferredStyle of UIAlertControllerStyleAlert instead

On iOS 8, you can do this:

let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)

Now UIAlertController is a single class for creating and interacting with what we knew as UIAlertViews and UIActionSheets on iOS 8.

Edit: To handle actions:

alert.addAction(UIAlertAction(title: "OK", style: .Default, handler: { action in
switch action.style{
case .Default:
print("default")

case .Cancel:
print("cancel")

case .Destructive:
print("destructive")
}
}}))

Edit for Swift 3:

let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)

Edit for Swift 4.x:

let alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { action in
switch action.style{
case .default:
print("default")

case .cancel:
print("cancel")

case .destructive:
print("destructive")

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

WKWebView Thread 1: signal SIGABRT when using location

Me too, this sucks.

 *** Terminating app due to uncaught exception 'NSObjectNotAvailableException', reason: 'UIAlertView is deprecated and unavailable for UIScene based applications, please use UIAlertController!'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff23b98bde __exceptionPreprocess + 350
1 libobjc.A.dylib 0x00007fff503b5b20 objc_exception_throw + 48
2 CoreFoundation 0x00007fff23b98a1c +[NSException raise:format:] + 188
3 UIKitCore 0x00007fff466e9463 -[UIAlertView initWithFrame:] + 417
4 UIKitCore 0x00007fff466e9ad0 -[UIAlertView initWithTitle:message:delegate:cancelButtonTitle:otherButtonTitles:] + 218
5 UIKitCore 0x00007fff47516acf +[UIAlertView(ViewServiceSupport) _alertViewForWindow:] + 217
6 UIKitCore 0x00007fff476cdb44 -[UIWebGeolocationPolicyDecider _executeNextChallenge] + 242
7 WebKit 0x00007fff2d123a6d _ZN6WebKit43decidePolicyForGeolocationRequestFromOriginEPN7WebCore14SecurityOriginERKN3WTF6StringEPU37objcproto26WebAllowDenyPolicyListener11objc_objectP8UIWindow + 169
8 WebKit 0x00007fff2d123641 -[WKGeolocationProviderIOS(WebGeolocationCoreLocationUpdateListener) geolocationAuthorizationGranted] + 603
9 WebKit 0x00007fff2d12328d -[WKGeolocationProviderIOS
...

start contentview from appdelegate

The SceneDelegate.swift doesn't have to be commented, there are some steps you have to be aware of.

  • Set a window instance to your AppDelegate window property, as @pkc456 recommended.
window = UIWindow(frame: UIScreen.main.bounds)
  • Remove the Scene configuration from the AppDelegate (You have already done it by commenting it as showed on your screenshot).
  • Delete the Scene configuration key from Info.plist file.

Info.plist > Application Scene Manifest > Scene Configuration

Remember to use the View Hierarchy debugger, sometimes you are seeing the correct view controller but it doesn't have a backgroundColor.

Hope I Helped!



Related Topics



Leave a reply



Submit