Issue #377
OneSignal is an alternative for Parse for push notifications but the sdk has many extra stuff and assumptions and lots of swizzling.
We can just use Rest to make API calls. From https://github.com/onmyway133/Dust
Every official push notification SDK can do many things
- Register device token. This is crucial for the notification to get from your backend -> APNS -> device
- Manage player id, user id, arn, …This is used to associate with device token
- Manager tag, topic, subscription, segments, …This is used to group a set of device tokens
- Do swizzling, update your application badge number, change your user notification settings, … without your knowing about that
- Some other fancy stuffs
- Dust does only one thing, which is push notification handling. The rest is under your control
OneSignal
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
OneSignal.appID = ""
}
func application(application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData) {
OneSignal.handleDeviceToken(deviceToken)
}
Here is the implementation
import UIKit
struct Utils {
static func parse(deviceToken data: NSData) -> String {
let buffer = UnsafePointer<CChar>(data.bytes)
var string = ""
for i in 0..<data.length {
string += String(format: "%02.2hhx", arguments: [buffer[i]])
}
return string
}
static func deviceModel() -> String {
var systemInfo = utsname()
uname(&systemInfo)
var v = systemInfo.machine
var deviceModel = ""
let _ = withUnsafePointer(&v) {
deviceModel = String(UTF8String: UnsafePointer($0)) ?? ""
}
return deviceModel
}
static func systemVersion() -> String {
let version = NSProcessInfo.processInfo().operatingSystemVersion
return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
}
static func language() -> String {
return NSLocale.preferredLanguages().first!
}
static func timezone() -> Int {
return NSTimeZone.localTimeZone().secondsFromGMT
}
static func soundFiles() -> [String] {
guard let resourcePath = NSBundle.mainBundle().resourcePath
else { return [] }
let files = try? NSFileManager.defaultManager()
.contentsOfDirectoryAtPath(resourcePath)
.filter {
return $0.hasSuffix(".wav") || $0.hasSuffix(".mp3")
}
return files ?? []
}
static func versionNumber() -> String? {
return NSBundle.mainBundle().infoDictionary?["CFBundleShortVersionString"] as? String
}
static func buildNumber() -> String? {
return NSBundle.mainBundle().infoDictionary?["CFBundleVersionString"] as? String
}
static func netType() -> Int {
// Reachability
return 0
}
}
import Foundation
public struct UserDefaults {
struct Key {
static let playerID: String = "Dust-OneSignal-Player-ID-Key"
static let deviceToken: String = "Dust-OneSignal-Device-Token-Key"
static let subscribed: String = "Dust-OneSignal-Disable-Subscribed-Key"
}
public static var playerID: String? {
get {
return NSUserDefaults.standardUserDefaults().stringForKey(Key.playerID)
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: Key.playerID)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
public static var deviceToken: String? {
get {
return NSUserDefaults.standardUserDefaults().stringForKey(Key.deviceToken)
}
set {
NSUserDefaults.standardUserDefaults().setObject(newValue, forKey: Key.deviceToken)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
public static var subscribed: Bool {
get {
return NSUserDefaults.standardUserDefaults().boolForKey(Key.subscribed)
}
set {
NSUserDefaults.standardUserDefaults().setBool(newValue, forKey: Key.subscribed)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
}
import Foundation
import Alamofire
public struct OneSignal {
static var appID: String = ""
static let version = "020115"
static let baseURL = NSURL(string: "https://onesignal.com/api/v1")!
enum NotificationType: Int {
case subscribed = 7
case unsubscribed = -2
static func value() -> Int {
return UserDefaults.subscribed
? NotificationType.subscribed.rawValue : NotificationType.unsubscribed.rawValue
}
}
enum Provisioning: Int {
case development = 1
}
public static func setup(appID appID: String) {
NSUserDefaults.standardUserDefaults().registerDefaults([
UserDefaults.Key.subscribed: true
])
OneSignal.appID = appID
}
public static func registerOrUpdateSession(completion: ((String?) -> Void)? = nil) {
guard let bundleID = NSBundle.mainBundle().bundleIdentifier,
let deviceToken = UserDefaults.deviceToken
else {
return
}
var params: [String: AnyObject] = [
"app_id" : appID,
"device_model" : Utils.deviceModel(),
"device_os" : Utils.systemVersion(),
"language" : Utils.language(),
"timezone" : NSNumber(integer: Utils.timezone()),
"device_type" : NSNumber(integer : 0),
"sounds" : Utils.soundFiles(),
"sdk" : version,
"identifier" : deviceToken,
"net_type" : NSNumber(integer: Utils.netType()),
"rooted": NSNumber(bool: false),
"as_id": "OptedOut",
"sdk_type": "native",
"ios_bundle": bundleID,
"game_version": Utils.versionNumber() ?? "",
"notification_types": NotificationType.value(),
]
#if DEBUG
params["test_type"] = Provisioning.development.rawValue
#endif
let url: NSURL
if let playerID = UserDefaults.playerID {
url = baseURL.URLByAppendingPathComponent("players/\(playerID)/on_session")
} else {
url = baseURL.URLByAppendingPathComponent("players")
}
Alamofire
.request(.POST, url, parameters: params)
.responseJSON { response in
guard let json = response.result.value as? [String: AnyObject]
else {
completion?(nil)
return
}
if let id = json["id"] as? String {
UserDefaults.playerID = id
completion?(id)
} else if let value = json["success"] as? Int,
playerID = UserDefaults.playerID where value == 1 {
completion?(playerID)
} else {
completion?(nil)
}
}
}
public static func handle(deviceToken data: NSData) {
UserDefaults.deviceToken = Utils.parse(deviceToken: data)
registerOrUpdateSession()
}
public static func update(subscription subscribed: Bool) {
guard let playerID = UserDefaults.playerID else { return }
UserDefaults.subscribed = subscribed
let url = baseURL.URLByAppendingPathComponent("players/\(playerID)")
let params: [String: AnyObject] = [
"app_id": appID,
"notification_types": NotificationType.value()
]
Alamofire
.request(.PUT, url, parameters: params)
.responseJSON { response in
print(response)
}
}
public static func update(badge count: Int) {
guard let playerID = UserDefaults.playerID else { return }
let url = baseURL.URLByAppendingPathComponent("players/\(playerID)")
let params: [String: AnyObject] = [
"app_id": appID,
"badge_count": count
]
Alamofire
.request(.PUT, url, parameters: params)
.responseJSON { response in
}
}
public static func getPlayerID(completion: String -> Void) {
if let playerID = UserDefaults.playerID {
completion(playerID)
return
}
registerOrUpdateSession { playerID in
if let playerID = playerID {
completion(playerID)
}
}
}
}