Making getNotificationSettings return instead of using a completion block
Asked Answered
A

1

7

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.

Abaxial answered 19/6, 2018 at 20:38 Comment(2)
Were you ever able to resolve this? I am experiencing the same issue.Millett
did you manage to find out why the completion block is not called?Gil
A
19

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
}
Ares answered 19/6, 2018 at 20:43 Comment(4)
That was a good advice, but now somehow the completion block is not completed. I just edited my question to add more info. Thanks buddy!Abaxial
It's possible that the completion block to getNotificationSettings is being called on the same thread which could be causing a deadlock. Wrap it in a call to DispatchQueue.global().async.Ares
Same... the getNotificationSettings is not getting called. This function is used across the whole app and it only fails in one point but I can't identify what's the difference...Abaxial
the problem is somewhere else... the method is perfect now but something is weird somewhere else... thanks for your help @rmaddy!Abaxial

© 2022 - 2024 — McMap. All rights reserved.