html5中文学习网

您的位置: 首页 > ios » 正文

App与Extensions间通信共享数据_IOS开发

[ ] 已经帮助:人解决问题

   最近玩了玩Watch开发,而目前Watch的主要逻辑处理都是放在WatchKit Extension。真正的Host App,也就是WatchKit App只是用来在界面上显示数据的。于是实践了下containing app与app extension之间的通信和数据共享。9eaHTML5中文学习网 - HTML5先行者学习网

  App Groups & Framework9eaHTML5中文学习网 - HTML5先行者学习网

  这两样兵器大家都很熟悉。想要共享数据就需要开启App Groups,给group起一个风骚的名字,这样无论是NSUserDefaults还是NSFileManager都能通过App Groups共享持久层数据了。Core Data也需要NSFileManager提供存储的URL支持,而存取Core Data中的数据需要大量的模板代码,在持久层文件共享之后,代码也应该做到共享,所以将能够重用的代码打包成Framework就显得尤为重要。(除非是为了做毕设凑代码量)9eaHTML5中文学习网 - HTML5先行者学习网

  还是以HardChoice为例,我新建了一个类型为Cocoa Touch Framework的target,名字叫DataKit。新建一个DataAccess.swift文件并将以前AppDelegate.swift中自动生成的Core Data模版代码转移过来。得益于Swift1.2的改进,构造一个线程安全的单例模式变得无比简单:9eaHTML5中文学习网 - HTML5先行者学习网

  private static let instance = DataAccess()9eaHTML5中文学习网 - HTML5先行者学习网

  public class var sharedInstance : DataAccess {9eaHTML5中文学习网 - HTML5先行者学习网

  return instance9eaHTML5中文学习网 - HTML5先行者学习网

  }9eaHTML5中文学习网 - HTML5先行者学习网

  需要注意Swift的权限控制问题,我们需要在暴漏给框架使用者的公开接口和属性前加上public关键字修饰。9eaHTML5中文学习网 - HTML5先行者学习网

  为了实现Core Data持久层共享,需要修改原先的applicationDocumentsDirectory属性:9eaHTML5中文学习网 - HTML5先行者学习网

  lazy var applicationDocumentsDirectory: NSURL = {9eaHTML5中文学习网 - HTML5先行者学习网

  // The directory the application uses to store the Core Data store file. This code uses a directory named "com.yxy.iCloudCoreDataTest" in the application's documents Application Support directory.9eaHTML5中文学习网 - HTML5先行者学习网

  // let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)9eaHTML5中文学习网 - HTML5先行者学习网

  // return urls[urls.count-1] as! NSURL9eaHTML5中文学习网 - HTML5先行者学习网

  var sharedContainerURL:NSURL? = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(appGroupIdentifier)9eaHTML5中文学习网 - HTML5先行者学习网

  return sharedContainerURL ?? NSURL()9eaHTML5中文学习网 - HTML5先行者学习网

  }()9eaHTML5中文学习网 - HTML5先行者学习网

  在这里containerURLForSecurityApplicationGroupIdentifier方法起到了至关作用。9eaHTML5中文学习网 - HTML5先行者学习网

  同样能够共享的代码就是Model层,它们都是NSManagedObject的子类,用于存储Core Data中的数据实例。在把它们从原来的位置拖拽过来时别忘了更改下文件的target:”File inspector”->”Target Membership”,选中DataKit。9eaHTML5中文学习网 - HTML5先行者学习网

  在处理iCloud与Core Data同步数据时,我对NSPersistentStoreCoordinatorStoresWillChangeNotification、NSPersistentStoreCoordinatorStoresDidChangeNotification和NSPersistentStoreDidImportUbiquitousContentChangesNotification这三个数据更新的通知进行了观察和处理,但是写在了persistentStoreCoordinator计算属性的get方法中。现在使用lazy关键字进行惰性加载,导致对这三个数据更新通知的观察延后,这会引发严重的错误。所以需要将那三个addObserverForName(name, object, queue, usingBlock)方法挪到init()方法中,在第一时间观察通知。9eaHTML5中文学习网 - HTML5先行者学习网

  最后在AppDelegate.swift中添加import DataKit,替换掉中的application(application, didFinishLaunchingWithOptions) -> Bool方法中controller.managedObjectContext = managedObjectContext为controller.managedObjectContext = DataAccess.sharedInstance.managedObjectContext,也就是不再使用以前的模板代码中的上下文实例,而是用DataAccess单例中的managedObjectContext。9eaHTML5中文学习网 - HTML5先行者学习网

  同理,applicationWillTerminate(application)方法中的saveContext()也要替换成DataAccess.sharedInstance.saveContext()。9eaHTML5中文学习网 - HTML5先行者学习网

  于是我们也可以在App Extensions中import进来DataKit,进行地存取Core Data中的数据啦。而且用的是同一段代码,同一块数据。简直是同一个世界,同一个梦想啊。9eaHTML5中文学习网 - HTML5先行者学习网

  Container app 与 Extension的通信9eaHTML5中文学习网 - HTML5先行者学习网

  要知道之前做的共享数据只能是主动获取数据,并不能在数据变化时实时获取通知。如果用户在iPhone上更改了数据,我们需要在Watch上实时更改界面上数据的显示。这点NSNotificationCenter是做不到的,因为它只在App内部工作而不会在两个App之间发通知。同样KVO也无能为力,自己手写委托什么的更是别想了(因为我试过了)。直到我在这篇文章找到了救世主,问题迎刃而解:9eaHTML5中文学习网 - HTML5先行者学习网

  CFNotificationCenterGetDarwinNotifyCenter9eaHTML5中文学习网 - HTML5先行者学习网

  这是CoreFoundation库中一个系统级的通知中心,苹果的系统自己也在用它,看清了”Darwin”了没有?哈哈!看了下CFNotificationCenter相关的API,跟NSNotificationCenter有点像。需要用到Toll-Bridge的知识与CoreFoundation相关的类进行桥接,这虽不常用但也不难。还需要注意下个别参数的使用。9eaHTML5中文学习网 - HTML5先行者学习网

  MMWormhole9eaHTML5中文学习网 - HTML5先行者学习网

  更有趣的是几乎同时我也发现了MMWormhole这个开源库,它专门用于在Container app 与 Extension间传递消息。我读了下它的代码,虽然只有一个类,但是依然学到了很多。虽然在我的HardChoice上完全可以只用CFNotificationCenter进行通知就可以了,完全不需要使用MMWormhole来持久化数据和传递数据。但我觉得以后还可能会用到MMWormhole,于是我用Swift1.2重新写了一个Wormhole.swift,放在了DataKit里。9eaHTML5中文学习网 - HTML5先行者学习网

  Swift与CoreFoundation9eaHTML5中文学习网 - HTML5先行者学习网

  原来OC写的两百多行的MMWormhole被我用150行“清新优雅”的Swift代码取代。之所以打上引号是因为Swift与CoreFoundation之间的桥接有些不愉快。因为CoreFoundation中都是C的API,C中的指针和类型转换很出格,有安全隐患。Swift是一门安全的语言,但为了调用由历史原因造成的不安全的C的API,Swift中引入了很多类型来映射C中的类型,参考Interacting with C APIs9eaHTML5中文学习网 - HTML5先行者学习网

  Swift中不用像OC那样使用__bridge和类型转换、内存管理交接,因为这些全都交给Swift了:如果Swift中存在类型映射到C的API所需的参数类型,那么可以直接将其传入API。此外内存管理也归Swift中的ARC统一管理。于是Swift大大简化了与CoreFoundation打交道的过程。9eaHTML5中文学习网 - HTML5先行者学习网

  我们最关心的是指针,UnsafePointer对应了const CType *,UnsafeMutablePointer对应了CType *。当然SwiftType与CType也是对应的:9eaHTML5中文学习网 - HTML5先行者学习网

App与Extensions间通信共享数据 三联

  更多的转换规则,在上面提到的官方文档有很详细的描述,这里只说三个tips:9eaHTML5中文学习网 - HTML5先行者学习网

  在Swift中将self转成UnsafePointer(也就是const void *)只需用这个函数:unsafeAddressOf(self)9eaHTML5中文学习网 - HTML5先行者学习网

  CoreFoundation库中后缀为”Ref”的类在Swift中已经去掉后缀。9eaHTML5中文学习网 - HTML5先行者学习网

  Swift中函数指针被表示为CFunctionPointer,Type就是函数的类型,但还不允许你将Swift写的函数或闭包转化成CFunctionPointer,也就是干脆没提供建立CFunctionPointer实例的方法,只能通过外部引入C的函数。这就涉及到了Swift与OC混编,请戳Swift and Objective-C in the Same Project9eaHTML5中文学习网 - HTML5先行者学习网

  在Framework中混编OC9eaHTML5中文学习网 - HTML5先行者学习网

  我之所以需要做这种破坏工程纯洁性的事儿,是因为要用到下面这个方法来对通知进行观察:9eaHTML5中文学习网 - HTML5先行者学习网

  1func CFNotificationCenterAddObserver(center: CFNotificationCenter!, observer: UnsafePointer, callBack: CFNotificationCallback, name: CFString!, object: UnsafePointer, suspensionBehavior: CFNotificationSuspensionBehavior)9eaHTML5中文学习网 - HTML5先行者学习网

  除了类型为CFNotificationCallback的参数,其余的都好说:9eaHTML5中文学习网 - HTML5先行者学习网

  1typealias CFNotificationCallback = CFunctionPointer Void)>9eaHTML5中文学习网 - HTML5先行者学习网

  于是就回到了CFunctionPointer这块蛋疼地上了,只好在OC里写C函数然后调用之:9eaHTML5中文学习网 - HTML5先行者学习网

  static NSString * const WormholeNotificationName = @"WormholeNotificationName";9eaHTML5中文学习网 - HTML5先行者学习网

  @implementation HelpMethod9eaHTML5中文学习网 - HTML5先行者学习网

  - (instancetype)init9eaHTML5中文学习网 - HTML5先行者学习网

  {9eaHTML5中文学习网 - HTML5先行者学习网

  self = [super init];9eaHTML5中文学习网 - HTML5先行者学习网

  if (self) {9eaHTML5中文学习网 - HTML5先行者学习网

  _callback = wormholeNotificationCallback;9eaHTML5中文学习网 - HTML5先行者学习网

  }9eaHTML5中文学习网 - HTML5先行者学习网

  return self;9eaHTML5中文学习网 - HTML5先行者学习网

  }9eaHTML5中文学习网 - HTML5先行者学习网

  void wormholeNotificationCallback(CFNotificationCenterRef center,9eaHTML5中文学习网 - HTML5先行者学习网

  void * observer,9eaHTML5中文学习网 - HTML5先行者学习网

  CFStringRef name,9eaHTML5中文学习网 - HTML5先行者学习网

  void const * object,9eaHTML5中文学习网 - HTML5先行者学习网

  CFDictionaryRef userInfo) {9eaHTML5中文学习网 - HTML5先行者学习网

  NSString *identifier = (__bridge NSString *)name;9eaHTML5中文学习网 - HTML5先行者学习网

  [[NSNotificationCenter defaultCenter] postNotificationName:WormholeNotificationName9eaHTML5中文学习网 - HTML5先行者学习网

  object:nil9eaHTML5中文学习网 - HTML5先行者学习网

  userInfo:@{@"identifier" : identifier}];9eaHTML5中文学习网 - HTML5先行者学习网

  }9eaHTML5中文学习网 - HTML5先行者学习网

  @end9eaHTML5中文学习网 - HTML5先行者学习网

  然后在Swift中这样写就可以了:9eaHTML5中文学习网 - HTML5先行者学习网

  1CFNotificationCenterAddObserver(center, unsafeAddressOf(self), helpMethod.callback, identifier, nil, CFNotificationSuspensionBehavior.DeliverImmediately)9eaHTML5中文学习网 - HTML5先行者学习网

  在Swift中使用OC写的类本来是一件很easy的事儿,但是到了Framework中就变得不寻常。我在DataKit中新建了HelpMethod类,并建立”DataKit-Bridging-Header.h”文件,将HelpMethod.h头文件引入,然后在DataKit target下的”Build Settings” -> “Swift Complier-Code Generation” -> “Objective-C Bridging Header”下填入”DataKit-Bridging-Header.h”,编译出错:using bridging headers with framework targets is unsupported。9eaHTML5中文学习网 - HTML5先行者学习网

  在stackoverflow上找到了解决方案,于是删除之前的”DataKit-Bridging-Header.h”文件并清除”Build Settings”关于Bridging Header的引用;在DataKit.h添加#import "HelpMethod.h",并在HelpMethod.h文件的 “File inspector”->”Target Membership”中DataKit右侧将”project”修改为”public”(否则会出现include of non-modular header inside framework module ‘DataKit’的编译错误)。9eaHTML5中文学习网 - HTML5先行者学习网

  至此,我们可以在HelpMethod类中实现一个函数指针,并在Wormhole.swift文件中直接使用这个函数指针来为CFunctionPointer类型的参数传值。9eaHTML5中文学习网 - HTML5先行者学习网

  总结9eaHTML5中文学习网 - HTML5先行者学习网

  来个效果图:9eaHTML5中文学习网 - HTML5先行者学习网

1429177850800772.gif

  这是我第一次写Watch的App(废话谁不是第一次),经验并不是很多,也因为Swift1.2还未正式发布,遇到了一些坑。好歹最后克服了,但也丢了贞操(毕竟不是纯Swift的App了)。有不对的地方还请多多指教。随着Swift的不断完善,希望以后能够支持创建CFunctionPointer对象,这样它好我也好。9eaHTML5中文学习网 - HTML5先行者学习网

(责任编辑:)
推荐书籍
推荐资讯
关于HTML5先行者 - 联系我们 - 广告服务 - 友情链接 - 网站地图 - 版权声明 - 人才招聘 - 帮助