IOS菜鸟初学第九篇:实现远程推送通知(证书创建,证书使用,ios代码,php后台发送推送)

首先说一点,我这里只是简单实现整个推送过程,还暂未对推送后的通知做处理,比如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调用这个方法,此时能够在浏览器中成功运行。

<?php


		$fp =create_apns_link();
		$message = array("title" =>"劲爆活动来袭" , "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('开始推送' .'<br/>');
			$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 .'的通知<br/>');
			//每隔100次重新和APNS服务器建立连接.
			if( $i%100 == 0 ){
				$fp = create_apns_link();
			}
			$i++;
		}

		usleep(500000); 
		if(checkAppleErrorResponse($fp)){
			print_r('推送失败<br/>');
		}else{
			print_r('推送完成<br/>');
		}
		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'.'<br/>');
			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,等待推送' . '<br/>';
		}
		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 '<br><b>+ + + + + + 错误</b> 返回命令:<b>' . $error_response['command'] . '</b>   识别码:<b>' . $error_response['identifier'] . '</b>   状态码:<b>' . $error_response['status_code'] . '</b><br>';
		    echo '标识符是导致问题的数据库中的rowID(索引),Apple将断开您与服务器的连接。 要继续发送推送通知,只需从此标识符后面的下一个rowID开始。<br>';
		    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打开就能跑起来,非常方便。

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是使用Firebase_Messaging接收推送通知的完整Flutter iOS代码: ```dart import 'package:firebase_core/firebase_core.dart'; import 'package:firebase_messaging/firebase_messaging.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Firebase.initializeApp(); runApp(MyApp()); } class MyApp extends StatefulWidget { @override _MyAppState createState() => _MyAppState(); } class _MyAppState extends State<MyApp> { final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance; @override void initState() { super.initState(); _firebaseMessaging.requestPermission(); _firebaseMessaging.configure( onMessage: (Map<String, dynamic> message) async { print("onMessage: $message"); showDialog( context: context, builder: (context) { return AlertDialog( content: ListTile( title: Text(message['notification']['title']), subtitle: Text(message['notification']['body']), ), actions: <Widget>[ FlatButton( child: Text('Ok'), onPressed: () { Navigator.of(context).pop(); }, ), ], ); }, ); }, onResume: (Map<String, dynamic> message) async { print("onResume: $message"); }, onLaunch: (Map<String, dynamic> message) async { print("onLaunch: $message"); }, ); } @override Widget build(BuildContext context) { return MaterialApp( title: 'Flutter Firebase Messaging', home: Scaffold( appBar: AppBar( title: Text('Flutter Firebase Messaging'), ), body: Center( child: Text('Welcome to Flutter Firebase Messaging'), ), ), ); } } ``` 在这个例子中,我们首先使用Firebase.initializeApp()初始化Firebase应用程序,然后创建FirebaseMessaging实例并调用requestPermission()请求权限。接下来,我们使用configure()方法配置FirebaseMessaging以处理不同的消息事件。当应用程序处于前台时,onMessage()方法被调用,我们在这里创建一个AlertDialog来显示推送通知的标题和正文。当应用程序处于后台或未运行时,onResume()和onLaunch()方法被调用,我们可以在这里处理推送通知。 希望这个例子能够帮助你使用Firebase_Messaging接收推送通知

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值