Skip to content

Commit

Permalink
Merge branch 'release/1.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
KWANG HYOUN KIM committed Jan 26, 2019
2 parents 441752d + 9df4263 commit 6aea0cf
Show file tree
Hide file tree
Showing 7 changed files with 110 additions and 85 deletions.
2 changes: 1 addition & 1 deletion APNSUtil.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'APNSUtil'
s.version = '1.2.0'
s.version = '1.3.0'
s.summary = 'APNSUtil is makes code simple using apple push notification service.'
s.description = 'APNSUtil is makes code simple using apple push notification service.'
s.homepage = 'https://github.com/pisces/APNSUtil'
Expand Down
83 changes: 36 additions & 47 deletions APNSUtil/Classes/APNSManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class APNSManager {

// MARK: - Constants

public typealias Processing = (RemoteNotificationElement) -> Void
public typealias SubscribeClosure = (RemoteNotificationElement) -> Void

public static let shared = APNSManager()
private let kAuthorizationStatusDetermined: String = "kAuthorizationStatusDetermined"
Expand All @@ -22,7 +22,7 @@ public class APNSManager {

private var isInitialized: Bool = false
private var types: UIUserNotificationType = [.sound, .alert, .badge]
private var processingClosureMap = [Int: Processing]()
private var subscribeClosureMap = [Int: SubscribeClosure]()
private var elements = [RemoteNotificationElement]()
private(set) var isAuthorizationStatusDetermined: Bool {
get {
Expand All @@ -37,42 +37,33 @@ public class APNSManager {
// MARK: - Public methods

@discardableResult
public func begin() -> APNSManager {
public func begin() -> Self {
isInitialized = true
dequeue()
return self
}
public func didFinishLaunching<T: Decodable>(withOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?, as type: T.Type) {
public func didFinishLaunching(withOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {
let remote = launchOptions?[UIApplicationLaunchOptionsKey.remoteNotification] as? [AnyHashable: Any]
let local = launchOptions?[UIApplicationLaunchOptionsKey.localNotification] as? [AnyHashable: Any]
guard let userInfo = remote ?? local else {return}
didReceive(userInfo: userInfo, as: type, isInactive: true)
didReceive(userInfo: userInfo, isInactive: true)
}
public func didReceive<T: Decodable>(userInfo: [AnyHashable : Any], as: T.Type, isInactive: Bool) {
do {
let data = try JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted)
let model = try JSONDecoder().decode(`as`, from: data)
enqueue(RemoteNotificationElement(isInactive: isInactive, model: model)).dequeue()
} catch {
}
public func didReceive(userInfo: [AnyHashable : Any], isInactive: Bool) {
enqueue(.init(isInactive: isInactive, userInfo: userInfo)).dequeue()
}
public func processing(_ subscribable: Subscribable, _ closure: @escaping Processing) -> APNSManager {
guard processingClosureMap[subscribable.hash] == nil else {return self}
processingClosureMap[subscribable.hash] = closure
return self
}
public func register() -> APNSManager {
public func register() -> Self {
#if !APP_EXTENSIONS
if #available(iOS 10.0, *) {
let types = self.types
let options: () -> UNAuthorizationOptions = {
var rawValue: UInt = 0
if self.types.rawValue & UIUserNotificationType.alert.rawValue == UIUserNotificationType.alert.rawValue {
if types.rawValue & UIUserNotificationType.alert.rawValue == UIUserNotificationType.alert.rawValue {
rawValue |= UNAuthorizationOptions.alert.rawValue
}
if self.types.rawValue & UIUserNotificationType.sound.rawValue == UIUserNotificationType.sound.rawValue {
if types.rawValue & UIUserNotificationType.sound.rawValue == UIUserNotificationType.sound.rawValue {
rawValue |= UNAuthorizationOptions.sound.rawValue
}
if self.types.rawValue & UIUserNotificationType.badge.rawValue == UIUserNotificationType.badge.rawValue {
if types.rawValue & UIUserNotificationType.badge.rawValue == UIUserNotificationType.badge.rawValue {
rawValue |= UNAuthorizationOptions.badge.rawValue
}
return UNAuthorizationOptions(rawValue: rawValue)
Expand All @@ -81,11 +72,7 @@ public class APNSManager {
let center = UNUserNotificationCenter.current()
center.delegate = UIApplication.shared.delegate as? UNUserNotificationCenterDelegate
center.requestAuthorization(options: options()) { (granted, error) in
if let error = error {
print("Push registration failed")
print("ERROR: \(error.localizedDescription) - \(error.localizedDescription)")
return
}
guard error == nil else {return}

DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
Expand All @@ -101,56 +88,58 @@ public class APNSManager {
public func registerDeviceToken(_ deviceToken: Data) {
APNSInstance.shared.setAPNSToken(deviceToken)
}
public func setTypes(_ types: UIUserNotificationType) -> APNSManager {
public func setTypes(_ types: UIUserNotificationType) -> Self {
self.types = types
return self
}
public func subscribe<T: Hashable>(_ target: T, _ closure: @escaping SubscribeClosure) -> Self {
guard subscribeClosureMap[target.hashValue] == nil else {return self}
subscribeClosureMap[target.hashValue] = closure
return self
}
public func unregister() {
processingClosureMap.removeAll()
subscribeClosureMap.removeAll()
#if !APP_EXTENSIONS
UIApplication.shared.unregisterForRemoteNotifications()
#endif
APNSInstance.shared.clear()
elements.removeAll()
isAuthorizationStatusDetermined = true
}
public func unsubscribe(_ subscribable: Subscribable) {
processingClosureMap.removeValue(forKey: subscribable.hash)
public func unsubscribe<T: Hashable>(_ target: T) {
subscribeClosureMap.removeValue(forKey: target.hashValue)
}

// MARK: - Private methods

private func dequeue() {
guard isInitialized, elements.count > 0 else {return}
processingClosureMap.forEach { $0.value(elements.first!) }
elements.remove(at: 0)
let element = elements.removeFirst()
subscribeClosureMap.forEach { $0.value(element) }
dequeue()
}
@discardableResult
private func enqueue(_ element: RemoteNotificationElement) -> APNSManager {
private func enqueue(_ element: RemoteNotificationElement) -> Self {
elements.append(element)
return self
}
}

public struct RemoteNotificationElement {
public typealias T = Decodable
public let isInactive: Bool
public let userInfo: [AnyHashable : Any]

public private(set) var isInactive: Bool = false
private var model: T!

public init(isInactive: Bool, model: T) {
public init(isInactive: Bool, userInfo: [AnyHashable : Any]) {
self.isInactive = isInactive
self.model = model
self.userInfo = userInfo
}

public func payload<E: Decodable>() -> E {
return model as! E
public func payload<T: Decodable>() -> T? {
do {
let data = try JSONSerialization.data(withJSONObject: userInfo, options: .prettyPrinted)
return try JSONDecoder().decode(T.self, from: data)
} catch {
return nil
}
}
}

public protocol Subscribable {
var hash: Int {get}
}

extension NSObject: Subscribable {}
2 changes: 1 addition & 1 deletion APNSUtilAppExtension.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

Pod::Spec.new do |s|
s.name = 'APNSUtilAppExtension'
s.version = '1.2.0'
s.version = '1.3.0'
s.summary = 'APNSUtil is makes code simple using apple push notification service.'
s.description = 'APNSUtil is makes code simple using apple push notification service.'
s.homepage = 'https://github.com/pisces/APNSUtil'
Expand Down
18 changes: 11 additions & 7 deletions Example/APNSUtil/APNSPayload.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@

import APNSUtil

extension RemoteNotificationElement {
typealias T = APNSPayload
}

struct APNSPayload: Decodable {
var msg: String?
var id: String?
let aps: APS?

struct APS: Decodable {
let sound: String?
let alert: Alert?
}

struct Alert: Decodable {
let body: String?
let title: String?
}
}

11 changes: 5 additions & 6 deletions Example/APNSUtil/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
APNSManager.shared.didFinishLaunching(withOptions: launchOptions, as: APNSPayload.self)
APNSManager.shared.didFinishLaunching(withOptions: launchOptions)
return true
}

// MARK: - Push Notification

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
APNSManager.shared.registerDeviceToken(deviceToken)
// TODO: write code to update devicetoken with your api server
// <<your function to register device token on your server>>(APNSInstance.shared.tokenString)
}
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
// TODO: write code to update devicetoken with your api server
}

// MARK: - Push Notification for iOS 9
Expand All @@ -36,18 +35,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
application.registerForRemoteNotifications()
}
func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) {
APNSManager.shared.didReceive(userInfo: userInfo, as: APNSPayload.self, isInactive: application.applicationState == .inactive)
APNSManager.shared.didReceive(userInfo: userInfo, isInactive: application.applicationState == .inactive)
}

// MARK: - Push Notification for iOS 10 or higher

@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
APNSManager.shared.didReceive(userInfo: notification.request.content.userInfo, as: APNSPayload.self, isInactive: false)
APNSManager.shared.didReceive(userInfo: notification.request.content.userInfo, isInactive: false)
}
@available(iOS 10.0, *)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
APNSManager.shared.didReceive(userInfo: response.notification.request.content.userInfo, as: APNSPayload.self, isInactive: true)
APNSManager.shared.didReceive(userInfo: response.notification.request.content.userInfo, isInactive: true)
}
}

9 changes: 7 additions & 2 deletions Example/APNSUtil/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,13 @@ class ViewController: UIViewController {
APNSManager.shared
.setTypes([.sound, .alert, .badge]) // setting user notification types
.register() // registering to use apns
.processing(self) { // processing received apns payload
let payload: APNSPayload = $0.payload() // your custom payload with generic
.subscribe(self) { // subscribe receiving apns payload
// your custom payload with generic
guard let payload: APNSPayload = $0.payload() else {
return
}

print("subscribe", $0.isInactive, $0.userInfo, payload)

if $0.isInactive {
// TODO: write code to present viewController on inactive
Expand Down
Loading

0 comments on commit 6aea0cf

Please sign in to comment.