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):
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:
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 UIAlertView
s and UIActionSheet
s 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
How to Implement Protocol Methods That Return Covariant Selfs
How to Pass Int.Init to a Function in Swift
Swift [1,2] Conforms to Anyobject But [Enum.A, Enum.B] Does Not
How to Loop Through an Array from the Second Element in Elegant Way Using Swift
Uibarbuttonitem Doesn't Work When Created as a Property, But Does When Created in a Function
Error: Binary Operator '<=' Cannot Be Applied to Operands of Type 'Int' and 'Int'
Expand and Contract Tableview Cell When Tapped, in Swift
Swiftui Change View with Button
How to Shuffle an Array So That No Two Consecutive Values Are the Same
Print the Nstableview's Row Number of the Row Clicked by the User
Using Uiapplicationdelegateadaptor to Get Callbacks from Userdidacceptcloudkitsharewith Not Working
How to Convert Uint16 to Uint8 in Swift 3