diff --git a/Permission.podspec b/Permission.podspec index 3fc73a2..b552ce9 100644 --- a/Permission.podspec +++ b/Permission.podspec @@ -21,71 +21,76 @@ Pod::Spec.new do |s| s.subspec 'AddressBook' do |ab| ab.dependency 'Permission/Core' - ab.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_ADDRESS_BOOK" } + ab.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_ADDRESS_BOOK" } end s.subspec 'Bluetooth' do |bl| bl.dependency 'Permission/Core' - bl.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_BLUETOOTH" } + bl.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_BLUETOOTH" } end s.subspec 'Camera' do |cm| cm.dependency 'Permission/Core' - cm.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_CAMERA" } + cm.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_CAMERA" } end s.subspec 'Contacts' do |cn| cn.dependency 'Permission/Core' - cn.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_CONTACTS" } + cn.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_CONTACTS" } end s.subspec 'Events' do |ev| ev.dependency 'Permission/Core' - ev.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_EVENTS" } + ev.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_EVENTS" } end s.subspec 'Location' do |lo| lo.dependency 'Permission/Core' - lo.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_LOCATION" } + lo.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_LOCATION" } end s.subspec 'Microphone' do |mi| mi.dependency 'Permission/Core' - mi.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MICROPHONE" } + mi.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MICROPHONE" } end s.subspec 'Motion' do |mo| mo.dependency 'Permission/Core' - mo.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MOTION" } + mo.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MOTION" } end s.subspec 'Notifications' do |no| no.dependency 'Permission/Core' - no.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_NOTIFICATIONS" } + no.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_NOTIFICATIONS" } + end + + s.subspec 'UserNotifications' do |un| + un.dependency 'Permission/Core' + un.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_USER_NOTIFICATIONS" } end s.subspec 'Photos' do |ph| ph.dependency 'Permission/Core' - ph.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_PHOTOS" } + ph.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_PHOTOS" } end s.subspec 'Reminders' do |re| re.dependency 'Permission/Core' - re.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_REMINDERS" } + re.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_REMINDERS" } end s.subspec 'SpeechRecognizer' do |rs| rs.dependency 'Permission/Core' - rs.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_SPEECH_RECOGNIZER" } + rs.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_SPEECH_RECOGNIZER" } end s.subspec 'MediaLibrary' do |ml| ml.dependency 'Permission/Core' - ml.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MEDIA_LIBRARY" } + ml.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_MEDIA_LIBRARY" } end s.subspec 'Siri' do |ab| ab.dependency 'Permission/Core' - ab.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_SIRI" } + ab.pod_target_xcconfig = { "SWIFT_ACTIVE_COMPILATION_CONDITIONS" => "PERMISSION_SIRI" } end end diff --git a/Permission.xcodeproj/project.pbxproj b/Permission.xcodeproj/project.pbxproj index 04a199b..cb445ec 100644 --- a/Permission.xcodeproj/project.pbxproj +++ b/Permission.xcodeproj/project.pbxproj @@ -33,6 +33,7 @@ 6DF9C2BE1C8F4FE5000710C1 /* Photos.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2BD1C8F4FE5000710C1 /* Photos.swift */; }; 6DF9C2C01C8F5003000710C1 /* Events.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2BF1C8F5003000710C1 /* Events.swift */; }; 6DF9C2C61C8F5B4C000710C1 /* Location.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6DF9C2C51C8F5B4C000710C1 /* Location.swift */; }; + E1370D871E31F7F700C9E83D /* UserNotifications.swift in Sources */ = {isa = PBXBuildFile; fileRef = E1370D861E31F7F700C9E83D /* UserNotifications.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -76,6 +77,7 @@ 6DF9C2BF1C8F5003000710C1 /* Events.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Events.swift; sourceTree = ""; }; 6DF9C2C51C8F5B4C000710C1 /* Location.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Location.swift; sourceTree = ""; }; D08FF2891DC3AD2900F28088 /* PermissionFlags.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = PermissionFlags.xcconfig; sourceTree = ""; }; + E1370D861E31F7F700C9E83D /* UserNotifications.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserNotifications.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -154,6 +156,7 @@ 6DF9C2B91C8F4FAC000710C1 /* Microphone.swift */, 6D935F5E1C9A14AB00BB39E3 /* Motion.swift */, 6DF9C2B71C8F4F8F000710C1 /* Notifications.swift */, + E1370D861E31F7F700C9E83D /* UserNotifications.swift */, 6DF9C2BD1C8F4FE5000710C1 /* Photos.swift */, 6DF9C2B51C8F4F69000710C1 /* Reminders.swift */, 3F21DC7C1E30B6DB00B3EF65 /* Siri.swift */, @@ -291,6 +294,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + E1370D871E31F7F700C9E83D /* UserNotifications.swift in Sources */, 6D491E781C9CA90B00611006 /* PermissionStatus.swift in Sources */, 6D935F5F1C9A14AB00BB39E3 /* Motion.swift in Sources */, 6D935F5D1C9A0FEA00BB39E3 /* Bluetooth.swift in Sources */, diff --git a/PermissionConfiguration.xcconfig b/PermissionConfiguration.xcconfig index 5bf1ae5..88b2ecd 100644 --- a/PermissionConfiguration.xcconfig +++ b/PermissionConfiguration.xcconfig @@ -22,22 +22,23 @@ // SOFTWARE. // -PERMISSION_ADDRESS_BOOK = PERMISSION_ADDRESS_BOOK -PERMISSION_BLUETOOTH = PERMISSION_BLUETOOTH -PERMISSION_CAMERA = PERMISSION_CAMERA -PERMISSION_CONTACTS = PERMISSION_CONTACTS -PERMISSION_EVENTS = PERMISSION_EVENTS -PERMISSION_LOCATION = PERMISSION_LOCATION -PERMISSION_MICROPHONE = PERMISSION_MICROPHONE -PERMISSION_MOTION = PERMISSION_MOTION -PERMISSION_NOTIFICATIONS = PERMISSION_NOTIFICATIONS -PERMISSION_PHOTOS = PERMISSION_PHOTOS -PERMISSION_REMINDERS = PERMISSION_REMINDERS -PERMISSION_SPEECH_RECOGNIZER = PERMISSION_SPEECH_RECOGNIZER -PERMISSION_MEDIA_LIBRARY = PERMISSION_MEDIA_LIBRARY -PERMISSION_SIRI = PERMISSION_SIRI +PERMISSION_ADDRESS_BOOK = PERMISSION_ADDRESS_BOOK +PERMISSION_BLUETOOTH = PERMISSION_BLUETOOTH +PERMISSION_CAMERA = PERMISSION_CAMERA +PERMISSION_CONTACTS = PERMISSION_CONTACTS +PERMISSION_EVENTS = PERMISSION_EVENTS +PERMISSION_LOCATION = PERMISSION_LOCATION +PERMISSION_MICROPHONE = PERMISSION_MICROPHONE +PERMISSION_MOTION = PERMISSION_MOTION +PERMISSION_NOTIFICATIONS = PERMISSION_NOTIFICATIONS +PERMISSION_USER_NOTIFICATIONS = PERMISSION_USER_NOTIFICATIONS +PERMISSION_PHOTOS = PERMISSION_PHOTOS +PERMISSION_REMINDERS = PERMISSION_REMINDERS +PERMISSION_SPEECH_RECOGNIZER = PERMISSION_SPEECH_RECOGNIZER +PERMISSION_MEDIA_LIBRARY = PERMISSION_MEDIA_LIBRARY +PERMISSION_SIRI = PERMISSION_SIRI // Do not modify this line. Instead, remove comments above as needed to enable the categories your app uses. -PERMISSION_FLAGS = $(PERMISSION_ADDRESS_BOOK) $(PERMISSION_BLUETOOTH) $(PERMISSION_CAMERA) $(PERMISSION_CONTACTS) $(PERMISSION_EVENTS) $(PERMISSION_LOCATION) $(PERMISSION_MICROPHONE) $(PERMISSION_MOTION) $(PERMISSION_NOTIFICATIONS) $(PERMISSION_PHOTOS) $(PERMISSION_REMINDERS) $(PERMISSION_SPEECH_RECOGNIZER) $(PERMISSION_MEDIA_LIBRARY) $(PERMISSION_SIRI) +PERMISSION_FLAGS = $(PERMISSION_ADDRESS_BOOK) $(PERMISSION_BLUETOOTH) $(PERMISSION_CAMERA) $(PERMISSION_CONTACTS) $(PERMISSION_EVENTS) $(PERMISSION_LOCATION) $(PERMISSION_MICROPHONE) $(PERMISSION_MOTION) $(PERMISSION_NOTIFICATIONS) $(PERMISSION_USER_NOTIFICATIONS) $(PERMISSION_PHOTOS) $(PERMISSION_REMINDERS) $(PERMISSION_SPEECH_RECOGNIZER) $(PERMISSION_MEDIA_LIBRARY) $(PERMISSION_SIRI) -SWIFT_ACTIVE_COMPILATION_CONDITIONS= $(inherited) $(PERMISSION_FLAGS) +SWIFT_ACTIVE_COMPILATION_CONDITIONS = $(inherited) $(PERMISSION_FLAGS) diff --git a/Source/Permission.swift b/Source/Permission.swift index 9ba0aa2..0dfdce6 100644 --- a/Source/Permission.swift +++ b/Source/Permission.swift @@ -21,6 +21,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // +#if PERMISSION_USER_NOTIFICATIONS +import UserNotifications +#endif open class Permission: NSObject { public typealias Callback = (PermissionStatus) -> Void @@ -97,38 +100,57 @@ open class Permission: NSObject { open static let siri = Permission(type: .siri) #endif - #if PERMISSION_NOTIFICATIONS + #if PERMISSION_USER_NOTIFICATIONS + /// Variable used to retain the notifications permission. + @available(iOS 10.0, *) + fileprivate static var _userNotifications: Permission? + /// The permission to send notifications. - open static let notifications: Permission = { - let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil) - return Permission(type: .notifications(settings)) + @available(iOS 10.0, *) + open static let userNotifications: Permission = { + let settings: UNAuthorizationOptions = [.alert, .badge, .sound] + _userNotifications = Permission(type: .userNotifications(settings)) + return _userNotifications! }() + /// The permission to send notifications. + @available(iOS 10.0, *) + open static func userNotifications(options: UNAuthorizationOptions) -> Permission { + _userNotifications = Permission(type: .userNotifications(options)) + return _userNotifications! + } + #endif + + #if PERMISSION_NOTIFICATIONS /// Variable used to retain the notifications permission. fileprivate static var _notifications: Permission? + /// The permission to send notifications. + open static let notifications: Permission = { + let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: nil) + _notifications = Permission(type: .notifications(settings)) + return _notifications! + }() + /// The permission to send notifications. open static func notifications(types: UIUserNotificationType, categories: Set?) -> Permission { - let settings = UIUserNotificationSettings(types: types, categories: categories) - let permission = Permission(type: .notifications(settings)) - _notifications = permission - return permission + let settings = UIUserNotificationSettings(types: types, categories: categories) + _notifications = Permission(type: .notifications(settings)) + return _notifications! } /// The permission to send notifications. open static func notifications(types: UIUserNotificationType) -> Permission { let settings = UIUserNotificationSettings(types: types, categories: nil) - let permission = Permission(type: .notifications(settings)) - _notifications = permission - return permission + _notifications = Permission(type: .notifications(settings)) + return _notifications! } /// The permission to send notifications. open static func notifications(categories: Set?) -> Permission { let settings = UIUserNotificationSettings(types: [.badge, .sound, .alert], categories: categories) - let permission = Permission(type: .notifications(settings)) - _notifications = permission - return permission + _notifications = Permission(type: .notifications(settings)) + return _notifications! } #endif @@ -154,6 +176,10 @@ open class Permission: NSObject { if case .notifications = type { return statusNotifications } #endif + #if PERMISSION_USER_NOTIFICATIONS + if case .userNotifications = type { return statusUserNotifications } + #endif + #if PERMISSION_MICROPHONE if case .microphone = type { return statusMicrophone } #endif @@ -274,7 +300,7 @@ open class Permission: NSObject { #endif #if PERMISSION_LOCATION - if case .locationAlways = type { + if case .locationAlways = type { requestLocationAlways(callback) return } @@ -292,6 +318,13 @@ open class Permission: NSObject { } #endif + #if PERMISSION_USER_NOTIFICATIONS + if case .userNotifications = type { + requestUserNotifications(callback) + return + } + #endif + #if PERMISSION_MICROPHONE if case .microphone = type { requestMicrophone(callback) diff --git a/Source/PermissionType.swift b/Source/PermissionType.swift index ba10853..58ed2f6 100644 --- a/Source/PermissionType.swift +++ b/Source/PermissionType.swift @@ -21,6 +21,9 @@ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. // +#if PERMISSION_USER_NOTIFICATIONS +import UserNotifications +#endif public enum PermissionType { #if PERMISSION_CONTACTS @@ -36,8 +39,12 @@ public enum PermissionType { case locationWhenInUse #endif + #if PERMISSION_USER_NOTIFICATIONS + @available(iOS 10.0, *) case userNotifications(UNAuthorizationOptions) + #endif + #if PERMISSION_NOTIFICATIONS - case notifications(UIUserNotificationSettings) + case notifications(UIUserNotificationSettings) // Deprecated in iOS 10.0 #endif #if PERMISSION_MICROPHONE @@ -100,6 +107,10 @@ extension PermissionType: CustomStringConvertible { if case .notifications = self { return "Notifications" } #endif + #if PERMISSION_USER_NOTIFICATIONS + if case .userNotifications = self { return "UserNotifications" } + #endif + #if PERMISSION_MICROPHONE if case .microphone = self { return "Microphone" } #endif diff --git a/Source/PermissionTypes/Notifications.swift b/Source/PermissionTypes/Notifications.swift index 8b7bbf5..58fd7bb 100644 --- a/Source/PermissionTypes/Notifications.swift +++ b/Source/PermissionTypes/Notifications.swift @@ -23,12 +23,23 @@ // #if PERMISSION_NOTIFICATIONS +import ObjectiveC +private var timerKey: UInt8 = 0 + internal extension Permission { + private var timer: Timer? { + get { + return objc_getAssociatedObject(self, &timerKey) as? Timer + } + set(newValue) { + objc_setAssociatedObject(self, &timerKey, newValue, objc_AssociationPolicy.OBJC_ASSOCIATION_RETAIN) + } + } + var statusNotifications: PermissionStatus { - if UIApplication.shared.currentUserNotificationSettings?.types.isEmpty == false { + if let settings = UIApplication.shared.currentUserNotificationSettings, settings.types.isEmpty == false { return .authorized } - return UserDefaults.standard.requestedNotifications ? .denied : .notDetermined } @@ -36,16 +47,19 @@ internal extension Permission { guard case .notifications(let settings) = type else { fatalError() } NotificationCenter.default.addObserver(self, selector: #selector(requestingNotifications), name: .UIApplicationWillResignActive) + timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(finishedRequestingNotifications), userInfo: nil, repeats: false) UIApplication.shared.registerUserNotificationSettings(settings) } - @objc func requestingNotifications() { + @objc private dynamic func requestingNotifications() { + timer?.invalidate() + NotificationCenter.default.removeObserver(self, name: .UIApplicationWillResignActive) NotificationCenter.default.addObserver(self, selector: #selector(finishedRequestingNotifications), name: .UIApplicationDidBecomeActive) } - @objc func finishedRequestingNotifications() { + @objc private dynamic func finishedRequestingNotifications() { NotificationCenter.default.removeObserver(self, name: .UIApplicationWillResignActive) NotificationCenter.default.removeObserver(self, name: .UIApplicationDidBecomeActive) diff --git a/Source/PermissionTypes/UserNotifications.swift b/Source/PermissionTypes/UserNotifications.swift new file mode 100644 index 0000000..aebd28c --- /dev/null +++ b/Source/PermissionTypes/UserNotifications.swift @@ -0,0 +1,56 @@ +// +// UserNotifications.swift +// Permission +// +// Created by Andreas Grauel on 20.01.17. +// Copyright © 2017 delba. All rights reserved. +// + +#if PERMISSION_USER_NOTIFICATIONS +import UserNotifications + +internal extension Permission { + + var statusUserNotifications: PermissionStatus { + guard #available(iOS 10.0, *) else { fatalError() } + return synchronousStatusUserNotifications + } + + func requestUserNotifications(_ callback: @escaping Callback) { + guard #available(iOS 10.0, *) else { fatalError() } + guard case .userNotifications(let settings) = type else { fatalError() } + + var status: PermissionStatus = .notDetermined + UNUserNotificationCenter.current().requestAuthorization(options: settings) { (isGranted, error) in + if error != nil { + status = .denied + } else { + status = isGranted ? .authorized : .denied + } + callback(status) + } + } + + fileprivate var synchronousStatusUserNotifications: PermissionStatus { + guard #available(iOS 10.0, *) else { fatalError() } + let semaphore = DispatchSemaphore(value: 0) + + var status: PermissionStatus = .notDetermined + + UNUserNotificationCenter.current().getNotificationSettings { settings in + switch settings.authorizationStatus { + case .authorized: + status = .authorized + case .denied: + status = .denied + case .notDetermined: + status = .notDetermined + } + semaphore.signal() + } + _ = semaphore.wait(timeout: DispatchTime.distantFuture) + + return status + } +} +#endif