GCM이 IOS를 공식적으로 지원하기 시작하면서, 크로스 플랫폼 개발에 있어 푸시 서비스를 단일화 할 수 있게 되었다. 굳이 GCM을 사용할 필요가 있는가? 라고 하면, 가장 큰 장점은 3.0에 와서 푸시 서버를 따로 만들 필요가 없어진 점을 가장 큰 장점이라고 말 할 수 있다.
GCM을 적용하기 위한 사전 준비는, 기존의 APNS와 같다.
인증서를 만들고, 푸시를 사용할 앱을 애플 개발자 사이트에 등록하고 키체인을 만든다. 그 중 필요한 것은 p12파일이다.
해당 과정은 다른 곳에 많이 나와있으니 지금은 생략하고 나중에 시간이 날 때 천천히 정리할 예정이다.
우선 이 곳에서는 실제로 코드에 적용시키는 부분을 살펴보자.
GCM 사이트에서 iOS 등록을 진행하다보면 GoogleServices-Info.plist 파일을 받게 된다. 그 파일이 위치할 곳은 프로젝트의 루트이다.
그리고 cocoapods가 필요하다. 없는 경우 설치해주고
Set up your CocoaPods dependencies
Google Cloud Messaging uses CocoaPods to install and manage dependencies. Open a terminal window and navigate to the location of the Xcode project for your application. If you have not already created a Podfile for your application, create one now:
pod init
Open the Podfile created for your application and add the following:
pod 'Google/CloudMessaging'
Save the file and run:
pod install
This creates an .xcworkspace
file for your application. Use this file for all future development on your application.
프로젝트의 루트 위치로 이동해서 위 과정처럼 init -> 'Google/CloudMessaging' -> install 해주자. 그러면 GCM관련 파일들이 설치된다.
그리고 아래 코드를 기술한다.
ViewController.swift
{
class ViewController: UIViewController, UIWebViewDelegate { @IBOutlet weak var mainWebView: UIWebView!
var appJavascriptDelegate : AppJavascriptDelegate? var webViewDelegate : WebViewDelegate?
var request : NSURLRequest!
required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) }
override func viewDidAppear(animated: Bool) { //regist notification let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate NSNotificationCenter.defaultCenter().addObserver(self, selector: "updateRegistrationStatus:", name: appDelegate.registrationKey, object: nil) NSNotificationCenter.defaultCenter().addObserver(self, selector: "showReceivedMessage:", name: appDelegate.messageKey, object: nil) println("registed observer")
if let url = NSBundle.mainBundle().URLForResource("index", withExtension: "html", subdirectory: "web"){
} } func updateRegistrationStatus(notification: NSNotification) { // registrationProgressing.stopAnimating() if let info = notification.userInfo as? Dictionary<String,String> { if let error = info["error"] { NSLog("Error registering!", "error") // registeringLabel.text = "Error registering!" showAlert("Error registering with GCM", message: error) } else if let registrationToken = info["registrationToken"] { // registeringLabel.text = "Registered!" NSLog("Registered!", "error") let message = "Check the xcode debug console for the registration token that you " + " can use with the demo server to send notifications to your device" showAlert("Registration Successful!", message: message) } } else { println("Software failure. Guru meditation.") } }
func showReceivedMessage(notification: NSNotification) { println("showReceivedMessage \(notification.userInfo)") if let info = notification.userInfo as? Dictionary<String,AnyObject> { if let title = info["title"] as? String { if let body = info["body"] as? String { showNotification(title, message: body) } } if let aps = info["aps"] as? Dictionary<String, String> { // showAlert("Message received", message:"showAlert message") // showNotification("message received", message: "message inside") } } else { println("Software failure. Guru meditation.") } if let info = notification.userInfo as? Dictionary<String, AnyObject>{ println("run run") // var url = info["gcm.notification.url"] as! String // // setUrl(url) // loadUrl(request) // var badge = UIApplication.sharedApplication().applicationIconBadgeNumber; // if badge > 0 { // badge--; // } } // showNotification("message received", message:"message outside") } |
중요한 부분은 NSNotificationCenter에 옵저버를 등록하는 것이다. 이를 등록하면, 푸시 알람이 들어왔을 때 해당 옵저버 태그에 해당하는 이름으로 post하면 그 함수가 실행된다. 해당 함수는 ViewController.swift에 기술하면 된다.
AppDelegate.swift
// AppDelegate.swift // webviewtest // // Created by Myungwoo Park on 2015. 8. 18.. // Copyright (c) 2015년 ygkwon. All rights reserved. // import UIKit @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, GGLInstanceIDDelegate, GCMReceiverDelegate { var window: UIWindow?
var connectedToGCM = false var subscribedToTopic = false var gcmSenderID: String? var registrationToken: String? var registrationOptions = [String: AnyObject]()
let registrationKey = "onRegistrationCompleted" let messageKey = "onMessageReceived" let subscriptionTopic = "/topics/global"
// [START register_for_remote_notifications] func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool { // [START_EXCLUDE] // Configure the Google context: parses the GoogleService-Info.plist, and initializes // the services that have entries in the file var configureError:NSError? GGLContext.sharedInstance().configureWithError(&configureError) assert(configureError == nil, "Error configuring Google services: \(configureError)") gcmSenderID = GGLContext.sharedInstance().configuration.gcmSenderID NSLog("sender id : " + gcmSenderID!, "sender") // [END_EXCLUDE] // Register for remote notifications var types: UIUserNotificationType = UIUserNotificationType.Badge | UIUserNotificationType.Alert | UIUserNotificationType.Sound var settings: UIUserNotificationSettings = UIUserNotificationSettings( forTypes: types, categories: nil ) application.registerUserNotificationSettings( settings ) application.registerForRemoteNotifications() // [END register_for_remote_notifications] // [START start_gcm_service] var gcmConfig = GCMConfig.defaultConfig() gcmConfig.receiverDelegate = self GCMService.sharedInstance().startWithConfig(gcmConfig) // [END start_gcm_service]
return true }
func subscribeToTopic() { // If the app has a registration token and is connected to GCM, proceed to subscribe to the // topic if(registrationToken != nil && connectedToGCM) { GCMPubSub.sharedInstance().subscribeWithToken(self.registrationToken, topic: subscriptionTopic, options: nil, handler: {(NSError error) -> Void in if (error != nil) { // Treat the "already subscribed" error more gently if error.code == 3001 { println("Already subscribed to \(self.subscriptionTopic)") } else { println("Subscription failed: \(error.localizedDescription)"); } } else { self.subscribedToTopic = true; NSLog("Subscribed to \(self.subscriptionTopic)"); } }) } }
// [START connect_gcm_service] func applicationDidBecomeActive( application: UIApplication) { // Connect to the GCM server to receive non-APNS notifications GCMService.sharedInstance().connectWithHandler({ (NSError error) -> Void in if error != nil { println("Could not connect to GCM: \(error.localizedDescription)") } else { self.connectedToGCM = true println("Connected to GCM") // [START_EXCLUDE] self.subscribeToTopic() // [END_EXCLUDE] } })
NSLog("foreground") } // [END connect_gcm_service]
// [START disconnect_gcm_service] func applicationDidEnterBackground(application: UIApplication) { GCMService.sharedInstance().disconnect() // [START_EXCLUDE] self.connectedToGCM = false // [END_EXCLUDE] } // [END disconnect_gcm_service]
// [START receive_apns_token] func application( application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: NSData ) { // [END receive_apns_token] // [START get_gcm_reg_token] // Create a config and set a delegate that implements the GGLInstaceIDDelegate protocol. NSLog("START get_gcm_reg_token", "getToken") var instanceIDConfig = GGLInstanceIDConfig.defaultConfig() instanceIDConfig.delegate = self // Start the GGLInstanceID shared instance with that config and request a registration // token to enable reception of notifications GGLInstanceID.sharedInstance().startWithConfig(instanceIDConfig) registrationOptions = [kGGLInstanceIDRegisterAPNSOption:deviceToken, kGGLInstanceIDAPNSServerTypeSandboxOption:true] GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID, scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler) // [END get_gcm_reg_token] }
// [START receive_apns_token_error] func application( application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: NSError ) { println("(in error)Registration for remote notification failed with error: \(error.localizedDescription)") // [END receive_apns_token_error] let userInfo = ["error": error.localizedDescription] NSNotificationCenter.defaultCenter().postNotificationName( registrationKey, object: nil, userInfo: userInfo) }
// [START ack_message_reception] func application( application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject]) { println("Notification received1: \(userInfo)") // This works only if the app started the GCM service GCMService.sharedInstance().appDidReceiveMessage(userInfo); // Handle the received message // [START_EXCLUDE] NSNotificationCenter.defaultCenter().postNotificationName(messageKey, object: nil, userInfo: userInfo) // [END_EXCLUDE] }
func application( application: UIApplication, didReceiveRemoteNotification userInfo: [NSObject : AnyObject], fetchCompletionHandler handler: (UIBackgroundFetchResult) -> Void) { println("Notification received2: \(userInfo)") // This works only if the app started the GCM service GCMService.sharedInstance().appDidReceiveMessage(userInfo); // Handle the received message // Invoke the completion handler passing the appropriate UIBackgroundFetchResult value // [START_EXCLUDE] NSNotificationCenter.defaultCenter().postNotificationName(messageKey, object: nil, userInfo: userInfo) handler(UIBackgroundFetchResult.NoData); // [END_EXCLUDE] }
func registrationHandler(registrationToken: String!, error: NSError!) { if (registrationToken != nil) { self.registrationToken = registrationToken println("Registration Token: \(registrationToken)") self.subscribeToTopic() let userInfo = ["registrationToken": registrationToken] NSNotificationCenter.defaultCenter().postNotificationName( self.registrationKey, object: nil, userInfo: userInfo) } else { println("Registration to GCM failed with error: \(error.localizedDescription)") let userInfo = ["error": error.localizedDescription] NSNotificationCenter.defaultCenter().postNotificationName( self.registrationKey, object: nil, userInfo: userInfo) } }
// [START on_token_refresh] func onTokenRefresh() { // A rotation of the registration tokens is happening, so the app needs to request a new token. println("The GCM registration token needs to be changed.") GGLInstanceID.sharedInstance().tokenWithAuthorizedEntity(gcmSenderID, scope: kGGLInstanceIDScopeGCM, options: registrationOptions, handler: registrationHandler) } // [END on_token_refresh]
// [START upstream_callbacks] func willSendDataMessageWithID(messageID: String!, error: NSError!) { if (error != nil) { // Failed to send the message. } else { // Will send message, you can save the messageID to track the message } }
func didSendDataMessageWithID(messageID: String!) { // Did successfully send message identified by messageID } // [END upstream_callbacks]
func didDeleteMessagesOnServer() { // Some messages sent to this device were deleted on the GCM server before reception, likely // because the TTL expired. The client should notify the app server of this, so that the app // server can resend those messages. } } |
중간에
NSNotificationCenter.defaultCenter().postNotificationName(messageKey, object: nil,
userInfo: userInfo)
구문을 볼 수 있다. 이런 식으로 포스트 하는 것으로 함수를 연결시킨다. 연결된 함수에서 notification이나 alert 등을 수행하면 된다.
댓글