欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  资讯频道

国外iOS大牛:开发Apple Watch应用我犯过的错

程序员文章站 2022-05-21 15:04:56
...
Apple Watch已经上市一段时间,在探究其防盗、防水等众多功能的同时,Apple Watch版应用开发也在不断的摸索前进中。在本文中,来自国外的知名iOS开发大牛就Watch版应用开发过程中遇到的问题以及犯下的错误进行了分享。

Natasha Murashev——Capital One iOS工程师、Natasha The Robot博主

我犯过的最大错误就是认为在Apple Watch上发送信息就可以打开iOS App。WatchKit有一个支持直接发送信息到iOS应用的API,能够有效地触发后台进程。想要启动时只需调用一个WKInterfaceController类函数即可:
// WKInterfaceController
class func openParentApplication(userInfo: [NSObject: AnyObject],
                                    reply: (([NSObject: AnyObject]!, NSError!) -> Void)?) -> Bool

// launches containing iOS application on the phone
// userInfo must be non-nil

但是,WatchKit却并没有实现帮助用户打开iOS应用。当我在模拟器中进行测试时,它是能够打开iOS应用的,但在真机环境下却只能在后台运行。尽管如此,它依然能快速传递信息并执行重要任务。

除上述之外,我还学会了另一个非常有用的事情:如何同时调试iOS和Watch版应用。在iOS应用指令中有一个可以接收WatchKit扩展信息的函数:
// AppDelegate
func application(application: UIApplication, handleWatchKitExtensionRequest
                    userInfo: [NSObject: AnyObject]?, 
                       reply: (([NSObject: AnyObject]!) -> Void)!)

当我第一次使用这个方法时并不奏效,而我也不知道该如何调试它。每次只能完成WatchKit或iOS端部署,而且还都是以iOS应用弹出警告结束,并且系统会尝试调试代码。后来我发现,有一个方法可以同时调试iOS和WatchKit App:
  • 运行WatchKit App;
  • 在你的模拟器中打开iOS版App;
  • 返回Xcode;
  • 选择Debug> Attach to Process > (YOUR iOS App)
Brian G*——The Working Group高级移动开发者、Five Minute WatchKit博主

我犯过的最大错误就是在iOS模拟器中单独运行应用时很容易忘记其很多功能都是依赖于BTLE连接的。模拟器会尝试通过延迟请求来解决这个问题,但有时它需要的回应时间实在太长。

因此,在对应用程序的设计和开发进行改进时,开发者可以问自己三个问题以自省:
  • 如果这个视图或操作需要长时响应该怎么办?
  • 如果根本得不到任何响应,应用程序会作何反应?
  • 我有什么办法可以解决这些问题?

对于这些问题,不同的应用会有不同的解决办法,但有些事情非常值得考虑:

1.如果使用API响应或图像超过一次的机会渺茫,就要尽可能地缓存。如果一个信息流无法做到每15分钟更新一次,那就不要在每次启动应用时运行API。
2.在用户需要某些数据之前,就要利用任何机会去下载它,可以后台获取,并将其与缓存数据和已存在的数据相结合。
3.如果你不得不让你的用户等待应用启动,可以考虑在这段加载的时间内播放些有趣的动画,让用户觉得应用卡顿实属大忌。同样地,应该确保及时更新UI,让用户能了解到他的请求是否已经失败。
Conrad Kramer——Workflow创始人

早前我们使用图像选取器去处理问题,但它给人的感觉是,似乎要永远加载下去,而且,内存的使用量会让WatchKit扩展崩溃。在经过多次试验和失败后,我得出了以下几点关于发送图像到Apple Watch上的技巧:
  • 图片的缩略图应适合屏幕的大小;
  • 将图片压缩成JPEG或PNG格式;
  • 调用-[WKInterfaceImage setImageData:],比使用UIImage对象更快,而且可以提高内存使用率。

如果需要使用动态图片,可以使用+[UIImage animatedImageWithImages:duration:]。另外,Apple Watch不支持持续duration属性,如需要,可使用-[WKInterfaceImage startAnimatingWithImagesInRange:duration:repeatCount:]。

James Robert——Media Predict CTO

我在开发Apple Watch应用时犯的第一个错误是尝试把iPhone应用中的图形元素直接搬运到Apple Watch上。最终,我不得不重新考虑设计以让应用在这个有限的环境中更完善。

正如前面Conrad所言,在开发Watch应用时需要更小心谨慎地使用图像。用JPEG压缩的NSData可以获取到合适的图像。但我只是将图像分组进行替换,并对背景颜色和边框进行设置。Watch的屏幕的太小,为了留出足够的空间给文本必须要做出各种努力。

Curtis Herbert——独立开发者&设计师

我在开发Watch版Slopes时犯了一个很大的错误,就是不能对视图进行智能化更新。我会想能不能像iOS版那样随时更新屏幕,但事实却无法让人舒心,这些功能只能在蓝牙开启的情况下才能正常运转。

在运行应用时,如果你注意到控制台日志,就会发现WatchKit在试图删除那些它所认为的重复更新,而一些调用didDeactivate后的更新也会被忽视。苹果十分积极地将更新发送至蓝牙,并且耗能极小,但却没有为WKInterfaceController或WKView提供任何工具去追踪这个状况对用户的影响。

Neil Kimmett——M&S Digital Labs开发者

我们在开发WatchKit App时犯的最大错误就是关于各种文本元素周围的边距。在开发PC和手机端应用时,我们在屏幕边缘和文本之间设置了一个非常漂亮并足够大的间距,但在Watch上,如果使用黑色背景,那手表边框就自然地和文本框之间产生了距离,所以从文本到屏幕边缘的距离就是多余的了。

不设置间距,这在模拟器上看会有些奇怪,但在设备上就显得非常自然。它还有一个额外的好处就是在有限的屏幕资源上可以有你更多的空余地方进行其他操作。

Kristina Thai——Intuit iOS软件工程师

我犯的最大错误之一是尝试在reply dictionary中通过handleWatchKitExtensionRequest自定义类对象来向WatchKit扩展发送信息。当自定义对象输入replyInfo dictionary时还是正常的,但它不能被plist文件序列化,所以会出现错误。如果你尝试发送自定义对象,你将在最后看到这样一条提示错误的信息:
引用
Error Domain=com.apple.watchkit.errors Code=2 "The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]" UserInfo=0x61000006f640 {NSLocalizedDescription=The UIApplicationDelegate in the iPhone App never called reply() in -[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}

因此,自定义对象不能通过plist文件序列化,也可以说iPhone上的应用在handleWatchKitExpensionRequest中调用reply(),即使你尝试这样做。在此,我列出一些可以在iPhone和Watch应用之间进行数据共享的方法:

1.你仍然可以使用reply dictionary去传输一些简单的数据。最初,为了可以在Apple Watch上显示,我的自定义对象需要的是一段字符串,而这些通过应用是很容易传递的,状态代码和快速信息同样如此。你也可以将稍微复杂一些的数据在App中用数组和字典等方式传递。
2.如果你需要自定义对象的所有属性,首先设置App Groups,然后根据需求使用NSUserDefaults或NSFileManager。
3.以上两条有一个非常有趣的共性就是使用NSKeyedArchiver将自定义对象存档后,可以通过reply dictionary来进行传输。示例如下:
  • 在手机端创建reply dictionary

NSMutableDictionary *reply = [NSMutableDictionary new];
MyCustomObject *myObject = <something you need to send>;
reply[@"myKey"] = [NSKeyedArchiver archivedDataWithRootObject: myObject];
NSAttributedString *myString = <some attributed string>;
reply[@"otherKey"] = [NSKeyedArchiver archivedDataWithRootObject: myString];

  • 解压到Apple Watch端

NSData *objectData = replyInfo[@"myKey"];
MyCustomObject *myObject = [NSKeyedUnarchiver unarchiveObjectWithData: objectData];
NSData *stringData = replyInfo[@"otherKey"];
NSAttributedString *myString = [NSKeyedUnarchiver unarchiveObjectWithData: stringData];

文章来源:Realm