适配 iOS 8 时遇到的问题两则:远程推送和 Unwind Segue

昨天我在微博上吐槽:iOS 8 / Xcode 6 真是史上对开发者最糟糕的版本了。收到很多朋友表达同感。

之所以这么说,倒不是针对 iOS 8 本身的特性来说的,相反,iOS 8 开放的那些扩展机制以及各种 Kit 对开发者来说是非常好。

我抱怨的是 Apple 最近对软件质量的控制不太好,上次发一个让手机不工作的 iOS 8.0.1 就不说了,iOS 8 / Xcode 6 本身充满了非常多的 Bug。Xcode 作为 IDE,提交 App 的时候还经常失败。比如前几天我提交 Manico 1.4.1 版本,Xcode 6 还直接失败,最后还用 Xcode 5 完成了提交。这让作为开发者的我们非常窝火。

除此之外,Apple 在 iOS 上的演进越来越激烈,为了推进新的系统特性,Apple 直接 break 了软件在升级后的向下兼容兼容特性,实在是让我吃惊。今天就谈两则最近遇到的吧。

实际上我要记录的这些在 Apple 的 WWDC 上应该都有提到,无奈视频没有全部看完,只有在碰到问题的时候才去解决。

远程推送机制的变化

如果你还在代码里用 registerForRemoteNotificationTypes 这个方法来注册推送功能,你的 App 又用 iOS 8 的 SDK 编译了,那么你的 App 在 iOS 8 下将无法注册功能推送(在 iOS 7 下是可以的)。Apple 直接将这个 API 在 iOS 8 下设置成了「无法工作」,而不是简单的标记了 deprecated。

那么在 iOS 8 下用哪个 API 去注册远程推送功能?用新的 API:registerForRemoteNotifications。

但是,这个 API,仅仅会注册一个静默功能的远程推送,尽管 App 之后会收到推送并进行处理,它不会在用户界面上有任何提示。那么如何在 iOS 8 下完成和 iOS 7 下一样的远程推送注册支持?下面的代码可以让推送的支持在 iOS 7 和 iOS 8 下都正常工作,if 里面的是 iOS 8 的,而 else 那里是 iOS 7。

UIApplication *application = [UIApplication sharedApplication];

if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
    UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
                                                                                         |UIRemoteNotificationTypeSound
                                                                                         |UIRemoteNotificationTypeAlert)
                                                                             categories:nil];
    [application registerUserNotificationSettings:settings];
    [application registerForRemoteNotifications];
} else {
    [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge
                                                     |UIRemoteNotificationTypeSound
                                                     |UIRemoteNotificationTypeAlert)];
}

可以看到,iOS 8 把原先一步到位的 RemoteNotification 的注册分成两部分,一部分是注册新引入的那个「UIUserNotificationSettings」,另一部分才是 RemoteNotifications。Apple 为什么要这样设计?

简单的说,Apple 在 iOS 8 将 RemoteNotification 和 LocalNotification 统一了起来。两种 Notifications 将统一由 UIUserNotificationSettings 来管理用户界面相关的东西:标记、声音和提醒。除了统一用户界面的通知外,UIUserNotificationSettings 还引入了 UIUserNotificationCategory,可以让用户方便的直接在 Notification 上进行一些快捷的操作(Action)。这部分我还没玩过,所以又不多讲了。

总之,Apple 为了推进新的技术,不惜直接把老的 API 弄成不工作,实在是让人惊叹…

Unwind Segue 的变化

Unwind Segue 是 Storyboard 里面一项我非常喜欢而且实用的技术。通过 Unwind,可以很方便的在同一层级(通过 Push 进行)和不同层级(通过 Modal 进行)的 ViewController 之间进行回退,只要前面的 ViewController 有一个实现了特定的 Unwind,那么在当前的 ViewController 执行这个 Unwind,就会回跳到前面的某个 ViewController,不管中间隔了几个,都能准确的跳回去。

喜欢这个技术主要是因为 Unwind 比 Delegate 更灵活,ViewController 中间可以隔好几个,不需要用 delegate 关联起来,只要实现特定的 Unwind 方法就可以了。

然而,在 iOS 8 当中,很遗憾的,Unwind 不再像以前一样好好工作了:如果你的 ViewController 是基于NavigationController,那么现在它只支持同一层级的 ViewController 之间的相互跳转(即通过 Push 方式产生的),而不支持不同层级的 ViewController 之间的相互跳转了(即通过 Modal 形式产生)。

Apple 没有什么 Depcated 的 Warning,直接就让 Modal 形式的 ViewController 下执行 Unwind 不工作了,没有任何错误和提示…

关于这个,StackOverflow 上有一个讨论:Unwind Segue not working in iOS 8

目前为止,没有一个好办法(包括那个被接受的 Answer 实际上了不行),目前为止我发现最好的办法就是重写相关代码,然后用 delegate 去实现。如果你有更好的办法,请记得告诉我。

后记

由于我差不多是 iOS 7 时代才开始正式做 iOS 开发的,我不知道以前 Apple 从 iOS 5 -> iOS 6,从 iOS 6 -> iOS 7 有没有过类似的直接把某个 API 弄为不工作,而不是循序渐进的 deprecated 的过程。

总之这次让我真切的体验到新旧版本切换时作为开发者的阵痛,且不说破坏 API 的向下兼容到底合不合适,光是 iOS 8 / Xcode 6 里各种令人无奈的 Bug 就让人足够头疼了,还是希望 Apple 能在 Xcode 6.1 里把问题变得少一些。

接下去,我要去适配 Manico 的 Yosemite 风格了…

<推广> 本站架设于 Linode 东京机房,同时使用 云梯 进行科学上网

5 Comments

  1. 乔布斯没了之后,从产品品味到软件质量,整个苹果都没有苹果原有的品质了,除了B格什么都不剩了

  2. abc

    个人见解吧,手机是个很小的东西一切为了用户体验,所以有时候要给ipad做单独的app,要做差异化,不想pc像windows大部分功能xp可以win7可以server可以。。。其实不是可以二十就没有用系统的特性,

  3. luke

    还有定位功能的api也改了,请求定位权限的API改掉了。。

  4. Jy

    更改了推送机制是为了督促开发者使用 iOS 7 新引用的后台刷新机制吧。
    先push一个静默通知让app在后台fetch新数据,fetch完毕发动本地通知来提醒用户。
    这样用户在打开app后无需等待网络加载,可直接看到最新内容,提升体验感受。

Leave a Comment