UIActivityViewController completion handler still calls action if user presses cancel - ios

In my UIActivityViewController, I use completion handler to execute a "successfully shared" notification. It works but my only problem is, it still shows the notification if the user presses cancel.
Here is my completion handler code,
[controller setCompletionHandler:^(NSString *activityType, BOOL completed) {
CWStatusBarNotification *notification = [CWStatusBarNotification new];
[notification displayNotificationWithMessage:#"✓ Successfully Shared Centre!"
forDuration:3.0f];
notification.notificationLabelBackgroundColor = [UIColor colorWithRed:38.0f/255.0f green:81.0f/255.0f blue:123.0f/255.0f alpha:1.0f];
notification.notificationLabelTextColor = [UIColor whiteColor];
}];
Thanks for the help!

Note: the completionHandler property is deprecated in iOS8, so it's not possible anymore to know the result of a share action.
https://developer.apple.com/documentation/uikit/uiactivityviewcontroller/1622010-completionhandler
Update:
Like adruzh said, on iOS8 there's a new completionHandler that Apple forgot to mention in the documentation:
[activityController setCompletionWithItemsHandler:
^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
}];
https://developer.apple.com/documentation/uikit/uiactivityviewcontroller/1622022-completionwithitemshandler

That's what the completed argument is for:
[controller setCompletionHandler:^(NSString *activityType, BOOL completed) {
if (!completed) return;
CWStatusBarNotification *notification = [CWStatusBarNotification new];
[notification displayNotificationWithMessage:#"✓ Successfully Shared Centre!"
forDuration:3.0f];
notification.notificationLabelBackgroundColor = [UIColor colorWithRed:38.0f/255.0f green:81.0f/255.0f blue:123.0f/255.0f alpha:1.0f];
notification.notificationLabelTextColor = [UIColor whiteColor];
}];

For Swift, this is what worked for us:
...
// Configure UIActivityViewController
let activityViewController = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityViewController.excludedActivityTypes = [UIActivityTypeAirDrop,
UIActivityTypeAddToReadingList,
UIActivityTypeAssignToContact,
UIActivityTypePrint,
UIActivityTypeCopyToPasteboard]
// Show UIActivityViewController
presentViewController(activityViewController, animated: true, completion: nil)
// Define completion handler
activityViewController.completionWithItemsHandler = doneSharingHandler
...
func doneSharingHandler(activityType: String!, completed: Bool, returnedItems: [AnyObject]!, error: NSError!) {
// Return if cancelled
if (!completed) {
return
}
// If here, log which activity occurred
println("Shared video activity: \(activityType)")
}

Swift 5 - The below function covers most of the UIActivityViewController properties. It worked for me and thought so it might be helpful for you guys as well.
func performShareAction() {
let itemsToShare : [Any] = ["Hello World"]
let activityView = UIActivityViewController(activityItems: itemsToShare, applicationActivities: nil)
// Apps that you want to exclude sharing the items
let excludedActivityTypes : [UIActivity.ActivityType] = [
.addToReadingList,
.assignToContact,
.copyToPasteboard,
.mail,
.markupAsPDF,
.message,
.openInIBooks,
.postToFacebook,
.postToFlickr,
.postToTencentWeibo,
.postToTwitter,
.postToVimeo,
.postToWeibo,
.print,
.saveToCameraRoll
]
activityView.excludedActivityTypes = excludedActivityTypes
self.present(activityView, animated: true, completion: nil)
activityView.completionWithItemsHandler = { activityType, completed, items, error in
// Event Cancelled
if !completed {
print("Content Sharing was cancelled.")
return
}
// Content Shared on particular activity
print("Shared on activity type: \(String(describing: activityType?.rawValue))")
// Detect app on which the items are shared
if let type = activityType {
switch type {
case .addToReadingList: print("Added To Reading List"); break
case .airDrop: print("AirDropped to Other Device"); break
case .assignToContact: print("Assigned To Contact"); break
case .copyToPasteboard: print("Copied To Pasteboard"); break
case .mail: print("Mailed"); break
case .markupAsPDF: print("Marked-Up As PDF"); break
case .message: print("Messaged"); break
case .openInIBooks: print("Opened In iBooks"); break
case .postToFacebook: print("Posted To Facebook"); break
case .postToFlickr: print("Posted To Flickr"); break
case .postToTencentWeibo: print("Posted To Tencent Weibo"); break
case .postToTwitter: print("Posted To Twitter"); break
case .postToVimeo: print("Posted To Vimeo"); break
case .postToWeibo: print("Posted To Weibo"); break
case .print: print("Printed"); break
case .saveToCameraRoll: print("Saved To Camera Roll"); break
default: print("Shared with new app"); break
}
}
}
}

For the Swifties out there, here's how you would code this in Swift along with some share service detection:
activityViewController.completionHandler = {(activityType, completed:Bool) in
if !completed {
//cancelled
return
}
//shared successfully
//below is how you would detect for different sharing services
var activity:String = "other"
if activityType == UIActivityTypePostToTwitter {
activity = "twitter"
}
if activityType == UIActivityTypeMail {
activity = "mail"
}
//more code here if you like
}

The completed parameter will be NO is the user cancels.
[controller setCompletionHandler:^(NSString *activityType, BOOL completed) {
if (completed) {
CWStatusBarNotification *notification = [CWStatusBarNotification new];
[notification displayNotificationWithMessage:#"✓ Successfully Shared Centre!"
forDuration:3.0f];
notification.notificationLabelBackgroundColor = [UIColor colorWithRed:38.0f/255.0f green:81.0f/255.0f blue:123.0f/255.0f alpha:1.0f];
notification.notificationLabelTextColor = [UIColor whiteColor];
}
}];

SWIFT 2.0, iOS 8.0 >, you should use completion handler like this:
self.presentViewController(activityVC, animated: true, completion: nil)
activityVC.completionWithItemsHandler = {(activityType, completed:Bool, returnedItems:[AnyObject]?, error: NSError?) in
//do some action
}
see my answer here: https://stackoverflow.com/a/34581940/1109892

Swift 3
func completionHandler(activityType: UIActivityType?, shared: Bool, items: [Any]?, error: Error?) {
if (shared) {
print("Cool user shared some stuff")
}
else {
print("Bad user canceled sharing :(")
}
}
activityController.completionWithItemsHandler = completionHandler

Related

UIActivityViewController chose different text for WhatsApp or Facebook

I have the text in activity Items but now it share on WhatsApp or Facebook, this method share for each app the same text.
is it possible to share different text for each app ?
- (void)share {
NSString *text = #"share test";
NSArray * activityItems = #[[NSString stringWithFormat:#"%#", text], [NSURL URLWithString:#"http://www.test.co"]];
NSArray * applicationActivities = nil;
NSArray * excludeActivities = #[UIActivityTypeAssignToContact, UIActivityTypeCopyToPasteboard, UIActivityTypePostToWeibo, UIActivityTypePrint, UIActivityTypeMessage];
UIActivityViewController * activityController = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:applicationActivities];
activityController.excludedActivityTypes = excludeActivities;
[self presentViewController:activityController animated:YES completion:nil];
[activityController setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
NSLog(#"The setCompletionWithItemsHandler completed = %i", completed);
if (completed) {
NSLog(#"The selected activity was %#", activityType);
if ( [activityType isEqualToString:UIActivityTypeMail]) {
NSLog(#"Mail sended");
} else if ( [activityType isEqualToString:UIActivityTypePostToTwitter]) {
NSLog(#"Post on twitter, ok!");
} else if ( [activityType isEqualToString:UIActivityTypePostToFacebook]) {
NSLog(#"Post on facebook, ok!");
} else if ( [activityType isEqualToString:UIActivityTypeMessage]) {
NSLog(#"SMS sended!");
}
}
}];
}
Short answer: Yes!
There is actually a way to return different content for multiple apps, AFTER choosing them.
You will have to create two UIActivityItemSource, one which will return an UIImage and other which will return a NSObject. This is done to "fool" the system that you will share these objects, but after the user has selected an app, we can actually return something else, like a URL.
class SocialActivityItem: NSObject, UIActivityItemSource {
var img: UIImage?
var url: URL?
convenience init(img: UIImage, url: URL) {
self.init()
self.img = img
self.url = url
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return img!
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
//Instagram
if activityType?.rawValue == "com.burbn.instagram.shareextension" {
return img!
} else {
return url
}
}
}
and
class TextActivityItem: NSObject, UIActivityItemSource {
var textToShare: String?
convenience init(textToShare: String) {
self.init()
self.textToShare = textToShare
}
// This will be called BEFORE showing the user the apps to share (first step)
func activityViewControllerPlaceholderItem(_ activityViewController: UIActivityViewController) -> Any {
return NSObject()
}
// This will be called AFTER the user has selected an app to share (second step)
func activityViewController(_ activityViewController: UIActivityViewController, itemForActivityType activityType: UIActivityType?) -> Any? {
var text = ""
if activityType?.rawValue == "net.whatsapp.WhatsApp.ShareExtension" {
text = "Sharing on Whatsapp"
}
if activityType == UIActivityType.postToFacebook {
text = "Sharing on Facebook"
}
return text.isEmpty ? textToShare : text
}
}
Then, you just need to set everything up:
let url = URL(string: "www.google.com")!
let socialProvider = SocialActivityItem(img: img, url: url)
let textProvider = TextActivityItem(textToShare: "Sharing on social media!")
let activityViewController = UIActivityViewController(activityItems: [socialProvider, textProvider], applicationActivities: nil)
Short answer: No.
But you can use the open App with URL method and pass an parameter to that App.
When using that method you have to use a custom UI and can not use the UIActivityViewController. (As far as i know)
So, it is possible, if you want to have multiple buttons like:
"Share with WhatsApp"
"Share with Facebook"
etc.

UIActivityIndicatorView doesn't stop at the right time

I'm trying to share video from my app with UIActivityViewController.
So, the process looks like that:
User is tapping on Share button.
I have to prepare video for him with my function saveToShare().
I'm starting animation for my UIActivityIndicatorView and launching saveToShare().
I'm sending notification from saveToShare to my controller.
Observer in my controller is launching function shareVideo().
shareVideo() looks like that:
func videoIsReady() {
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
let videoName = "NewWatermarkedVideoNew2Share.mov"
let exportPath = NSTemporaryDirectory().appending(videoName)
let exportUrl = URL(fileURLWithPath: exportPath)
let urlData = NSData(contentsOf: exportUrl)
if ((urlData) != nil){
let videoLink = exportUrl
let objectsToShare = [videoLink]
let activityVC = UIActivityViewController(activityItems: objectsToShare, applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view // so that iPads won't crash
activityVC.setValue("#myhashtag", forKey: "subject")
activityVC.excludedActivityTypes = [UIActivityType.airDrop, UIActivityType.addToReadingList, UIActivityType.assignToContact, UIActivityType.copyToPasteboard, UIActivityType.openInIBooks, UIActivityType.postToTencentWeibo, UIActivityType.postToVimeo, UIActivityType.postToWeibo, UIActivityType.print, UIActivityType.saveToCameraRoll, UIActivityType.postToFlickr, UIActivityType.postToTwitter, UIActivityType(rawValue: "com.apple.reminders.RemindersEditorExtension"), UIActivityType(rawValue: "com.apple.mobilenotes.SharingExtension"),UIActivityType(rawValue: "com.google.Drive.ShareExtension"), UIActivityType(rawValue: "com.apple.mobileslideshow.StreamShareService")]
self.present(activityVC, animated: true, completion: {
})
} else {
print("url is empty...")
}
}
It works, but my UIActivityIndicatorView is not hidden before share dialog and actually is working for several seconds after that dialog is shown.
What is wrong here?
P.S. So, it works if I put UIActivityIndicatorView in DispatchQueue.main.async so my problem is solved but I don't know why this problem was arisen in the first place.
To get it to disappear immediately, you'll want to call the code on the main queue synchronously. To avoid a dead lock (in case videoIsReady() is called from the main queue) use this little extension I've developed:
extension DispatchQueue {
class func safeUISync(execute workItem: DispatchWorkItem) {
if Thread.isMainThread { workItem.perform() }
else { DispatchQueue.main.sync(execute: workItem) }
}
class func safeUISync<T>(execute work: () throws -> T) rethrows -> T {
if Thread.isMainThread { return try work() }
else { return try DispatchQueue.main.sync(execute: work) }
}
}
Now you can call your code as follows:
func videoIsReady() {
DispatchQueue.safeUISync {
self.activityIndicator.stopAnimating()
self.activityIndicator.isHidden = true
}
...
}
You must be calling your videoIsReady() function from a background thread. All UI calls must be made from the main thread or the results are undefined. A common result is for the UI changes to take a LOOONG time to appear. (Another common result is a crash.)

Swift : Share image using UIActivityViewController

I want to share QR image on tap of button using ActivityViewController.
Below is code that I’ve used :
#IBAction func btnShareQRCode_Clicked(sender: UIButton) {
self.shareQRCodeUsingActivityViewController(self.imageviewQRCode.image!)
}
func shareQRCodeUsingActivityViewController(imageParamater: UIImage) {
let activityItem: [UIImage] = [imageParamater as UIImage]
let objActivityViewController = UIActivityViewController(activityItems: activityItem as [UIImage], applicationActivities: nil)
objActivityViewController.excludedActivityTypes = [UIActivityTypeAirDrop, UIActivityTypeAddToReadingList]
// objActivityViewController.popoverPresentationController?.sourceView = sender
self.presentViewController(objActivityViewController, animated: true, completion: {
objActivityViewController.completionWithItemsHandler = { activity, success, items, error in
if !success { print("cancelled")
return
}
if activity == UIActivityTypeMail {
print("mail")
}
else if activity == UIActivityTypeMessage {
print("message")
}
else if activity == UIActivityTypeSaveToCameraRoll {
print("camera")
}
}
})
}
func completionHandler() {
}
The issue with this is that it is getting crashed on mail stating an error regarding MailComposer.
I want to know how and where these MailComposer function should be handled?
If you are running this on iOS Simulator, Mail component is likely to fail.
Apart from that, I don't think you need to cast your activity items list as UIImage. Simply put an array of objects as a activityItems array.

UIActivityViewController UIActivityViewControllerCompletionWithItemsHandler

List item
Using Swift for an app that runs in iOS 8, I need to write a completion handler for the UIActivityViewController to capture the results of which "share" method a user selected.
This is a snippet of the code I have so far. My question is how to I set the avc.completionWithItemsHandler? I'm sure it's simple, but I don't see it.
var activityItems = NSMutableArray()
activityItems.addObject("Email or text for 'share' goes here")
var avc = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
avc.setValue("Subject for Email", forKey: "Subject")
avc.completionWithItemsHandler = //Here is where I dont know what to do.
self.navigationController?.presentViewController(avc, animated: true, completion: nil)
The completionWithItemsHandler typealias:
typealias UIActivityViewControllerCompletionWithItemsHandler = (String?, Bool, [AnyObject]?, NSError?) -> Void
Note: the previous code block is not to be used in your project, it just shows the type of closure needed (docs).
So those are the parameters that are passed into the completion handler for you to do with as you will, so the completion handler would look like this:
avc.completionWithItemsHandler = { activity, success, items, error in
}
NOTE: Because I didn't read the "SWIFT" part of the question, I answered the question in Obj-C. My bad, To the OP: I apologize
Here is a more complete answer that actually can be compiled. I used: dispatch_async in order to do an alert so you can see what "activityType" ended up being.
avc.completionWithItemsHandler = ^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError *activityError) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertViewQuick(#"Activity Status", activityType, #"OK");
});
if (completed)
{
NSLog(#"The Activity: %# was completed", activityType);
}
else
{
NSLog(#"The Activity: %# was NOT completed", activityType);
}
};
As this answer says, for Swift 3 and 4 and iOS 10 and 11 use it like this:
activityVC.completionWithItemsHandler = {(activityType: UIActivityType?, completed: Bool, returnedItems: [Any]?, error: Error?) in
}
present(activityVC, animated: true, completion: nil)
This was answered quite a while ago, but has a mix of missing and non-swift info so here's my version in the hope that it will help someone needing a more complete example of the completion handler:
avc.completionWithItemsHandler = {[weak self](activityTypeChosen, completed:Bool, returnedItems:[AnyObject]?, error:NSError?) -> Void in
// ReturnedItems is an array of modified NSExtensionItem, or nil of nothing modified
// if (activityType == nil) User dismissed the view controller without making a selection.
// Dismiss the view controller we presented
// (assume a reference to it was stored in self.activityVC)
self?.activityVC?.dismissViewControllerAnimated(true, completion: {
if activityTypeChosen == nil {
NSLog("User canceled without choosing anything")
}
else if completed {
NSLog(")User chose an activity and iOS sent it to that other app/service/whatever OK")
}
else {
NSLog("There was an error: \(error)")
}
})
}
Note the line where it dismisses the view controller. The docs for UIActivityViewController say very explicitly that your app is responsible for both presenting the VC and dismissing it.

Detect permission of camera in iOS

I am developing a very simple video app. I use the official control: UIImagePickerController.
Here is the problem. When presenting the UIImagePickerController for the first time, the iOS will ask for the permission. The user can click yes or no. If the user clicks no, the control is not dismissed. Instead, if the user keeps clicking the start button, the timers go on while the screen is always black, and the user can't stop the timers or go back. The only thing the user can do is to kill the app. The next time the UIImagePickerController is presented, it is still a black screen and the user can't go back if clicking start.
I was wondering if it's a bug. Is there any way we can detect the permission of the camera so that we can decide to show the UIImagePickerController or not?
Check the AVAuthorizationStatus and handle the cases properly.
NSString *mediaType = AVMediaTypeVideo;
AVAuthorizationStatus authStatus = [AVCaptureDevice authorizationStatusForMediaType:mediaType];
if(authStatus == AVAuthorizationStatusAuthorized) {
// do your logic
} else if(authStatus == AVAuthorizationStatusDenied){
// denied
} else if(authStatus == AVAuthorizationStatusRestricted){
// restricted, normally won't happen
} else if(authStatus == AVAuthorizationStatusNotDetermined){
// not determined?!
[AVCaptureDevice requestAccessForMediaType:mediaType completionHandler:^(BOOL granted) {
if(granted){
NSLog(#"Granted access to %#", mediaType);
} else {
NSLog(#"Not granted access to %#", mediaType);
}
}];
} else {
// impossible, unknown authorization status
}
Swift 4 and newer
Make sure to:
import AVFoundation
The code below checks for all possible permission states:
let cameraMediaType = AVMediaType.video
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: cameraMediaType)
switch cameraAuthorizationStatus {
case .denied: break
case .authorized: break
case .restricted: break
case .notDetermined:
// Prompting user for the permission to use the camera.
AVCaptureDevice.requestAccess(for: cameraMediaType) { granted in
if granted {
print("Granted access to \(cameraMediaType)")
} else {
print("Denied access to \(cameraMediaType)")
}
}
}
Since iOS 10 you need to specify
NSCameraUsageDescription key in your Info.plist to be able ask for camera access, otherwise your app will crash at runtime. See APIs Requiring Usage Descriptions.
An interesting sidenote from Apple Developer forum:
The system actually kills your app if the user toggles your app's
access to camera in Settings. The same applies to any protected
dataclass in the Settings→Privacy section.
Swift Solution
extension AVCaptureDevice {
enum AuthorizationStatus {
case justDenied
case alreadyDenied
case restricted
case justAuthorized
case alreadyAuthorized
case unknown
}
class func authorizeVideo(completion: ((AuthorizationStatus) -> Void)?) {
AVCaptureDevice.authorize(mediaType: AVMediaType.video, completion: completion)
}
class func authorizeAudio(completion: ((AuthorizationStatus) -> Void)?) {
AVCaptureDevice.authorize(mediaType: AVMediaType.audio, completion: completion)
}
private class func authorize(mediaType: AVMediaType, completion: ((AuthorizationStatus) -> Void)?) {
let status = AVCaptureDevice.authorizationStatus(for: mediaType)
switch status {
case .authorized:
completion?(.alreadyAuthorized)
case .denied:
completion?(.alreadyDenied)
case .restricted:
completion?(.restricted)
case .notDetermined:
AVCaptureDevice.requestAccess(for: mediaType, completionHandler: { (granted) in
DispatchQueue.main.async {
if granted {
completion?(.justAuthorized)
} else {
completion?(.justDenied)
}
}
})
#unknown default:
completion?(.unknown)
}
}
}
And then in order to use it you do
AVCaptureDevice.authorizeVideo(completion: { (status) in
//Your work here
})
As an addition to the answer from #Raptor the following should be mentioned. You may receive the following error starting with iOS 10: This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
To fix this, make sure you handle the results from the main thread as follows (Swift 3):
private func showCameraPermissionPopup() {
let cameraMediaType = AVMediaTypeVideo
let cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(forMediaType: cameraMediaType)
switch cameraAuthorizationStatus {
case .denied:
NSLog("cameraAuthorizationStatus=denied")
break
case .authorized:
NSLog("cameraAuthorizationStatus=authorized")
break
case .restricted:
NSLog("cameraAuthorizationStatus=restricted")
break
case .notDetermined:
NSLog("cameraAuthorizationStatus=notDetermined")
// Prompting user for the permission to use the camera.
AVCaptureDevice.requestAccess(forMediaType: cameraMediaType) { granted in
DispatchQueue.main.sync {
if granted {
// do something
} else {
// do something else
}
}
}
}
}
Specify NSCameraUsageDescription key in Info.plist first.
Then check AVAuthorizationStatus if Authorised then present the UIImagePickerController. It will work.
Swift: Using AVFoundation
Add AVFoundation to Target -> Build Phases -> Link Binary with Libraries.
import AVFoundation on ViewController.
On Info.plist, Add the following:
On View Controller:
#IBAction func cameraButtonClicked(sender: AnyObject) {
let authorizationStatus = AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo)
print(authorizationStatus.rawValue)
if AVCaptureDevice.authorizationStatusForMediaType(AVMediaTypeVideo) == AVAuthorizationStatus.Authorized{
self.openCameraAfterAccessGrantedByUser()
}
else
{
print("No Access")
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
AVCaptureDevice.requestAccessForMediaType(AVMediaTypeVideo, completionHandler: { (granted :Bool) -> Void in
if granted == true
{
// User granted
self.openCameraAfterAccessGrantedByUser()
}
else
{
// User Rejected
alertToEncourageCameraAccessWhenApplicationStarts()
}
});
}
}
//Open camera
func openCameraAfterAccessGrantedByUser()
{
if(UIImagePickerController .isSourceTypeAvailable(UIImagePickerControllerSourceType.Camera)){
self.cameraAndGalleryPicker!.sourceType = UIImagePickerControllerSourceType.Camera
cameraAndGalleryPicker?.delegate = self
cameraAndGalleryPicker?.allowsEditing = false
cameraAndGalleryPicker!.cameraCaptureMode = .Photo
cameraAndGalleryPicker!.modalPresentationStyle = .FullScreen
presentViewController(self.cameraAndGalleryPicker!, animated: true, completion: nil)
}
else
{
}
}
//Show Camera Unavailable Alert
func alertToEncourageCameraAccessWhenApplicationStarts()
{
//Camera not available - Alert
let cameraUnavailableAlertController = UIAlertController (title: "Camera Unavailable", message: "Please check to see if it is disconnected or in use by another application", preferredStyle: .Alert)
let settingsAction = UIAlertAction(title: "Settings", style: .Destructive) { (_) -> Void in
let settingsUrl = NSURL(string:UIApplicationOpenSettingsURLString)
if let url = settingsUrl {
dispatch_async(dispatch_get_main_queue()) {
UIApplication.sharedApplication().openURL(url)
}
}
}
let cancelAction = UIAlertAction(title: "Okay", style: .Default, handler: nil)
cameraUnavailableAlertController .addAction(settingsAction)
cameraUnavailableAlertController .addAction(cancelAction)
self.window?.rootViewController!.presentViewController(cameraUnavailableAlertController , animated: true, completion: nil)
}

Resources