2018-02-07 12:43:37 weixin_34125592 阅读数 0

写在前面

  • Cupertino:苹果电脑的全球总公司所在地
  • 作者:Maximiliano Firtman
  • 原文地址:PWAs are coming to iOS 11.3: Cupertino, we have a problem
  • 说明:本人水平有限,在翻译过程中难免有理解翻译不准确的地方,为避免错误引导大家,希望能够指,向大家传递正确的观点和知识。

iOS 11.3以及macOS 10.12.4将包含service Worker-一个强大的规范,允许后台脚本支持离线web应用程序。iOS 11.3还会在将Web应用程序添加到主屏幕时咨询Web应用程序清单。 -@rmondello

昨天看到Ricky Mondello的推特以及Safari 11.1 beta测试版实现了Web App Manifest和Service Workers,这意味着开发多平台的PWA应用成为了现实,现在让我们回到现实世界,看看我们已经有了什么。

测试并不容易

在iOS上测试这些新的功能并不容易,因为Safari上的开发者工具并不允许你查看Service Workers进程,而且客户端API允许我们用它的客户端与服务人员进行通信的消息通道并不在那里。但是,我设法玩了几个小时,尽管存在一些Bug,但是我相信在最终的版本上WebKit团队可以解决这些问题。我想重点关注iOS PWAs与Android的显著差异。

如果你发布了一个PWA应用或者即将发布,你必须注意用户体验以及有可能在iOS上发生的一些问题

复制代码

十八个月以前(竟然已经一年半了)我宣称:“不要在PWA应用中不负责任的使用iOS元标签”。Twitter和Flipkart等几家公司在这些问题出现的时候注意到了这些问题,并删除了iOS元标签,或者解决了这些问题。
当时的问题是,一些公司没有进行测试以及分析安卓PWA应用与iOS之间的区别就选择通过苹果元标签来支持iOS。
很抱歉,现在出现的大多数问题都和我十八个月之前说的一样,不过有一个地方还是很不同的:现在你不需要加入到iOS当中去。iOS将支持Web App Manifest,所以你的PWA将会自动变成iOS的PWA应用。但苹果并没有模仿安卓系统的行为,这意味着:Cupertino,我们有一个问题。

只能在线上安装图标

如果你在主屏幕上安装你的PWA应用,虽然Service Workers就在这里,但一直处于注册状态却没有运行(SW不会吧web应用当成客户端一样处理)。所以不要期望在第一个测试版本上获得要离线体验。不过我相信这是一个BUG,并且在以后的版本中会解决掉。

PWA:克隆攻击

Service Worker API可以在Safari、Web View上使用HTTPS通信(也就是Chrome、Firefox和Facebook的内置浏览器)上使用,应用程序可以添加到主屏幕(Web.app)和Safari视图控制器(比如当你在iOS的推特上点击链接时)。
这听起来很棒,是吗?好吧,还有一个很大的问题:在Safari上、在每个应用的web视图上以及主屏幕的web应用上,引擎是不支持分享Service Workers和缓存存储的,这就意味着用户可能会在同一设备上以多个PWA文件的副本结束。
你也许会想:嘿,Max,同样的事情也会发生在安卓以及其它不同的浏览器上(Chrome, Firefox, Opera, Samsung Internet, UC Web)。好吧,你是对的,但这是一个不一样的用例。在安卓上,操作系统的web views上不支持Service Workers,首屏上的PWA应用都共享拥有这个应用图标的浏览器的SW和缓存。同样的,在同一设备上使用不同的浏览器载入同一web应用似乎并不是一个典型的用例。
现在,假设你是一个iOS用户,并且使用一个PWA应用,比如Twitter Lite。当你要使用它时,你打开你的浏览器,像iOS上的Chrome或者 Firefox。你获得了这个应用的副本。假定你把它添加到主屏幕,这就生产了第二个应用副本。因为在iOS上你无法修改默认的浏览器,所以当你在邮箱中收到一个到推特的链接,你点击之后,Safari就会打开,在你的设备上就生成了第三个应用副本。这就结束了?还没有。如果你在其他应用程序中使用Facebook或一些报纸应用程序,你可以在app内只浏览器中体验,当你点击一个链接到推特或者推特账户,这就生成了另一个应用副本。幸运的是,Safari的视图控制器似乎与Safari共享SW和cache。


所以,一个iOS用户在关闭PWA应用时,存在四个或者更多的副本(我们讨论的是service worker和缓存文件,而不是图标)。

web应用的Manifest应用

当您的HTML中有一个manifest时,Safari将使用它而不是旧的非标准的苹果移动元标签。非常棒,然而,知道beat1版本,和安卓相比你也会有一些意想不到的行为发生。
我们来讨论一下那些特性被忽略了(但现在只是beta1版本,我不确定以后哪些特性会有或者没有):

  • APP 名字:只能为图标使用很短的名字
  • 主题颜色和背景颜色:没有启动屏幕,没有彩色状态栏。
  • 图标:我昨天看到几个PWA应用的作者很高兴他们的解决方案在安装之后很好的起作用了,但这还不是完全正确的。大多数PWA应用的图标都是通过<link rel=”apple-touch-icon”>设定来获取的,而不是来自应用的Manifest。我猜苹果在未来的版本中会解决这个问题,我希望能够提供120x120和180x180两个尺寸。
  • 方向:并没有提供锁定方向的方法。
  • 展示:全屏:它是独立的(尽管它现在被标记为弃用,但你仍然可以使用黑色半透明状态栏来获得全屏幕。)
  • 展示:小屏,它是浏览器的标准快捷方式。
    另外,我很惊讶start_url会被授权,这是网页从浏览器到主屏幕的一个巨大变化。现在单页面应用在iOS上添加到主页面,为了使应用保持最新状态使用推送方式,并不需要其他的奇怪方法就可以做到。然而,请注意,manifest中的URL将在对话框中可见。
    另外,显示模式的CSS媒体查询和我们预期的实现方式相同。

独立模式下的Scope和links

Manifest的作用域使得当你使用<a>标签创建链接时会变得不一样。当你点击链接时,它应该在PWA应用中打开,还是去浏览器中打开呢?
Android浏览器通常在PWA上下文的范围内打开url,或者在浏览器或自定义选项卡中打开其他链接。如果你没有特别指定web应用Manifest的作用范围,安卓通常会把manifest的文件夹作为默认范围,这也是通常能预想到的操作。
如果没有特别指定,Safari不会定义一个默认的范围,那么然后你的PWA中的每个链接都将在你的应用程序的iOS窗口中打开。问题在哪里?它是iOS,它没有返回按钮也没有返回操作,所以用户也能就会被限制在你所链接的一个外部网站上而无法回到应用中。如果你指定了范围,那么每个应用效果都会和安卓上预期的一样,范围之外的链接将会在Safari中打开,并带有一个返回按钮(状态栏中小的那个),能够回到你的PWA应用中去。

每次在屏幕上出现时,iOS都会重新加载PWAs。

不幸的是,一个首屏上使用web应用的bug(一个特性?)仍然存在。每次你离开PWA时,你将会丢失所有的状态,当用户再次进入时,PWA应用将从头开始加载。
对于性能、电池的使用以及用户体验,这种行为都是一个非常严重的问题。如果你访问一个外部的站点,返回按钮回到应用时总是从头开始载入,这将花费很多时间,这并不是用户所期望的(你可以使用本地存储来改掉这个问题,但你知道的,这并不是一个好的方法)。
而且,这对于一个需要双重验证的应用来说是一个很大的问题,比如推特。如果你需要去另一个应用获取验证码或者打开一条消息或者邮件,你将离开PWA应用。当你回来要粘贴这些信息时,你发现页面不见了,你需要重新登录,然后发现验证码失效了。我在使用推特时就发生了这个问题!这就意味着iOS上的推特应用对我来说完全没有用处。

缺少一些功能

不幸的是,并不支持Web应用程序轮播图或Manifest 规范的事件,比如 appinstalled(译者不知道这是什么意思),所以你需要想别的方式来跟踪它。
和预想的一样,并不支持web消息推送,即使是在今天,在死刑的边缘上也有了推送通知。另外,也没有后台同步或者web共享API。从iOS的角度来看,这真是一个很令人羞耻的事情,毕竟使用原生的SocialKit框架应该很容易实现的。

交互问题以及bugs

正如我在上一篇文章中提到的,iOS有一些不同之处,比如:

  • 在iOS中,你没有一个物理或逻辑上的后退按钮或手势。你总是需要在UI界面上自己提供一个。这意味着您不能使用OAuth登录机制作为顶级文档(没有办法返回)。这里有个例子,在推特的PWA应用中我点击了一个词,然后我没有办法返回或者取消刚才的操作。甚至,如果我进入到邮箱中去,然后就无法再回到登录页了。
  • 在iOS上,你无法使用透明图标,而且在安卓上,大多数PWA应用使用圆形图标,在iOS上使用同样的方案,透明部分的颜色就会变成黑色,这并不是一个好的选择。
  • 已经五年了,iOS中状态栏的bug仍然存在。如果你不特别声明一些东西,用户状态栏中就会没有时钟,没有电池,没有wifi图标。现在的方法仍然是使用状态栏 meta标签,现在再次接受白色,黑色和黑色半透明,以获得全屏体验(在iphone x上特别注意,你也许想要在CSS中使用心的notch-helpers)。因为一些未知的原因,黑色和黑色半透明现在被标记为弃用,并在未来被移除。我猜,manifest 上的主题颜色会被优先考虑,但为什么会弃用黑色而不是白色呢?

我们仍然有时间去改变

让然还有时间让苹果去做一些改变来使我们的生活更美好。同时,我们也有时间去检查完善我们的PWA应用,比如:

  • 你的图标:添加iOS的大小和不透明度。
  • 在有<a>链接的交互界面上加一个返回按钮
  • web应用中作用范围的使用
  • 如果你要求用户从你的应用程序中跳出来,并且因为重新加载而回来,该怎么办?
  • 如何推动app的安装(iOS更新提示)
  • 你如何追踪PWA的安装?

你还发现别的了吗?请记住在Twitter上关注我,因为我将经常更新信息,并在未来测试新版本。

2013-09-28 17:00:46 u011168635 阅读数 0

  今日苹果推送了iOS 7.0.2正式版固件,对锁屏密码漏洞和键盘输入等问题进行修复。iOS 7系统受到的重视如此之高,以致成为了历史上升级率最快的iOS系统。不少iOS用户都为iOS 7所着迷,正摩拳擦掌准备升级最新的iOS 7.0.2系统。本文将以iPad设备升级iOS 7.0.2系统为例,分享下iPad如何升级iOS 7.0.2系统的方法。

  首先在升级前不少iOS用户会有这样的疑问,iOS 7系统运行在老iOS设备上是否存在卡顿现象呢?笔者使用的是最为短命的iPad 3设备,在第一时间升级到iOS 7系统后发现存在一定的卡顿现象,虽然影响甚少。但是用惯流畅的iOS系统,大家肯定会为这小点卡顿而抓狂。

  ios7正式版怎么升级?iPad升级iOS7教程

  苹果iOS 7系统

  ios7正式版怎么升级?iPad升级iOS7教程

  提示有苹果iOS 7.0.2系统固件升级

  我们打开专为升级iOS 7系统而生的最新版iTunes软件,iPad连接电脑后找到右上角的“iPad”标志点击进入。

  ios7正式版怎么升级?iPad升级iOS7教程

  进入到iTunes

  进入到iPad信息页面,可以发现目前的系统为iOS 7.0,iTunes也会提示有可用的新系统更新。在更新iOS 7.0.2之前,先对iPad信息进行备份操作,可选将信息备份到iCloud或者电脑本地,然后点击立即备份即可。

  ios7正式版怎么升级?iPad升级iOS7教程

  对iPad个人文件进行备份

  备份完成后,我们这时候需要下载最新的iOS 7.0.2固件,大家可以直接通过iTunes下载固件,不过此方法所需时间比较长而且不太稳定。建议大家在网上搜索7.0.2固件进行下载。获得固件后,我们可以进行系统升级。下面有两种升级iOS 7.0.2的方式,大家可以根据个人需要升级系统:

  1、【Shift+更新】:仅对固件进行更新,保留现有资料和已经安装的程序。但是已经越狱的iPhone或iPad禁止使用此方法!否则有后遗症!没有越狱的iPhone或iPad则可以直接使用此方式。

  2、【Shift+恢复】:清除iPhone或iPad里的所有资料,并写入固件。这种方式刷的固件相对干净一些,当然没有越狱的机器也可以使用此方法;但越狱的iPhone或iPad必须用此方式。

  ios7正式版怎么升级?iPad升级iOS7教程

  更新iOS 7.0.2系统

  选择已经下载好的固件,确认更新iOS 7.0.2系统。iTunes会傻瓜式的自动升级系统,大家需要做得就是等待十分钟左右。

  ios7正式版怎么升级?iPad升级iOS7教程

  选择iOS 7.0.2固件

  ios7正式版怎么升级?iPad升级iOS7教程

  确认更新iOS 7.0.2系统

  ios7正式版怎么升级?iPad升级iOS7教程

  iTunes自动升级iOS 7.0.2

  约十分钟之后,iPad会自动重启开机,这时候打开设置—通用—关于本机,可以查看到系统版本为iOS 7.0.2的系统信息。

  ios7正式版怎么升级?iPad升级iOS7教程

  苹果iOS 7.0.2系统

  ios7正式版怎么升级?iPad升级iOS7教程

  苹果iOS 7.0.2系统界面

  总结来说,iPad 3从苹果iOS 7.0升级到苹果iOS 7.0.2在相应速度上有一定的提升(不排除个人感觉),动画效果也不再存在卡顿掉幁现象。如果担心iOS 7.0.2日后无法越狱的用户可以放心,今日越狱大神肌肉男在个人推特上表示iOS 7.0.2升级并不涉及iOS 7越狱漏洞修复,大家可以放心升级。

2018-02-02 09:59:37 dajian790626 阅读数 2581

凤凰涅槃,浴火重生。
在传说当中,凤凰是人世间幸福的使者,每五百年,它就要背负着积累于在人间的所有痛苦和恩怨情仇,投身于熊熊烈火中自焚,以生命和美丽的终结换取人世的祥和与幸福。同样在肉体经受了巨大的痛苦和轮回后它们才能得以重生。垂死的凤凰投入火中,在火中浴火新生,其羽更丰,其音更清,其神更髓,成为了美丽,辉煌,永生的火凤凰。
凤凰涅槃的佛教故事来源于印度。据印度史诗《罗摩衍那》记载:保护神毗湿奴点燃熊熊烈焰,垂死的凤凰投入火中,燃为灰烬,再从灰烬重生,成为美丽辉煌永生的火凤凰,这就是凤凰涅槃的佛教来源。涅槃的意思是火的息灭或风的吹散状态,被人寓意寂灭、解脱、不生。
还有一个美丽传说,传说在古老的天方国,有一对神鸟,雄为凤,雌为凰。满五百岁后,集香木自焚,复从死灰中更生,从此鲜美异常,不再死。雄奇的大黑山上,全彩激光灯映射出长达数公里的时光隧道和漫天的云彩,高达十米的烈焰从山顶喷薄而出,飞瀑飞流直下,在水与火的交融中,凤在歌鸣,凰在和弦,演绎了一个流传千古的美丽传说。
此典故寓意不畏痛苦、义无反顾、不断追求、提升自我的执着精神。

之前我在负责公司的一款海外产品时,需要进行facebook和twitter分享,在完成这两项功能后,进行了总结,现在拿出来分享给大家,希望大家喜欢。注意,以下所有网址都需要连接VPN。

Facebook分享

1、第一步到https://www.facebook.com/网站注册自己的账号和密码,一般有gmail的邮箱即可注册。

2、第二步到facebook的开发网站获取一些分享方面的开发信息

位置为https://developers.facebook.com
分享方面的文档在:https://developers.facebook.com/docs/android/share?locale=zh_CN

3、第三步,需要在后台进行项目注册,这个工作一般由服务端来做。

例如,我们的产品的地址为,https://www.facebook.com/pages/xxxxx/666666666666。到其中去注册添加分享项目的信息。客户端需要提供一些信息,比如launcher的类名称全拼。
如,我们的项目为com.shere.xxxx.ui350.MainSplashActivity,正式版本签名的key hash值等。

4、编译需要链接源代码项目FacebookSDK,这个是其官方提供的SDK。

并且需要在混淆文件添加如下代码,来排除混淆。

-libraryjars ../FacebookSDK/libs/bolts-android-1.1.2.jar
-keep class com.facebook.** { *; }
-keep interface com.facebook.** { *; }
-keep public class * extends com.facebook.**

5、分享分两种方式,客户端分享和网页分享。具体代码在GetCoinActivity.java中。

通过返回值,例如如下代码

canPresentShareDialog = FacebookDialog.canPresentShareDialog(this,FacebookDialog.ShareDialogFeature.SHARE_DIALOG);

中的canPresentShareDialog来判断,如果为true表示客户端分享,false为网页分享。具体流程可以参考代码,都在代码GetCoinActivity.java中。

6、分享后有如下回调

private FacebookDialog.Callback dialogCallback = new FacebookDialog.Callback(),

失败会在onError函数中处理,成功会在onComplete中处理。

7、分享需要添加从服务器端申请的应用id。

有两个地方需要添加。

其一,字符串中添加如下代码,

    <string name="app_id">11111111111111111</string>

其二、manifest.xml中添加,如下代码,

        <!-- facebook -->
<meta-data
android:name="com.facebook.sdk.ApplicationId"
android:value="@string/app_id" />

<activity
android:name="com.facebook.LoginActivity"
android:label="@string/app_name"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

<provider
android:name="com.facebook.NativeAppCallContentProvider" android:authorities="com.facebook.app.NativeAppCallContentProvider11111111111111111"
android:exported="true" />

注意其中标红的数值和字符串资源中定义的app_id保持一致。

8、分享facebook需要登录,目前只要登录一次即可,未实现退出登录。

再次分享可以直接进行分享,无需登录。

9、facebook和twitter分享都需要一个Logo地址。

形式如下所示,

public static final String LOGO_URL = http://update.xxx.xxx.cn/androidup/xxxx/icon.png

,在分享后可以看到分享应用的Logo图标。这个需要服务端来提供。

Twitter分享

1、登录网站https://twitter.com/,注册账号。

2、后台进入网站https://dev.twitter.com,注册后台信息,一般需要服务端人员进行信息注册。

客户端协助提供一些信息。

3、twitter没有官方的分享指导文档,但有不同版本的sdk。

分享方面全是一些个人做的开源项目。我参考的是其中的一个,也是较稳定的一个。
需要集成如下.jar。

    signpost-commonshttp4-1.2.1.1.jar
signpost-core-1.2.1.1.jar
signpost-jetty6-1.2.1.1.jar
twitter4j-core-4.0.2.jar

4、混淆文件需要排除twitter相关的类,在混淆中添加如下代码,

    -keep class twitter4j.** { *; }
-keep interface twitter4j.** { *; }
-keep public class * extends twitter4j.**
-keep class oauth.signpost.**
{ *; }
-keep interface oauth.signpost.** { *; }
-keep public class * extends oauth.signpost.**
-keep class oauth.signpost.commonshttp.**
{ *; }
-keep class oauth.signpost.jetty.** { *; }

5、twitter分享需要Consumer Key和Consumer Secret,这两个值由服务端在twitter官网注册应用信息时生成。

类似于,

    //twitter的Consumer Key
public static final String twitter_consumer_key = "AAAAABBBBBBBCCCCCCDDDD";
//twitter的Consumer Secret
public static final String twitter_secret_key = "OIubPNuoTzoe9FMFJ5jvEW1TpvcG5CRQmsdfdfadfafeae";

这样的格式。
如我们的项目是在Constants.java中进行定义的。

6、twitter只有接口分享,没有客户端分享。

在接口的基础上,我们自己加了一个分享界面TwitterShareContentDialog.java。用户可以修改分享的内容。具体流程可以参看代码。

7、分享另外需要几个辅助文件,分别为,TwitterApp.java,TwitterDialog.java,TwitterSession.java都开源项目中提供的。

以上就是facebook和twitter如何进行分享的方法,按照如上方法可以构建对应的分享功能,如有任何疑问也欢迎咨询。

这里写图片描述
本公众号将以推送Android各种技术干货或碎片化知识,以及整理老司机日常工作中踩过的坑涉及到的经验知识为主,也会不定期将正在学习使用的新技术总结出来进行分享。每天一点干货小知识把你的碎片时间充分利用起来。

2017-03-01 14:32:02 byeweiyang 阅读数 195

感谢作者 bang 的授权发布,版权归原作者所有,未经允许,请勿转载。
原文地址:http://blog.cnbang.net/tech/3354/
作者:陈振焯,网名 bang,推特中文圈、伊书、JSPatch 作者。2011年毕业,曾在百度实习做 Web 前端,目前在腾讯做微信读书 iOS 端。
技术之路,共同进步,欢迎投稿、给文章纠错,请发送邮件至 mobilehub@csdn.net,或加微信 tree-rain-chen。

最近有一些开发朋友问我应该怎样提升自己的能力,回想起来做了这么久 iOS 开发,我也有过那种“让我做一个功能实现个需求我会做,但接下来怎样提高我不知道。”的时期,这里尝试列一下 iOS 开发的相关技术,再说说在学习进阶上我的一些想法。

iOS 技术栈

这里按我的理解给 iOS 相关技术分个类,以工程实现的角度,分成了基础、需求、效率、质量四个类别。基础指程序开发和 iOS 开发的基础知识和技能,需求就是产品的需求,有了基础技能,实现了产品需求后,剩下的事情就都是为了提高项目质量和提升开发效率。

大致的思维导图(高清点这里):

基础

基础包括语言、框架、内存、网络、存储、渲染、线程。

语言目前 iOS 开发就是 OC 和 Swift,国内仍以 OC 为主,对于 OC 除了语法外,最好了解它的对象模型,动态机制等特性。Swift 方面若要在团队里使用,目前还是风险大收益小的,但个人最好保持对它的关注。

框架就是 Foundation / UIKit 以及苹果系统封装好的各种框架,Foundation 和 UIKit 每个做 iOS 开发的人都熟知这套,iOS 功能越来越多,苹果提供的框架也越来越多,像 StoreKit / MessageUI / AVFoundation 等可以在使用到再去了解。

接着是客户端里最常见流程里的四个关键部分:从网络拉取数据,存储到本地文件系统,再从本地取出来放进内存,最后渲染出来。而这里所有的处理都在操作系统的进程和线程中执行。

网络方面若要深入的话内容很多,客户端一般只需要关心 http / https / dns 这几个协议,了解 https 的原理,处理运营商劫持 dns 劫持等情况,另外需要处理好各种异常情况做好重试机制,iOS 作为移动端网络不稳定,要看情况优化弱网络下的连接,做好离线机制,以及注意避免耗费太多流量。还有客户端跟后台的通信协议,数据结构一般用 json 或 protobuf,由于客户端本地会保存一部分内容,很多 APP 都会需要做数据的增量更新。

存储方面主要是 sqlite,sqlite 作为存储引擎是大多数 APP 的核心,也是性能优化的关键点,最基本的需要知道主键索引事务等数据库基本概念,再深入需要了解具体的存储机制/索引的实现/sqlite的七层结构等,才能在遇到问题时找到最佳的解决方案。客户端上 nosql 用得较少,除了 sqlite 一般就剩单文件存储,XML存文件或对象序列化成二进制存储,也是常用的存储方式,近期有 realm 这种新型数据库,也值得了解一下。

内存方面,需要了解 OC 的引用计数、 ARC 机制、自动释放池等相关点,最好其他语言的垃圾回收机制也有所了解,另外需要注意避免内存泄露,管理好客户端的缓存,避免缓存太多导致OOM,或缓存命中率太低性能低下。

渲染方面主要是文字和图像,基础上文字方面 UIKit 已封装得很好,CoreText也提供了更自由的排版渲染方式,图像渲染只需要注意解压时机,再深入需要了解 iOS 具体的渲染机制,像图层混合,渲染时机,离屏渲染等,才好做更多的优化。

线程和进程方面,iOS 开发只在做 Extension 时才需要考虑到进程,一般只需处理好线程,需要了解主线程子线程,多线程并发锁竞争,死锁,GCD,Runloop 等知识点。

需求

需求方面姑且概括为普通需求、特殊需求和运营需求。

普通需求就是上面提到的网络拉数据->存储->读取->展示,大多数 APP 主要都是在实现这类需求,熟悉上述的基础知识后就能轻易实现。

特殊需求是指一些特定 APP 的需求,像浏览器内核,文字排版引擎,音视频和图像处理引擎,图标绘制引擎等,要求较高,都需要在相关领域里较深入的钻研才能做好。

运营需求是 APP 上线后持续运营过程中的需求,包括功能动态化,可以随时增删改线上的功能,一般这块由内嵌 web 承担。配置系统也算动态化的一种,可以通过各种开关控制展现的功能。统计系统记录 APP 各项运营数据,包括用户增长情况,留存率,功能使用情况等。事件流可以清楚看到用户在 APP 里的使用流程。有些 APP 还会开发推荐系统,根据收集来的数据给不同用户推送不同内容,提高用户转化率等。

质量

越大的 APP 会花越多的精力在保证和提高 APP 质量上,包括性能优化,搭建监控体系,提升代码质量,保证安全,以及通过测试保证质量。

性能优化范围很大,在网络/存储/内存/渲染/算法各方面都有优化的可能,一般性能上的优化可以分成三步走,一是检测各方面的数据,量化运行性能,二是从中找到性能瓶颈,三是找办法优化,用第一步的数据验证优化效果。

监控体系在面向大众用户的产品里无论是前端后端一直都是非常重要的,你需要时刻知道用户在使用你的产品过程中有没有发生什么问题,让你的 APP 处于可知可控状态,客户端最常见的监控点是 crash,这个无需多说,另外一般对于 APP 里的错误码,包括本地错误、网络错误等都需要监控起来,这样在出现异常时才能即时得知进行处理。其他通用的监控包括卡顿监控、数据库监控、流量消耗监控、内存消耗监控、各种耗时监控等等,还有各类业务相关的监控,越大的 APP 监控的项目就越多越细致,目的都是及时发现问题,以及衡量 APP 的质量。除了监控问题外,这里还需要做好出错时的补救措施,可以通过预埋功能开关配置或接入热修复的库去做。

安全方面,客户端上安全的分量相对于服务端是少很多,尤其是在 iOS 系统沙盒机制的保护下,本身已经比较安全,最需要注意的是网络传输的安全,避免网络传输内容被篡改,或泄露了用户名密码等敏感信息。对于代码里有机密信息的可以考虑混淆代码对 APP 进行加固,减少被破解的概率。

代码质量主要存在于团队协作上,一般团队都会定义代码规范,让大家的代码风格趋于一致,有些会开发代码规范检测工具,确保提交的代码遵循代码规范。另外很多团队都会实行 code review 机制,互相查看代码,减少脏乱差代码出现的概率,具体 review 机制各有不同。

测试是一个专业,国内终端产品因为迭代快,常见的是黑盒测试,虽然不能保证无问题,但成本低效率高,部分稳定的核心功能会做单元测试,也有一些团队所有业务功能都做自动化测试的。

效率

客户端的架构可以说都是为了提高开发协作效率,因为功能可以用很多种方法实现,可以不需要什么架构,无论是大型还是小型 APP 都可以按一套来实现,只不过差的架构在中大型 APP 上代码会很混乱,导致在开发/协作/debug上效率会越来越低,好的架构则会提升这里的效率。大多数架构都是分层抽象和解耦,把功能独立的组件抽离出来,业务模块化,分层职责清晰,互相不耦合。只要分层抽象和解耦做得足够好,无论多大的 APP 都是很多小模块的拼接,就可以降低复杂度,提高开发效率。但有时解耦会带来通信的麻烦,抽象也有粒度大小的问题,这些都需要根据具体情况权衡。业界有各种各样的架构模式可供参考,像 MVC / MVVM / MVP / VIPER 等。除了解耦和抽象,还有一些改变编方式的架构,像响应式编程,单向数据流等。

持续集成的意思是不断把每个人做的东西(代码/资源等)集成到一起输出成品,进行自动化构建,其中涉及代码管理(git / svn),编译流程,证书和签名机制,自动化测试,打包发布等。其中还会有一些自定义的自动化流程,例如自动生成代码,根据 debug / release 包类型自动更改配置等,重复做的事都应该自动化,以提高开发效率。

业界为了提升开发效率,跨平台开发一直是大家孜孜不倦追求的目标。终端上跨平台愿望是只开发一次,就能完美运行在 Android 和 iOS 上,业界有很多尝试,这篇文章总结得比较全,总的来说目前最好的跨平台方案就是 web (H5),代价是性能略低。

进阶

列完 iOS 开发的相关知识点,接下来说说怎样学习提高。

如果自学能力强的话,不需要多说,上述每个点网上都有大量资料,像内存网络存储这些计算机基础知识也有经典的书籍,一个个啃下去就行了,只要理解得足够深入,就已经可以成为领域里的专家,并很容易触类旁通。

但这种学习方法会比较枯燥,也难以实践,个人还是比较推荐在实践中学习,具体来说就是在平时开发过程中不断地发现问题 -> 解决问题

发现问题

首先你最好处在一个有很多工程上的问题急需解决的环境里,这样发现问题就很容易,最好的是处于这几类项目里:

  1. 处于高速发展期的项目。增长会带来很多问题,一切又未成熟,解决这些问题是非常自然又有价值的。
  2. 庞大的项目,超级APP会带来很多中小型APP没有遇到过的问题,又因为体量大,就算只有千分之一的人遇到也会影响几十万人,很有解决的价值,会有很多细致的问题。
  3. 像上面提到的有“特殊需求”一类的项目,需要在一个领域里深入研究,也会自然碰到很多问题。

如果恰巧没有在这三种类型的项目里,也没关系,只要是健康发展的项目,总会存在问题和优化空间,只是要培养发现问题的意识,很多时候问题就在那里,但没人发现它,没人觉得它可以/应该解决。可以按上述列的点,在相关点上多问自己能不能提高效率和质量,例如能不能提高前后台联调效率,重复写的代码能不能自动生成,启动耗时能不能短一点,线上问题发现和排查的效率能不能提高等等。各种问题会涵盖上述提到的所有知识点。

如果不幸你的项目没有健康发展,实在没碰到什么问题或者问题不值得解决,或者你还没毕业,那这里还有一个万能问题可供参考:那些知名的开源项目具体是怎样实现的?剖析开源项目源码可以学到很多东西,各种各样的开源项目也覆盖了很多知识面,只要深入去研究它们,学习它的架构和编码,不懂的地方再去补齐知识,也是个很好的学习方式,如果学习后能输出文章效果会更好,相当于动手实践了。

解决问题

不同的解决问题的方式差别很大,有一些常见的套路可供参考:

  1. 业界是怎样解决这个问题的?他们的方案有什么不足?我怎样可以做得更好?

    业界有各种各样的开源库和技术分享,只要问题不是太偏门,大多会有人已经提出解决方案,多对比和研究这些已有的方案,看它们是否能满足需求,找出它们的优点和不足,看看能不能做得比它们更好或更适合解决碰到的问题。

  2. 解决方案能否通用化,封装成开源库供其他项目使用?

    开源项目都是这样来的,如果遇到一个别人没解决好的问题,别错过封装成开源库造福社会。

  3. 有没有办法防止以后出现类似的问题?

    有些问题可能会反复出现,能不能防止,或者能不能在出现问题的时候能及时发现和修复,这可能涉及到开发流程、自动化和监控体系等方面的完善。

  4. 总结沉淀

    能不能总结出解决这类问题的方法论(套路)?最好能输出文章或分享,写的过程是很好的学习过程,因为要把原本模糊的想法都清晰地表达出来,迫使自己去整理思路。

总结

这里按我的理解列了 iOS 相关技术点,以及在实践中提升能力的一点小建议,可能无法各方面都覆盖到,只是作为一个参考。另外这里只局限在 iOS 开发上,实际上作为程序员不应该限制自己学习的范围,有时间多去了解后端/前端/运维也会很有利于自身开发能力的提高。


2013-10-16 22:53:58 yuanya 阅读数 0

目录(?)[+]

目前找到的网上关于iOS Push最详细的文章,很不错,而且还有php代码。

http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12


Learn how to add Push Notifications into your iPhone app!

Learn how to add Push Notifications into your iPhone app

在iOS系统中,在后台运行的程序能够进行的操作是非常有限的。这种限制是为了节省手机电池。
但是,如果你需要在用户没有使用你的程序的情况下给他们推送消息该怎么办呢?

打个比方,用户收到了一个来自推特的信息,他喜欢的球队在比赛中取胜,或者他的晚饭准备好了。因为现在用户并没有在使用我们的程序,程序本身无法听取这些事件。

幸运的是,苹果系统对此已有了解决办法。你并不需要让程序不停地听取事件或者在后台跑运算。你只需要编写一个服务器组件来完成这个任务。

在一个特定的事件发生时,那个服务器的组件就可以给我们的程序发一个推送信息!推送信息可以做如下三件事:

  • 显示一条信息
  • 播放一小段提示音乐
  • 在程序的小图标边上放置一个数量标志

你可以随意组合这些选项;比如你可以播放提示音并放置数量标志,而不显示任何信息。

在这个有两部分的教程中,你会用苹果推送服务器(APNS)来完成一个有消息推送功能的简单的程序。

在第一部分,你会学习如何接收推送的信息。

这篇教程针对的是中级或者高级的iOS开发者。如果你对iOS还处于入门阶段,你应该先看看这个网站上一些初级教程. 并且,我也建议你可以先看看下面这两篇教程(或者有类似的经验):

那就让我们开始吧!

文章概略

为你的app加入信息推送是需要付出很多努力的。这个任务有很多个部分。下面是一个概要:

Apple Push Notification Services (APNS) Overview

  1. 程序启用消息推送功能。用户必须确认他希望接受这些推送信息。
  2. 那个程序接收一个“设备标记码”。你可以把这个设备标记码理解为推送信息的地址。
  3. 那个程序将这个设备标记码发送到服务器。
  4. 每当任何关于你的程序的事件发生时,那个服务器会将信息发送到苹果的推送信息服务(APNS)。
  5. APNS 将这个信息再推送到用户的设备上

用户的设备收到这个信息时,会有提示窗口,播放提示音或者更新app的数量标志。用户可以在提示窗口中直接开启这个app。我们的app从这里接过推送信息的内容并能自定义处理这个信息的逻辑。

有人会问,iOS4中已经有了本地提示以及多重任务执行,那推送信息提示还有用吗?答案:“当然啦”!

本地提示仅能用于定时的事件。无限制的后台运算也仅限用于网络通话,导航和背景音乐类的app。如果在程序已经进入后台运行还想给用户信息提示,那我们仍然需要使用推送信息提示。

在这个教程中,我会详细解释推送信息提示是怎么实现的,以及如何在你的app中使用它。需要学的东西很多,让我们现在开始吧!

推送信息提示准备工作

在你的app中加入推送信息提示,你需要:

一台 iPhone 或者 iPad。 你需要真实的设备因为推送信息提示在模拟器中不能用。

你必须是注册的iOS开发者。 每个要加入推送信息的app的需要一个新的App ID,provisioning profile 和SSL证书。你可以在iOS Provisioning Portal来完成这些所需操作。

如果你想跟着这个教程中的例子,那你就需要创建新的provisioning profile和SSL 证书;你不能用我的。因为获取正确的证书很重要,我会一步步解释如何获取这个证书。

一个连接到网上的服务器。 推送信息是由这个服务器发送出来的。开发期间你可以用你的苹果电脑作为服务器(我们在这个教程中就会这样做),但是在app上线后,你至少需要一个VPS(虚拟私人服务器)。

一个简单的共享的服务器账号是不够的。你需要能够在服务器上跑后台线程,安装SSL证书以及对外在特定端口建立TLS链接。

大多数共享服务器的提供者是不会让你这么做的。所以,我强烈建议你用 Linode类型的服务器.

解析消息推送服务/h2>

你的服务器负责创建被推送的消息,所以我们应该来了解一下这个服务器是怎么做到的。

一个推送消息会包含设备标记码,信息负载和一些其他的字节。那个信息负载就是我们要发到设备上的推送消息。

你的服务器提供的这个信息负载应该是JSON字典的格式。一个简单的推送信息负载应该是这样的:

{
	"aps":
	{
		"alert": "Hello, world!",
		"sound": "default"
	}
}

如果你不了解JSON,你只需要知道用“{}”符号分割出来的一个代码块代表了一个键与值对应的字典(和NSDictionary相似)。

我们的信息就是这样的一个字典。这个字典里至少要有一个物件,“aps”。在这里,“aps”本身又是一个字典。“aps”包含了“alert”和“sound”两个键。当设备收到这个消息时,程序应该显示一个弹出消息:“Hello,world!”并且发出标准的提示音。

你还可以在“aps”字典中加入其他物件来设置那个消息。比如:

{
	"aps":
	{
		"alert":
		{
			"action-loc-key": "Open",
			"body": "Hello, world!"
		},
		"badge": 2
	}
}

注意“alert”本身也变成了一个字典。弹出信息栏的查看按钮的标签会变换成“action-lock-key”的值。“badge”键所对应的数字值会成为程序图标的数量标记。这个消息不会发出提示音。

这个JSON格式的信息负载还有很多可以设置的选项。你可以改变提示音的声音,提供翻译过的标签,你也可以加入自定义的键值对。如果有兴趣深入了解,你可以看看苹果公司提供的 本地和推送信息编程指南.

推送信息应该很简洁;那个信息负载不应该超过256个字节。这样一般有足够的空间传送一个SMS信息或者一个推特消息。正确的服务器不会浪费宝贵的负载空间了传送换行符和空格,所以你的服务其应该发送这样的信息:

{"aps":{"alert":"Hello, world!","sound":"default"}}

这个对于人来说比较难读懂,但是却更节省空间。苹果APNS不会接受超过256个字节的推送消息。

推送消息的易错点

推送消息不可靠!

推送消息不可靠!

太不可靠了! 就算在APNS服务器接收了信息的情况下,推送消息也不一定会成功地传到用户设备上。

你的服务器在发射推送消息到APNS后,没有任何办法可以获取消息的状态。消息实际被推送的时间也不一定,可能几秒钟,也可能要半个小时。

并且,用户的iPhone并不是随时都能接收推送信息。他们可能在一个无法接收苹果推送服务的无线网络中,原因可能是相应的网络端口被封闭了;或者用户的手机关机了。

苹果推送服务会在手机重新上线后试着重试没有传递的信息,但是这是有时限的。时限一过,我们就永远失去那个信息提示了!

在看到苹果推送服务的账单后

在看到苹果推送服务的账单后

好贵啊! 如果有很多用户,或者所推送的信息需要从其他地方不断获取,那这个服务的费用可能会很高。

打个比方,如果你控制了RSS源。因为你会清楚知道什么时候会有新的RSS,你可以轻松直接地在适当的时候给用户推送信息。
但是,如果你的程序允许用户输入自定义的RSS网址怎么办?在这种情况下你需要有自己的一套方案来探测是否有心的RSS信息。

事实上,你的服务器需要不停地访问这些地址来探测新的RSS信息。如果你有很多用户,那你就需要不断增加新的服务器来探测和发送信息。这很快就会变得非常昂贵。

理论问题讨论完了,那我们就开始实践吧。 但是在做有趣的事情–编程,之前, 我们还需要在iOS开发者门户网站上完成一些无聊的设置。

Provisioning Profiles以及各类证书

苹果推送服务需要一个证书!

苹果推送服务需要一个证书!

为了使你的程序能使用推送服务,我们需要建立一个特别用来进行这个服务的provisioning profile。另外,你的服务器还需要通过SSL证书来和苹果的推送服务器建立联系。

provisioning profile和SSL证书是一一对应的,并且只有在有一个有效的App ID的情况下才能用。这是为了保证你的服务器只能将推送信息发到这一个特定的程序中,而不能发到其他任何程序。

值得注意的是,一个程序在开发时和发布时需要用不同的provisioning profile。同时也有两种服务器证书:

  • 开发时. 如果你的程序是在Debug模式下运行,并且使用的是开发阶段的provisioning profile(Code Signing Identity属性的值是”iPhone Developer”),那你的服务器必须使用开发阶段的证书。
  • 生产时. 程序如果已经在苹果商店上发布(Code Signing Identity属性的值是”iPhone Distribution”),那服务器必须使用生产阶段的证书。如果这两个被弄混了,推送的提示信息就无法到达你的程序。

在这个叫教程里,我们将只会使用开发时的profile和证书。

生成Certificate Signing Request(证书申请)

还记得你如何在iOS开发者网站上注册iOS开发计划并获取开发证书的吗?我们下面要做的和那个类似。但我还是建议你一步一步跟着我来完成这个操作。因为大多数问题都出在证书上。

电子证书都是基于公共和私有密钥加密的。你并不需要知道这个加密的过程,但你需要知道那个证书必须和一个私有密钥一起用才会有效。

那个证书是这组密钥的公共部分。所以把这部分随便给别人是没有问题的,在你用SSL来通讯时就需要这么做。但是那个私有密钥却需要保密。这个秘密不能让任何别人知道。如果你没有这个密钥,那证书就失效了。

在你申请一个电子证书时,你需要提交一份Certificate Signing Request(证书申请),缩写为CSR。在你生成CSR时,会生成一个新的私有密钥并被放到你电脑的keychain中。然后你把这个CSR发到一个证书的认证网站(在我们的情况下,这个网站就是苹果的开发者门户网站)。这个网站会用CSR生成相应的SSL证书。

在你的Mac上打开Keychain Access程序(在Applications/Utilities子目录下),然后在菜单中选择 Request a Certificate from a Certificate Authority…,意思是从证书权威获取证书。

Requesting a certificate with Keychain Access

如果你在菜单里找不到需要的选项,或者选项的标签是“Request a Certificate from a Certificate Authority with key”, 那你就需要先下载并且安装WWDR Intermediate Certificate。 还需要注意的是,在Keychain Access主窗口中,千万不要选择任何的私有密钥。

然后你应该看到类似下面的窗口:

用Keychain Access来申请证书

输入你的邮箱。我曾听有人说你应该用和你申请iOS开发者计划时一样的邮箱,但是应该任何邮箱都是可以的。

在Common Name(公用名)一栏输入”PushChat”. 你可以输入任何名字作为公用名,但最好选择一些有代表性的名字。这样我们以后才能很快的找到这个私有密钥。

Saved to disk(保存到硬盘)边上打钩。将它保存为”PushChat.certSigningRequest”。

如果你现在来到Keychain Access的Keys部分,你应该看到一个新的,你刚刚生成的私有密钥。右键点击并将它汇出。

用keychain access汇出私有密钥

将汇出文件命名为“PushChatKey.p12”然后输入一个加密码。

为了方便,我的加密码就是“pushchat”。但为了更好地保护这个p12文件,你应该选择一些更难猜到的加密码。私有密钥必须是秘密的,不是吗?但是你一定要记得住这个加密码,否则私有密钥还是不能用。

创建App ID和获取SSL证书

登陆iOS置备门户网站.

首先,我们需要创建一个新的App ID。每一个用推送信息服务的app都需要一个特有的ID,因为只有这样,被推送的信息提示才能被发送到正确的程序上。(在这里,你不能使用通配符ID。)

在边上的目录栏中选择“App IDs”,然后点击“New App ID”按钮。

生成新的App ID

我填写内容如下:

  • Description: PushChat
  • Bundle Seed ID: Generate New
  • Bundle Identifier: com.hollance.PushChat

你最好是选择一个你自己的Bundle Identifier,而不是用我的,例如“com.你的网站.PushChat”。你之后在Xcode中还要使用这个Bundle Identifier。

很快,我们会生成一个SSL证书。你的服务器就是用这个证书来和苹果的推送服务器建立安全连接。这个证书和你的App ID绑定在一起,这样你的服务器就只能给这一个app发送推送信息提示了。

在你创建好App ID后,你应该看到类似如下的列表:

List of App IDs in the iOS Provisioning Portal

在“Apple Push Notification service”(苹果推送信息服务)一栏里,有两个橘黄色的标志分别写着“Configurable for Development”(开发设置)和“Configurable for Production”(发布生产设置)。这意味着我们的App ID已经可以用来进行推送了,只是还需要我们做相应的设置。点击那个写着“Configure”的链接来打开设置App ID的界面。

Configuring your App ID in the iOS Provisioning Portal

“Enable for Apple Push Notification service”(启用苹果推送服务)旁打钩然后点击对应“Development Push SSL Certificate”的Configure 按钮。苹果推送服务SSL证书助手界面就出现了:

Uploading your CSR with the SSL Assistant

它首先要你生成一个证书签署请求。我们已经完成这一步了。点击Continue。在下一步你需要上传已经生成的证书签署请求(CSR)。选择那个CSR文件,然后点击Generate

Generating a Certificate with the SSL Assistant

生成SSL证书需要几分钟时间,完成后点击Continue

Downloading a certificate with the SSL assistant

点击“Download” 来下载证书 – 名字应该叫 “aps_developer_identity.cer”. 点击 “Done” 来关闭助手界面并回到设置App ID的界面。

Screenshot after the SSL Assistant is Complete

如你所见,我们现在有一个有效的证书了,可以开始给App推送证书了。如果需要,你可以在刚才的地方重新下载证书。开发证书的有效期是3个月。

当你的App准备可以正式使用时,重复上述步骤来获取生产时用的证书。步骤是一样的。

注意: 生产时用的证书有效期是1年。但你需要提前更新它以确保你的App的推送服务不会中断。

创建PEM文件

现在我们总共有三个文件:

  • CSR
  • 私有密钥(PushChatKey.p12)
  • SSL证书(aps_developer_identity.cer)

将这三个文件好好保存起来。你可以选择不再用那个CSR文件了。但是在你更新证书时,你可以使用同一个CSR或者获取一个新的。如果你获取新的CSR,你同时会生成一个新的私有密钥。如果使用同一个CSR,那就只有SSL证书需要改变了。

我们需要将证书和私有密钥转换成另外一个更方便的格式。我们在服务器的推送部分会用PHP来写,所以我们现在可以把证书和私有密钥合并成一个PEM格式的文件。

PEM文件是怎么运作的并不重要(说实话我也不知道),但是这样我们能更容易地在PHP里使用这个证书。如果你准备用其他语言来写这个服务器部分,那下面的步骤可能会不配套。

我们将使用命令行OpengSSL的工具来完成这项任务。打开一个Terminal并输入以下指令。

“cd”到你下载证书,密钥文件的文件夹,对于我是桌面文件夹:

$ cd /Users/matthijs/Desktop

将那个.cer文件转换成.pem文件:

$ openssl x509 -in aps_developer_identity.cer -inform der 
    -out PushChatCert.pem

将那个密钥.p12文件转换成.pem文件:

$ openssl pkcs12 -nocerts -out PushChatKey.pem -in PushChatKey.p12
Enter Import Password: <输入你导出密钥时用的那个密码>
MAC verified OK
Enter PEM pass phrase: <输入一个新的密码>
Verifying - Enter PEM pass phrase: <重复密码>

你首先需要输入.p12文件的密码,这样openssl才能读取这个文件。然后你需要你个新的密码来对pem文件进行加密。在这个教程中我用的还是“pushchat”。但你自己应该选择一个更加保险的密码。

注意:如果你不输入PEM的密码,openssl不会给你任何的错误信息。但是生成的.pem文件里就不会有那个密钥。

最后,将那个证书和密钥合并为一个文件:

$ cat PushChatCert.pem PushChatKey.pem > ck.pem

我们应该测试一下这个证书是否能用。执行如下指令:

$ telnet gateway.sandbox.push.apple.com 2195
Trying 17.172.232.226...
Connected to gateway.sandbox.push-apple.com.akadns.net.
Escape character is '^]'.

我们试着与APNS服务器建立一个一般的,没有加密的连接。如果你看到类似上面的回复,那说明你的Mac能连上APNS。按Ctrl+C切断连接。如果你得到一个错误信息,那你应该确保你的防火墙允许对外2195端口的连接。

让我们再次试着连接。这次,我们会使用那个SSL证书和密钥来建立一个加密连接:

$ openssl s_client -connect gateway.sandbox.push.apple.com:2195 
    -cert PushChatCert.pem -key PushChatKey.pem
Enter pass phrase for PushChatKey.pem: 

你应该看到一大窜回复内容。那是openssl的运行信息。

如果连接成功建立,你应该可以键入几个字符,然后当你点回车时,服务器就会和你断开连接。如果建立连接过程出现问题,openssl会给出错误信息,但你可能需要在那一大窜信息中找出错误信息。

值得注意的是,APNS其实有两个不同的服务器:那个沙盒服务器使用来测试的。还有一个正式的服务器是在你的程序投入生产后使用的。我们上面用的是测试用的服务器。因为我们的证书是开发时才能用的。

生成Provisioning Profile

我们还需要用Provisioning门户网页来生成一个新的Provisioning Profile。在左边的目录栏中点击“Provisioning”,选择“Development”来生成一个新的profile。
Creating a Provisioning Profile in the iOS Provisioning Portal

填写表格如下:

  • Profile Name: PushChat Development
  • Certificates: 在你的证书旁打钩
  • App ID: PushChat
  • Devices: 选择你的设备

我们需要一个新的profile因为每个有推送信息的app都需要和它App ID相对应的一个profile。

点击Submit,你的profile就会被生成。那个profile的状态一开始会是“Pending”。刷新页面直到它的状态变成“Active”,这时你就可以下载那个文件了(给它取名为PushChat_Development.mobileprovision)。

双击那个文件或者将这个文件拖到Xcode的图标上,这样就能将这个profile添加到Xcode中了。

在你将程序投入生产时,你需要重复上述步骤来生成Ad Hoc或者distribution profile。

一个非常基础的APP

到现在为止我们做的准备工作都比较枯燥,但却是必要的。我们详细的学习了生成各种证书的过程因为我们不会经常坐这件事,但是推送信息没有这些就无法运作。

通过连接到沙盒服务器,我们确认了证书是可用的。现在我们要测试一下我们是否确实可以推送提示信息!

启动Xcode并建立一个新的项目。 选择View-based Application作为这个项目的模版然后点击进入下一步。

Creating a View-Based Application with Xcode 4

我是这样填写各个空格的:

  • Product Name: PushChat
  • Company Identifier: com.hollance
  • Device Family: iPhone

那个Product Name(产品名字)和Company Identifier(公司代码)合在一起就是Bundle ID。我的是“com.hollance.PushChat”。 你应该选择和你在苹果的Provisioning Portal上注册的App ID相对应的产品名字和公司代码(com.yourname.PushChat)。

完成创建项目并打开PushChatAppDelegate.m文件。将didFinishLaunchingWithOptions改为如下:

[objc] view plaincopy
  1. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">BOOL</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didFinishLaunchingWithOptions<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSDictionary_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSDictionary</span></a> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>launchOptions  
  2. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
  3.     self.window.rootViewController <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">=</span> self.viewController;  
  4.     <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>self.window makeKeyAndVisible<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  
  5.    
  6.     <span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>// 让手机知道我们想接收推送信息提示。</em></span>  
  7.     <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>UIApplication sharedApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span> registerForRemoteNotificationTypes<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span>  
  8.         <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  
  9.    
  10.     <span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">return</span> <span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">YES</span>;  
  11. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  

我们调用registerForRemoteNotificationTypes方法来告诉OS我们的程序想接收推送信息。

编译并运行你的程序。你应该在真正的苹果设备上运行因为模拟器不支持推送信息提示。Xcode应该会自动选择那个新的provisioning profile。如果你得到一个关于“code sign”的错误,那你需要确保已经选择了正确的profile。 你可以在“build settings”中的“Code Sign”设置部分对其修改。

当启动你的程序时,系统应该会弹出一个选择菜单来询问用户是否允许当前程序推送提示信息。

A Simple iPhone App Requesting Permission to Deliver Notifications

你的程序只会向用户询问一次然后记住用户的选择。用户只有选择“OK”之后你的程序才能接收推送提示信息。用户也可以在iPhone系统设置菜单中更改推送信息的设置。

Viewing Push Notification Permissions in iPhone Settings

你app的名称会出现在手机的提示设置界面里。用户可以在这里设置是否允许你的app推送信息提示,显示数量图标以及发出提示音。

A single app's Push Notification Settings

你的app也可以通过如下的代码来探测用户启用了哪一种提示方式:

[objc] view plaincopy
  1. UIRemoteNotificationType enabledTypes <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">=</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">[</span>UIApplication sharedApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span> enabledRemoteNotificationTypes<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">]</span>;  

要让我们的app开始接收推送提示,我们还需要将下面的代码加入PushChatAppDelegate.m文件中:

[objc] view plaincopy
  1. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">void</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didRegisterForRemoteNotificationsWithDeviceToken<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSData_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSData</span></a><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>deviceToken  
  2. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
  3.     NSLog<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>@</em></span><span style="font-family:inherit;color:#bf1d1a;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">"My token is: %@"</span>, deviceToken<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>;  
  4. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  
  5.    
  6. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">-</span> <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#a61390;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">void</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span>UIApplication<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>application didFailToRegisterForRemoteNotificationsWithError<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">:</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><a target="_blank" href="http://developer.apple.com/documentation/Cocoa/Reference/Foundation/Classes/NSError_Class/" style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,104,55); text-decoration:none"><span style="font-family:inherit;color:#40080;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">NSError</span></a><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">*</span><span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>error  
  7. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">{</span>  
  8.     NSLog<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">(</span><span style="font-family:inherit;color:#1174a;margin:0px; padding:0px; border:0px; outline:0px; vertical-align:baseline;"><em>@</em></span><span style="font-family:inherit;color:#bf1d1a;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">"Failed to get token, error: %@"</span>, error<span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">)</span>;  
  9. <span style="font-family:inherit;color:#0220;margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; vertical-align:baseline;">}</span>  

当你的app注册远程推送提示时,它会获取一个设备代码(device toke)。这是一个32字节的独特数字来辨别用户的设备。你可以把这个代码看做是用户设备的地址。APNS用这个地址来推送信息提示。

在你用来测试的苹果设备上运行你的app。你应该在Xcode的控制台窗口中看到:

My token is: 
<740f4707 bebcf74f 9b7c25d4 8e335894 5f6aa01d a5ddb387 462c7eaf 61bb78ad>

那个代码是一个NSDatat对象,拥有某种二进制数据结构。我们不应该对它进行任何修改。我们只用知道它有32个字节,可以用64个十六进制的字符表示。我们会直接用这种表示方式(去掉那括弧和中间的空格)。

如果你在模拟器里运行你的程序,didFailToRegisterForRemoteNotificationsWithError:方法应该会被调用因为模拟器不支持推送信息提示。

我们的app就暂时写完了。但要真的开始推送信息,我们还需要完成最后的一步!

推送第一个信息提示

我曾说过,你需要有一个服务器来把信息推送到你的app上。但为了测试,我们还不需要一个服务器。你可以用下面这个简单的PHP脚本来和APNS建立连接并将信息推送到指定的设备上。你可以直接在你的Mac上运行这个脚本。

下载这个叫“SimplePush”代码包 并解压缩. 你需要对simplepush.php文件进行如下修改.

  1. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你的设备代码放在这里(不要空格): </em></span>  
  2. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$deviceToken</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  
  3.    
  4. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你密钥的密码放在这里:</em></span>  
  5. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$passphrase</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'pushchat'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  
  6.    
  7. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-family:inherit; vertical-align:baseline; color:rgb(102,102,102)"><em>// 把你的提示信息放在这里:</em></span>  
  8. <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,136)">$message</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">=</span> <span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(0,0,255)">'My first push notification!'</span><span style="margin:0px; padding:0px; border:0px; outline:0px; font-style:inherit; font-family:inherit; vertical-align:baseline; color:rgb(51,153,51)">;</span>  

你应该把设备代码从app里拷贝粘贴到$deviceToken变量。请确保你去掉空格和括弧。把$passphrase的值设为你密钥的密码, $message的值设为你要推送的信息字符串。

把你的ck.pem文件拷贝到SimplePush文件夹中。记住,ck.pem包含你的证书和密钥。

打开终端并打入:

$ php simplepush.php

如果一切顺利,那个脚本应该回应:

Connected to APNS
Message successfully delivered

然后几秒之后,你应该收到app的第一个推送信息提示:

Simple app receiving a Push Notification

注意,如果你的app是开着的,你就看不到推送的信息。这是因为我们还没有写代码在app里接收推送的信息。你应该关掉app然后重试。

如果simplepush.php脚本因错误退出,那你因该确保你的PEM文件是正确生成的,并且确保你可以连接到苹果的沙盒服务器(请看上面的详细步骤)。

我们并不用急着知道这个脚本到底做了什么,因为这是这个教程的下一部分的内容。在下一部分,我们将学习如何编写一个真正的推送服务器。


IOS 6

阅读数 0