3

I want to request some permissions and want to return false unless all permissions have been granted. I get an error: "Unexpected non-void return value in void function"

But I am returning true/false in all circumstances, what is wrong ?

func requestPermissions () -> Bool {
    let types: UIRemoteNotificationType = [.alert, .badge, .sound]
    UIApplication.shared.registerForRemoteNotifications(matching: types)
    let center = UNUserNotificationCenter.current()

    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (videoGranted: Bool) -> Void in
        if (videoGranted) {
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeAudio, completionHandler: { (audioGranted: Bool) -> Void in
                if (audioGranted) {
                    center.requestAuthorization(options: [.alert, .badge, .sound]) { (notificationGranted:Bool, error) -> Void in
                        if (notificationGranted) {
                            DispatchQueue.main.async {

                                return true
                                print("Video, audio & notifications granted")
                            }
                        } else {

                            return false
                            print("Rejected notifications")
                        }
                    }
                }else {

                    return false
                    print("Rejected audio")
                }
            })
        } else {

            return false
            print("Rejected video")
        }
    })
}

Any help would be very much appreciated ! Thank you.

Alternative Answer:

func requestPermissionss (completion: ((Bool, Bool, Bool)->Void)?) {
    let types: UIRemoteNotificationType = [.alert, .badge, .sound]
    UIApplication.shared.registerForRemoteNotifications(matching: types)
    let center = UNUserNotificationCenter.current()

    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (videoGranted: Bool) -> Void in
        AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeAudio, completionHandler: { (audioGranted: Bool) -> Void in
            center.requestAuthorization(options: [.alert, .badge, .sound]) { (notificationGranted:Bool, error) -> Void in
                completion?(videoGranted, audioGranted, notificationGranted)
            }
        })
    })
}
KML
  • 2,302
  • 5
  • 26
  • 47
  • else { print("Rejected notifications") has no status=true –  Nov 12 '16 at 19:18
  • @alldani-com Hi I copied in the wrong function, sorry. Updated question. – KML Nov 12 '16 at 19:22
  • Does swift still run after a return statement? I know python does not. –  Nov 12 '16 at 19:44
  • 1
    Lookup "How to return value from an *asynchronous* function" – Martin R Nov 12 '16 at 19:44
  • 1
    try putting print statements before return statements. –  Nov 12 '16 at 19:44
  • See for example http://stackoverflow.com/questions/28390635/can-swift-return-value-from-an-async-void-returning-block or http://stackoverflow.com/questions/31064592/returning-object-from-callback-in-swift or http://stackoverflow.com/questions/37798352/how-to-return-a-value-from-a-void-closure-in-swift – Martin R Nov 12 '16 at 19:47

2 Answers2

1

The issue is that inside the closure return keyword has nothing to do with the return of requestPermissions() function. It rather means return from the closure, which doesn't actually return anything (because of the -> Void part)

You can pass a closure with three bool parameters to your function like this:

func requestPermissions (completion: ((Bool, Bool, Bool)->Void)?) {
    let types: UIRemoteNotificationType = [.alert, .badge, .sound]
    UIApplication.shared.registerForRemoteNotifications(matching: types)
    let center = UNUserNotificationCenter.current()

    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (videoGranted: Bool) -> Void in
        if (videoGranted) {
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeAudio, completionHandler: { (audioGranted: Bool) -> Void in
                if (audioGranted) {
                    center.requestAuthorization(options: [.alert, .badge, .sound]) { (notificationGranted:Bool, error) -> Void in
                        if (notificationGranted) {
                            completion?(true, true, true)
                        } else {
                            completion?(true, true, false)
                        }
                    }
                }else {
                    completion?(true, false, false)
                }
            })
        } else {
            completion?(false, false, false)
        }
    })
}

Usage:

requestPermissions { (videoGranted, audioGranted, notificationsGranted) in
    print("video granted: \(videoGranted)")
    print("audio granted: \(audioGranted)")
    print("notifications granted: \(notificationsGranted)")
}

Note, that I also removed DispatchQueue.main.async because it has no sense here. Again, return means return from the current scope, not from the outer function.

Edit

Another thing to note is that with your code you won't ask permissions for audio and notifications if permission for video was declined. Not sure if it is intentional or not. If in fact you need to request all three things independently, which I suppose is your case, you should use approach similar to this:

func requestPermissions() {
    let types: UIRemoteNotificationType = [.alert, .badge, .sound]
    UIApplication.shared.registerForRemoteNotifications(matching: types)

    self.requestVideo { videoGranted in
        print("video granted: \(videoGranted)")
        self.requestAudio { audioGranted in
            print("audio granted: \(audioGranted)")
            self.requestNotifications { notificationsGranted in
                print("notification granted: \(notificationsGranted)")
            }
        }
    }
}


func requestVideo (completion: ((Bool)->Void)?) {
    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo, completionHandler: { (videoGranted: Bool) -> Void in
        completion?(videoGranted)
    })
}

func requestAudio (completion: ((Bool)->Void)?) {
    AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeAudio, completionHandler: { (audioGranted: Bool) -> Void in
        completion?(audioGranted)
    })
}

func requestNotifications (completion: ((Bool)->Void)?) {
    UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { (notificationGranted:Bool, error) -> Void in
        completion?(notificationGranted)
    }
}

Basically you ask for the next thing as soon as you're done with previous one and every next one is requested independently no matter what was the choice on previous requests.

alexburtnik
  • 7,661
  • 4
  • 32
  • 70
  • I am not sure how to say this, but if I do that the function ill return the initialized value (false) automatically. and then after all the permissions has been asked it will return whatever they give.. – KML Nov 12 '16 at 19:42
1

You are trying to return Bool values in completionHandler block which is of the return type void which you might obviously know nothing is supposed to be returned !!!

That is the reason you are facing this error "Unexpected non-void return value in void function"