IOS推送通知開發流程
最近開發IOS的推送通知,現把流程及遇到的問題整理一下:
一、證書的準備工作
1.在開發電腦MAC上申請證書
開啟鑰匙串訪問
選擇從證書頒發機構請求證書
這裡的郵箱賬號寫你自己的,常用名稱可以隨便寫
選擇儲存到磁碟
這裡最好新建一個資料夾 用來專門儲存這些檔案
2、在蘋果開發平臺生成開發證書或者生產證書
登入開發者賬號
選擇Development 點選'+'按鈕新增,我這裡有證書在試用中
Development下面有兩個選項 第一個是不帶Push Notification,如果你選的是這個那你後期需要使用推送的時候,修改即可;第二種是帶有Push Notification,所以我們選擇第二個即可;頁面下方點選continue
這裡要注意,如果你在上一步選擇的是沒有Push Notification的,就不會出現下面這個頁面;這個頁面是要求你選擇APP ID;
這裡一定要看清楚,不要選錯了,否則到時候推送通知推送不了,就是證書不匹配的問題
continue;
continue;
這裡選擇我們第一步到處的證書申請檔案:CertificateSigningRequest.certSigningRequest
continue;
最後點選下載,下載完成以後雙擊安裝該證書到鑰匙串當中,一樣的這個檔案不能亂放等下會用到的,同csr檔案放在一個目錄中最好;這裡需要在xcode 更換一下開發證書
3、匯出開發證書格式為p12個人資訊交換
展開開發證書,右擊選單選擇匯出
證書名字這裡儲存為Push,儲存位置與上面一樣;檔案格式一定要p12
點選儲存
這裡需要輸入證書的驗證密碼 這裡就設定簡單點:123456
到這裡我們需要的檔案都已經齊了,因為這個證書是新的證書,還需要修改一下描述檔案
在開發平臺 選擇Provisioning Profiles,點選 Development,選擇Edit;這裡你選擇的欄目必須是你專案中的APP ID,不要弄錯了
在選擇Certificates選擇剛剛新建的開發證書 注意APP ID
點選Generate
將該描述檔案下載下來,這個你可以放在其他位置,不用存放在上面的目錄;
開啟xcode開啟專案替換一下描述檔案即可
4.處理證書
進入終端,我這裡是獲取了root許可權了
進入上面的目錄
生成一個PushChatCert.pem檔案
openssl x509 -in aps_development.cer -inform der -out PushChatCert.pem
把私鑰Push.p12檔案轉化為.pem檔案:
openssl pkcs12 -nocerts -out PushChatKey.pem -in Push.p12
對生成的這兩個pem檔案再生成一個p12檔案,來把證書和私鑰整合到一個檔案裡:
openssl pkcs12 -export -in PushChatCert.pem -inkey PushChatKey.pem -certfile CertificateSigningRequest.certSigningRequest -name "aps_developer_identity" -out aps_developer_identity.p12
注意在這裡有可能會報錯,我這裡是有錯誤:
命令列提示unable to load certificates
刪除 -certfile CertificateSigningRequest.certSigningRequest,再次執行命令,需要你輸入之前在鑰匙串匯出p12檔案設定的密碼
最後成功生成檔案aps_developer_identity.p12
5.開啟xcode專案,新增Push Notification程式碼
在AppDelegate.m中新增一下程式碼:
- (BOOL)application:(UIApplication *)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
……
//訊息推送註冊
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 10.0) {
//iOS 10 later
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
//必須寫代理,不然無法監聽通知的接收與點選事件
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert) completionHandler:^(BOOL granted, NSError * _Nullable error) {
if (!error && granted) {
//使用者點選允許
NSLog(@"註冊成功");
}else{
//使用者點選不允許
NSLog(@"註冊失敗");
}
}];
// 可以通過 getNotificationSettingsWithCompletionHandler 獲取許可權設定
//之前註冊推送服務,使用者點選了同意還是不同意,以及使用者之後又做了怎樣的更改我們都無從得知,現在 apple 開放了這個 API,我們可以直接獲取到使用者的設定資訊了。注意UNNotificationSettings是隻讀物件哦,不能直接修改!
[center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
NSLog(@"========%@",settings);
}];
[application registerForRemoteNotifications];
}else if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0){
//iOS 8 - iOS 10系統
//UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound categories:nil];
//[application registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerUserNotificationSettings:[UIUserNotificationSettings
settingsForTypes:(UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge)
categories:nil]];
[application registerForRemoteNotifications];
}else{
//iOS 8.0系統以下
[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound];
}
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:@"" forKey:@"token"];
return YES;
}
- (void)application:(UIApplication *)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken {
NSString *token = [NSString stringWithFormat:@"%@", deviceToken];
//獲取終端裝置標識,這個標識需要通過介面傳送到伺服器端,伺服器端推送訊息到APNS時需要知道終端的標識,APNS通過註冊的終端標識找到終端裝置。
NSLog(@"My token is:%@", token);
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:token forKey:@"token"];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
// 處理推送訊息
NSLog(@"userinfo:%@",userInfo);
NSLog(@"收到推送訊息:%@",[[userInfo objectForKey:@"aps"] objectForKey:@"alert"]);
}
- (void)application:(UIApplication *)applicationdidFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(@"Registfail%@",error);
}
五、服務端新增javapns程式碼
將第3步生成的p12檔案傳送到服務端,我是在windows下測試的,
String certificatePath = "D:/aps_developer_identity.p12";
public class IOSBgNotice {
private static final Logger log = LogManager.getLogger(IOSBgNotice.class);
public static String certificatePath = "";
public static void notice(String deviceToken, String alert, int badge) {
/************************************************
測試的伺服器地址:gateway.sandbox.push.apple.com /埠2195
產品推送伺服器地址:gateway.push.apple.com / 2195
***************************************************/
//為了相容開發測試 所以推送需要傳送兩次
sendNotice(deviceToken, alert, badge, "gateway.sandbox.push.apple.com");//developer
sendNotice(deviceToken, alert, badge, "gateway.push.apple.com");//distribution
}
private static void sendNotice(String deviceToken, String alert, int badge, String host) {
try {
deviceToken = deviceToken.replaceAll(" ", "");
PushNotificationPayload payload = new PushNotificationPayload();
payload.addAlert(alert);//push的內容
payload.addBadge(badge);//圖示小紅圈的數值
payload.addSound("default");//鈴音
PushNotificationManager pushManager = new PushNotificationManager();
pushManager.addDevice("iPhone", deviceToken);
//Connect to APNs
int port = 2195;
String certificatePath = "D:/aps_developer_identity.p12";//匯出的證書
String certificatePassword = "123456";//此處注意匯出的證書密碼不能為空因為空密碼會報錯
// pushManager.initializeConnection(host,port, certificatePath,certificatePassword, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);
pushManager.initializeConnection(new AppleNotificationServerBasicImpl(
certificatePath, certificatePassword, "PKCS12", host, port));
List<PushedNotification> notifications = new ArrayList<PushedNotification>();
// 開始推送訊息
// Send Push
Device client = pushManager.getDevice("iPhone");
log.debug("推送訊息: " + client.getToken() + "\n" + payload.toString() + " ");
PushedNotification notification = pushManager.sendNotification(client, payload);
notifications.add(notification);
List<PushedNotification> failedNotification = PushedNotification
.findFailedNotifications(notifications);
List<PushedNotification> successfulNotification = PushedNotification
.findSuccessfulNotifications(notifications);
int failed = failedNotification.size();
int successful = successfulNotification.size();
log.debug("功數:" + successful);
log.debug("失敗數:" + failed);
pushManager.stopConnection();
pushManager.removeDevice("iPhone");
log.debug("訊息推送完畢");
} catch (Exception e) {
e.printStackTrace();
}
}
}
我這裡傳送兩次是因為程式碼打包以後,生成伺服器也會使用
6、測試
IOS需要在真機上測試,因為要獲取token
執行程式碼以後,手機上收到推送通知
最重要的就是p12檔案的製作,最開始在測試的時候一直報下面的異常:
javax.net.ssl.SSLException: Received fatal alert: internal_error
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.Alerts.getSSLException(Alerts.java:154)
at sun.security.ssl.SSLSocketImpl.recvAlert(SSLSocketImpl.java:1991)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1104)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1343)
at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:728)
at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:123)
at java.io.OutputStream.write(OutputStream.java:75)
at Push_1.main(Push_1.java:62)
出現這種情況,有兩種原因:
第一、你IOS打包時的環境和p12證書不匹配,有可能你匯出的時候不仔細,用的是生產的證書:
gateway.sandbox.push.apple.com
第二、Push Notification時,如果是開發環境就要使用開發蘋果的開發伺服器去推送;如果是生產環境就要使用生產伺服器去推送:
gateway.push.apple.com
一定要注意這兩點,必須要匹配才行
其他連結:
https://segmentfault.com/q/1010000000452323
https://blog.csdn.net/showhilllee/article/details/8631734