Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Making getNotificationSettings return instead of using a completion block

I have a method that is used in several places of an app I'm working on. It's a method to check if remote push notifications are enabled or not. The method returns a value but as you may know currentUserNotificationSettings was deprecated so now I'm using getNotificationSettings.

The problem is the first returns a value and the latests uses a block. I want to still be able to return a value to avoid refactoring everything so I wrote the following but it's failing and I can't understand why...

Is this ok?!

public static var isRemoteEnabled: Bool {
  var notificationSettings: UNNotificationSettings?
  let semasphore = DispatchSemaphore(value: 2)

  UNUserNotificationCenter.current().getNotificationSettings { setttings in
      notificationSettings = setttings
      semasphore.signal()
  }

  semasphore.wait()
  guard let authorizationStatus = notificationSettings?.authorizationStatus else { return false }
  return authorizationStatus == .authorized
}

Edited:

I followed @rmaddy comment and at least now it doesn't crash but it gets stuck in the wait(). If I go to debugger and e semasphore.signal() it completes and the app continues working fine. Somehow the completion block is not being called.

like image 865
Andres Avatar asked Jun 19 '18 20:06

Andres


Video Answer


1 Answers

In a case like this you want the semaphore created with an initial value of 0, not 2.

let semasphore = DispatchSemaphore(value: 0)

This is mentioned in the documentation:

Passing zero for the value is useful for when two threads need to reconcile the completion of a particular event.

wait first decrements the value. It then blocks until the value is greater or equal to 0. With your value of 2, it was decremented to 1 and since that is already greater or equal to 0, the wait didn't need to block and your method returned long before the call to signal.

There is also the chance that the completion block for getNotificationSettings could be called on the same thread (causing a deadlock) so call it on a background queue.

public static var isRemoteEnabled: Bool {
    var notificationSettings: UNNotificationSettings?
    let semasphore = DispatchSemaphore(value: 0)

    DispatchQueue.global().async {    
        UNUserNotificationCenter.current().getNotificationSettings { setttings in
            notificationSettings = setttings
            semasphore.signal()
        }
    }

    semasphore.wait()
    guard let authorizationStatus = notificationSettings?.authorizationStatus else { return false }
    return authorizationStatus == .authorized
}
like image 80
rmaddy Avatar answered Sep 27 '22 00:09

rmaddy