首先说一点,我这里只是简单实现整个推送过程,还暂未对推送后的通知做处理,比如3Dtouch触发的快捷操作,如果你需要看这方面的资料,这里推荐几个参考
1.https://www.jianshu.com/p/81c6bd16c7ac
2.http://www.cocoachina.com/ios/20180719/24235.html
好了,整个实现流程就不多说了,简单总结一下
1.苹果开发者后台创建推送证书(建议开发环境和正式环境的证书都创建好)
2.创建好证书后,到xcode中配置,并在AppDelegate中注册设备token,发送给后台,以及处理通知
3.php使用证书,跟Apns建立连接,发送推送消息
就上面的3步操作,但是有繁琐的步骤,还有一些细节要注意。
由于步骤过于繁琐,某些步骤就不一一贴上来了,这里安利一下我参考的这几位大佬的教程,亲测是能成功的,只是可能会遇到些坑,我会总结一下,供大家参考。
1.证书创建和xcode配置:https://www.jianshu.com/p/adfce0921c09
2..p12证书合成.pem证书,以及php后台代码:https://www.jianshu.com/p/38d9fed94ee0
3. AppDelegate完整代码:https://www.jianshu.com/p/c58f8322a278
总结:
1.证书必须创建正确。自己写后台的发送程序,需要用到.pem文件。而第三方的推送服务器需要的是p12文件
2.xcode要在打开相应的开关
3.你测试获取的deviceToken是需要你自己写代码发给你后台的。当然自己测试的话,只需要直接复制就好了。
4.当你原生app已经能够获取deviceToken,你已经成功一半了,只需要在后端测试能否收到推送通知就好了。我用的是PHP,网上大部分是16年的教程,但是还是管用,下面附上我的PHP后段代码,亲测能成功,注意要在命令终端运行php并不可以直接运行,也可以写一个方法另一php调用这个方法,此时能够在浏览器中成功运行。
"劲爆活动来袭" , "body" =>'邀请好友,即可获得邀请大奖。累计奖金第一名还可获得额外实物奖励,速来吧');
//app右上角显示的数字
$badge = 1;
//推送消息到手机时的提示音
$sound = 'default';
//建设的通知有效载荷(即通知包含的一些信息)
$body = array();
$body['aps'] = array('alert' => $message,'badge'=>$badge,'sound'=>$sound);
//自定义参数
$body['userInfo'] = array('type' => '2');
//把数组数据转换为json数据
$payload = json_encode($notification);
echo strlen($payload);
//这里可批量发送deviceToken,将多个deviceToken放在数组中循环即可
$devicetoken =['8e2ac24b85b32f14bd5af82ebb80edebd0671195abb876b00044ad08ad3391c4'];
$i = 1;
//推送开始
foreach ($devicetoken as $v){
print_r('开始推送' .'
');
$msg = chr(0) . pack("n",32) . pack('H*', str_replace(' ', '', $v)) . pack("n",strlen($payload)) . $payload;
if( !fwrite($fp, $msg) || checkAppleErrorResponse($fp) ){
//如果失败,则进行重发,连续3次还是失败,则重新建立连接,并对下一个devicetoken进行推送
$times = 1;
while($times <= 3){
if(!fwrite($fp, $msg) || checkAppleErrorResponse($fp)){
$times ++ ;
}else{
$fp = create_apns_link();
break;
}
}
}
print_r('已推送' . $v .'的通知
');
//每隔100次重新和APNS服务器建立连接.
if( $i%100 == 0 ){
$fp = create_apns_link();
}
$i++;
}
usleep(500000);
if(checkAppleErrorResponse($fp)){
print_r('推送失败
');
}else{
print_r('推送完成
');
}
fclose($fp);
function create_apns_link(){
$passphrase = '123456';
//这是合成的.pem证书,跟php文件放在同一个文件夹
$filename = dirname(__FILE__) .'/'.'apns_production.pem';
//测试环境下的apns服务器,用tls连接方式
$link = 'tls://gateway.push.apple.com:2195';
//正式环境下的apns服务器
//$link = 'tls://gateway.sandbox.push.apple.com:2195';
$scc = stream_context_create();
//这里并不需要改为tls,保持ssl就好了
stream_context_set_option($scc, 'ssl', 'local_cert', $filename);
stream_context_set_option($scc, 'ssl', 'passphrase', $passphrase);
$fp = stream_socket_client($link, $err,$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $scc);
$i = 0;
while (gettype($fp) != 'resource'){
print_r('重连Apns'.'
');
if($i < 3){
usleep(500000);
$fp = stream_socket_client($link, $err,$errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $scc);
}else{
break;
}
}
stream_set_blocking ($fp, 0);
if($fp){
echo '成功连接到Apns,等待推送' . '
';
}
return $fp;
}
function checkAppleErrorResponse($fp) {
//byte1=always 8, byte2=StatusCode, bytes3,4,5,6=identifier(rowID).
// Should return nothing if OK.
//NOTE: Make sure you set stream_set_blocking($fp, 0) or else fread will pause your script and wait
// forever when there is no response to be sent.
$apple_error_response = fread($fp, 6);
if ($apple_error_response) {
// unpack the error response (first byte 'command" should always be 8)
$error_response = unpack('Ccommand/Cstatus_code/Nidentifier', $apple_error_response);
if ($error_response['status_code'] == '0') {
$error_response['status_code'] = '0-没遇到任何错误';
} else if ($error_response['status_code'] == '1') {
$error_response['status_code'] = '1-处理错误';
} else if ($error_response['status_code'] == '2') {
$error_response['status_code'] = '2-缺少设备token';
} else if ($error_response['status_code'] == '3') {
$error_response['status_code'] = '3-缺少主题';
} else if ($error_response['status_code'] == '4') {
$error_response['status_code'] = '4-缺少荷载';
} else if ($error_response['status_code'] == '5') {
$error_response['status_code'] = '5-无效的设备token';
} else if ($error_response['status_code'] == '6') {
$error_response['status_code'] = '6-无效的主题size';
} else if ($error_response['status_code'] == '7') {
$error_response['status_code'] = '7-无效的荷载size';
} else if ($error_response['status_code'] == '8') {
$error_response['status_code'] = '8-无效的token';
} else if ($error_response['status_code'] == '255') {
$error_response['status_code'] = '255-未知错误';
} else {
$error_response['status_code'] = $error_response['status_code'].'-错误不在错误列表内';
}
echo '
+ + + + + + 错误 返回命令:' . $error_response['command'] . ' 识别码:' . $error_response['identifier'] . ' 状态码:' . $error_response['status_code'] . '
';
echo '标识符是导致问题的数据库中的rowID(索引),Apple将断开您与服务器的连接。 要继续发送推送通知,只需从此标识符后面的下一个rowID开始。
';
return true;
}
return false;
}
?>
5.测试正式环境的推送
首先将php的测试发送通知的apns服务器和证书换成正式环境的。
然后最重要的是获取正式环境下的deviceToken。这里有两个解决方案,第一种就是代码里写好将获取到的deviceToken发送到服务器的数据库,存起来。然后在appstore上线了,在进行测试。这个时间成本太高了。第二种就是生成ipa文件,直接安装在手机上,将deviceToken存在本地文件,然后去找这个文件,读取deviceToken信息。
我这里详细说一下第二种方案:参考教程:https://www.jianshu.com/p/76e23be72b3b
1.首先修改代码,在获取deviceToken的地方把deviceToken存在本地文件。代码如下:
打个广告:我写的另一篇文章,收集了一些ios常用开发技巧:传送门
NSArray *pathArr=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *strPath=[pathArr lastObject];
NSString *strFinalPath=[NSString stringWithFormat:@"%@/myfile.txt",strPath];
[deviceToken writeToFile:strFinalPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
2.打包生成ipa文件,首先要在apple的developer后台创建这个应用的ad hoc证书,可参考上面的教程
生成之后,打包生成ipa操作如下:
xcode->product->archive,打开窗口后distribute app,选择ad hoc
然后next->next->
这里要注意hoc是我在apple的developer中创建的那个ad hoc证书。这里要用的。然后next->
export导出,就会在导出的地方有个文件夹,里面就有.ipa文件,如下:
3.真机安装ipa文件。xcode要链接好手机,然后打开设备窗口,操作如下:xcode->windows->devices and simulators,这样就会打开如下窗口:
点击+号,选择刚刚导出的ipa文件,就会自动安装好了,如下图
安装好之后,你就正常的使用你的app,你一打开app,他应该就会存下deviceToken了,然后你就会到上图这个窗口,选中这个app,+号旁边的设置icon就会亮起,点击选择download选项:
download之后就会在你download到的那个文件夹中出现一个app data文件:
右键->显示包内容。就能打开里面的文件目录,在document文件夹下应该有个txt文件,打开里面就是正式环境下的deviceToken,如下图:
5. 这时候你就可以拿着这个deviceToken去php后台发送测试了。之前我分享的那个教程中的smartpush那个测试插件挺好用的,也能用来测试,xcode打开就能跑起来,非常方便。