八.数据共享

2017-12-07 12:52:31来源:oschina作者:阿元人点击

分享
八.数据共享

共享数据场景:


与其他应用集成(例如,让用户使用Facebook的登陆信息登陆你的应用)。
发布一系列互补的应用。
将用户数据从同一的应用移动到有多个特定用途的应用,检测其是否存在,并在需要时传递控制。
在可用的最佳查看器中打开文档。
8.1 深层链接

iOS9之后用通用链接替换。


//源应用
-(void)openTargetApp{
NSURL *url = [NSURL URLWithString:@"com.yourdomain.app://x-callback-url/quote?ticker=GOOG/%start=2014-01-01&end=2015-12-31"];//1.构造URL
UIApplication *app = [UIApplication sharedApplication];
if ([app canOpenURL:url]){//2.检查应用是否被安装
//弃用openURL [app openURL:url];//3.启动目标应用
[app openURL:url options:[NSDictionary new] completionHandler:^(BOOL success) {

}];
}
}
//跳转的应用
//废弃
//-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{
// return YES;
//}
-(BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options{//4.接受URL目标应用的委托回调

NSString *host = url.host;//5.从URL中提取必要的详细信息
NSString *path = url.path;
NSDictionary *params = [self parseQuery:url.query];
if ([@"x-cakkvacj-url" isEqualToString:host]){//6.处理URL
if ([@"quote" isEqualToString:path]){
[self processQuoteUsingParameters:params];
}
}
return YES;
}
-(NSDictionary *)parseQuery:(NSString *)query{
NSMutableDictionary *dict = [NSMutableDictionary new];
if (query){
//以 & 和 = 作为分隔符解析
NSArray *pairs = [query componentsSeparatedByString:@"&"];//8.解析查询字符串
for (NSString *pair in pairs){
NSArray *kv = [pair componentsSeparatedByString:@"="];
NSString *key = [kv.firstObject stringByRemovingPercentEncoding];
NSString *value = [kv.lastObject stringByRemovingPercentEncoding];
[dict setObject:value forKey:key];
}
}
return [NSDictionary dictionaryWithDictionary:dict];
}
-(void)processQuoteUsingParameters:(NSDictionary *)params{
NSString *ticker = [params objectForKey:@"ticker"];
NSString *startDate = [params objectForKey:@"start"];
NSString *endDate = [params objectForKey:@"end"];//9处理提取的值
}最好使用较短的URL,因为它们的构建速度和解析速度都比较快。
避免基于正则表达式的模式。
优先选择基于查询的URL进行标准解析。
不要在URL中放置任何敏感数据。
不要信任任何传入的数据。始终验证URL。作为附加的措施,可以让应用在传递URL前对数据进行签名,并在处理前验证签名。
使用sourceApplication来标识源。有一个应用白名单非常有用,可以始终信任这些数据。
8.2 剪贴板

官方文档描述:剪贴板试试用于在应用之内或之间交换数据的安全且标准化的机制。许多操作取决于剪贴板,特别是赋值--剪切--粘贴。但你也可以在其他情况下使用剪贴板,例如,在应用之间共享数据时。
剪贴板可以是公共的或私有的。每个剪贴板必须有唯一的名称。
下图显示了一个具有两个项目的剪贴板,每个项目都有多种格式。


具有两种标准格式(RTF和纯文本)内容的文本项。
具有两种标准格式(JPG和PNG)和一种私有格式(com.yourdomain.app.type)图片内容的图片项目,为特定应用所专用。

输入图片说明


共享数据的代码示例:


//使用剪贴板共享数据
-(void)shareToPublicRTFData:(NSData *)rtfData text:(NSString *)text{
[[UIPasteboard generalPasteboard] setData:rtfData forPasteboardType:(NSString *)kUTTypeRTF];//设置已知类型kUTTypeRTF的二进制数据
[[UIPasteboard generalPasteboard] setData:text forPasteboardType:kUTTypePlainText];

[UIPasteboard generalPasteboard].string = text;
[UIPasteboard generalPasteboard].strings = @[text];
}
//假设数据的UTI类型是"com.yourdomain.app.type"
-(void)shareToPublicCustomData:(NSData *) data{
[[UIPasteboard generalPasteboard] setData:data forPasteboardType:@"com.yourdomain.app.type"];//为自定义类型设置二进制数据
}
//共享至自定义命名的剪贴板
-(void)sharePrivatelyCustomData:(NSData *)data{
UIPasteboard *appPasteboard = [UIPasteboard pasteboardWithName:@"myApp" create:YES];//获取具有给定名称的剪贴板。若不存在,新建一个

[appPasteboard setData:data forPasteboardType:@"com.yourdomain.app.type"];//设置自定义剪贴板的数据。
}
//从公共的剪贴板读取
-(NSArray *)readShareStrings{
return [UIPasteboard generalPasteboard].strings;
}
//从命名的剪贴板读取
-(NSData *)readPrivateData{
UIPasteboard *appPasteboard = [UIPasteboard pasteboardWithName:@"myApp" create:YES];

return [appPasteboard dataForPasteboardType:@"com.yourdomain.app.type"];//在自定义剪贴板中检索自定义类型的数据
}

与深层链接相比,剪贴板优点:


具有支持复杂数据(如图像)的能力
支持在多种形式中表示数据,这些形式可以基于目标应用的功能来选择。例如,消息应用可以使用纯文本格式,邮件应用可以使用来自同一剪贴板项目的富文本格式。
即使应用关闭后,剪贴板内容仍然会保留。

最佳实践:


剪贴板本质上是由剪贴板服务进行调解的进程间通信。IPC(Inter-Process Communication)的所有安全规则都适用。
因为不能控制哪个应用会访问剪贴板,所以使用时总是不安全的,除非数据被加密。
不要在剪贴板中使用大量数据。虽然剪贴板支持交换图像以及多种格式,但每个条目不仅消耗内存,也需要额外的时间来读写。
当应用将使用applicationDidEnterBackgroundNotification通知或applicationWillResignActiveNotification通知进入后台时,消除剪贴板。更好的做法是,可以实现UIApplocationDelegate响应的回调方法。通过将item设置为nil。可以消除剪贴板,myPasteboard.items = nil;
为防止任何类型的复制/粘贴,继承UITextView,并在canPerformAction的copy:动作中返回NO。
8.3 共享内容
8.3.1 文档交互

UIDocumentInteractionController类允许应用利用设备上的其他应用打开文档。支持预览,打印,邮寄和复印文档。
UIDocumentInteractionController不是UIViewController的子类,必须配置一个控制器来预览文档
控制器的使用涉及两个方面:发布者和用户


输入图片说明


1.发布者



统一类型标识符:(uniform type identifier,UTI) 是用来唯一标识某一项目类型的文本字符串.

内置的UTI用来标识公共系统对象。例如,文档的public.document,JPEG图像的public.jpeg和纯文本的public.plain-text.


第三方开发人员添加自己的UTI,用于特定应用或专有用途。例如,用于PDF文档的con.adobe.pdf和用于Apple Keynote文档的com.apple.keynote.key 


#import //1.UIDocumentInteraction类,相关类型以及常量定义在MobileCoreServices中
@interface ZJDocumentViewController : UIViewController//2.所在控制器需要实现协议
@property (nonatomic , strong) UIDocumentInteractionController *docController;
@end
@implementation ZJDocumentViewController
- (void)viewDidLoad {
[super viewDidLoad];

}
#pragma mark document Delegate
-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller{//3.此方法提供将要展示子视图的UIViewController,必须实现
return self;
}
-(NSURL *)fileInDocsDirectory:(NSString *)filename{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docsDir = [paths firstObject];
NSString *fullPath = [docsDir stringByAppendingPathComponent:filename];
return [NSURL fileURLWithPath:fullPath];
}
-(void)configureDIControlWithURL:(NSURL *)url uti:(NSString *) uti{//4.使用URL和UTI类型配置控制器
UIDocumentInteractionController *controller = [UIDocumentInteractionController interactionControllerWithURL:url];//5获取URL指向的控制器引用
controller.delegate = self;
controller.UTI = uti;//6指定委托和UTI类型
self.docController = controller;//7设置对控制器的强引用,确保控制器不会过早地被释放
}
#pragma Action
-(IBAction)previewDocument:(id)sender{//预览操作
NSURL *fileURL = [self fileInDocsDirectory:@"sample.pdf"];//8UIDocumentInteractionController对象引用的URL必须是操作系统可访问的

if (fileURL){
[self configureDIControlWithURL:fileURL uti:(__bridge NSString *)kUTTypePDF];
[self.docController presentPreviewAnimat
ed:YES];//预览文档
}
}
-(IBAction)openDocument:(id)sender{//打开PDF操作
NSURL *fileURL = [self fileInDocsDirectory:@"sample.pdf"];
if (fileURL){
[self configureDIControlWithURL:fileURL uti:(__bridge NSString *)kUTTypePDF];
[self.docController presentOpenInMenuFromRect:self.view.frame inView:self.view animated:YES];//显示“在。。。。打开”的菜单,并让用户选择某一应用打开文档
}
}

UIDocumentInteractionController需要一个NSURL来读取内容,它必须指向一个使用文件scheme的本地文件。任何其他scheme将引发异常。


2.消费者
文档的消费者需要两个基本步骤:注册应用支持的文件类型,然后处理文档内容。
要想注册应用支持的文件类型,必须在应用的Info.plist中的文档类型部分配置一下详细信息。


名称:想提供的人类可读的名称。
类型:标准的统一类标识符之一或自定义UTI。
图标:需要一个与文档相关联的图标。
属性:配置其他文档类型属性。

输入图片说明


-(BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary *)options{//接受URL目标应用的委托回调
DDLogDebug("%s src :%@,url:%@", __PRETTY_FUNCTION__,src,url);
return YES;
}
8.3.2 活动

UIActivityViewController提供了一个统一的服务接口,以便共享和执行与应用中数据有关的操作。
使用UIActivityViewController比使用UIDocumentInteractionController更容易,灵活。UIDocumentInteractionController只允许文件URL,但是用UIActivityViewController可以共享一下一种或多种类型。


NSString
NSAttributedString
NSURL
UIImage
ALAsset 这展示由照片应用管理的照片或视频,可以与目标应用共享。
UIActivityItemSource:符合此协议的对象可以共享。这有助于创建可以跨应用共享的自定义对象。
-(void)shareSomeContent{
NSString *text = @"Text to share";
NSURL *url = [NSURL URLWithString:@"http://github.com"];
UIImage *image = [UIImage imageNamed:@"blah"];//1要共享的元素

NSArray *item = @[text,url,image];
UIActivityViewController *ctrl = [[UIActivityViewController alloc]initWithActivityItems:item applicationActivities:nil];
ctrl.excludedActivityTypes = @[UIActivityTypePostToFacebook];//排除不允许的活动类型
[self presentViewController:ctrl animated:YES completion:nil];

}

共享钥匙串:共享钥匙串是在应用间安全分享数据的另一种选择。只有属于相同群组的ID,且使用相同证书签名的应用才能共享数据。
在所有应用中实现单点登录的唯一方法就是使用共享钥匙串
要实现在同一发布者的应用之间共享数据,这是唯一方法,且不需要从用户正在使用的应用调用其他应用


8.4 iOS8扩展

添加应用扩展条目:


输入图片说明
输入图片说明


实现数据共享的:


操作扩展
分享扩展
文档提供者扩展
应用群组

iOS8中添加的新类:


NSExtensionContext: 表示调用扩展时的主应用上下文。
NSExtensionItem: (https://developer.apple.com/documentation/foundation/nsextensionitem): 表示输入项数组中的某一项。NSExtensionItem对象是一个不可变集合,集合中的值代表了项目的不同方向,可通过attachments属性获得。
NSItemProvider:NSExtensionItem对象attachments属性中找到的数据对象,如文本,图像,URL。
8.4.1 配置操作扩展和共享扩展
元数据(info.plist):Xcode将NSExtension->NSExtensionAttributes->NSExtensionActivationRule的值设为TRUEPREDICATE,实质上表示该操作始终可用。可将其改为Dictionary类型,并使用应用扩展键提供更细粒度的控制。
目标产品名称:当创建新扩展时,使用产品名称字段中提供的名称创建新目标。
8.4.2 操作扩展

当创建操作扩展时,Xcode将创建一下的附加内容:


故事板主界面:当用户选择该操作时,故事板UI即将显示出来
ActionViewController类,支持故事板的视图控制器类

渲染源应用共享图像的典型代码:


- (void)viewDidLoad {
[super viewDidLoad];// Get the item[s] we're handling from the extension context.// For example, look for an image and place it into an image view.
// Replace this with something appropriate for the type[s] your extension supports.
BOOL imageFound = NO;
for (NSExtensionItem *item in self.extensionContext.inputItems) {//1.扫描所有的扩展项
for (NSItemProvider *itemProvider in item.attachments) {//2.对于每个项目而言,扫描所有的附件
if ([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]) {//3.检查附件是否为图像类型
// This is an image. We'll load it, then place it in our image view.
__weak UIImageView *imageView = self.imageView;
[itemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *image, NSError *error) {//4如果是,请检索内容
if(image) {
[[NSOperationQueue mainQueue] addOperationWithBlock:^{
[imageView setImage:image];//5因为检索回调可以在非主线程上被调用,所以切换上下文,以便使用UIImage内容更新UIImageview
}];
}
}];imageFound = YES;
break;
}
}

if (imageFound) {
// We only handle one image, so stop looking for more.
break;
}
}
}
8.4.3 共享扩展

创建共享扩展时,Xcode将创建一下附加内容:


故事板主界面
ShareViewController类

ShareViewController与以下生命周期事件挂钩:


内容验证:调用的第一个方法是isContentValid。使用NSExtensionContext验证传入的值,如果数据有效则返回YES,如果无效就返回NO。无论值是什么,该活动将始终被显示,如果内容无效,Post按钮将被禁用。
viewDidLoad:该方法在验证初始内容及加载试图后被调用。如果需要的话,使用textView属性访问UITextView编辑器,从而对文本进行修改。
获取配置项:视图加载后,configurationItems方法会被调用来检索配置项。
内容变化的验证:每当用户更改编辑器中的内容时,调用isContentValid。扩展可以调用validateContent方法来触发重新验证,或调用reloadConfigurationItems方法来重新加载配置项。还可以实现charactersRemaining方法,返回一个非负值,表示剩余的字符数。
取消通知:didSelectCancel方法。
发布通知:didSelectPost方法。
8.4.4 文档提供者扩展

要想读取共享文档的内容,请使用UIDocumentPickerViewController。要呈现一个UI来共享文档,则应该子类化UIDocumentPickerViewController。
使用文档提供者需要iCloud授权。
输入图片说明


1.打开/导入文档


输入图片说明


UIDocumentPickerViewController对象需要配置一下项目:


文档类型:编辑器应用可以支持的UTI类型
模式:必须被配置为Open或Import
委托
//协议UIDocumentPickerDelegate
-(void)clickOpen{
NSArray *types = @[];// 可以处理的UTI类型
UIDocumentPickerViewController *dpvc = [[UIDocumentPickerViewController alloc]initWithDocumentTypes:types inMode:UIDocumentPickerModeImport];
dpvc.delegate = self;
[self.navigationController presentViewController:dpvc animated:YES completion:nil];
}
//委托
-(void)documentPicker:(UIDocumentPickerViewController *)controller didPickDocumentAtURL:(NSURL *)url{
NSData *data = [NSData dataWithContentsOfURL:url];//url是本地文件URL,文档的内容将复制到应用的tmp/DocumentPickerIncoming文件夹中
//处理数据,在编辑器中渲染,让用户编辑
}
-(void)documentPickerWasCancelled:(UIDocumentPickerViewController *)controller{//取消选择文档回调
//获取可以展示一条信息,表明用户没有选择文件
}

2.提供文档
想成为提供文档的数据源,需要以下步骤:


创建UI以帮助用户选择文档
将文档的内容传递给编辑器应用
@interface ZJDocumentPickerExtensionViewController : UIDocumentPickerExtensionViewController
@end
@interface ZJDocumentPickerExtensionViewController ()//1.UIDocumentPickerExtensionViewController向用户提供可用文档和目标的列表
@property (nonatomic , strong) NSArray *allFiles;
@end
@interface HPEntry //2表示远程文件条目的模型类
@property (nonatomic , copy) NSString *fileName;
@property (nonatomic , copy) NSString *serverPath;
@property (nonatomic , assign) NSUInteger *size;
@property (nonatomic , copy) NSString *uti;
@property (nonatomic , copy) NSURL *iconURL;
@end
@implementation ZJDocumentPickerExtensionViewController
- (void)viewDidLoad {
[super viewDidLoad];
//从服务器检索文件的元数据并更新
//UITableview是很好的选择
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath{
HPEntry *selected = [self.allFiles objectAtIndex:indexPath.row];
//如果需要从服务器下载内容 如果文件内容在服务器上,则必须由应用扩展下载。最终的内容必须来自本地URL
NSURL *localFileURL = [self.documentStorageURL URLByAppendingPathComponent:selected.fileName];//将内容保存到self.documentStorageURL文件夹中
[self dismissGrantingAccessToURL:localFileURL];//通知操作系统,编辑器应用必须被授予使用该文件的权限

}
@end
8.4.5 应用群组

虽然扩展总是与应用捆绑在一起,但它在自己的进程中运行,并有自己的数据沙箱。


输入图片说明


因为应用扩展在自己的沙箱中运行,所以它因此无法直接访问由容器应用直接存储的数据(文档文件夹,用户默认数据,缓存文件夹,Core Data,SQLite,等等)


输入图片说明
创建一个共享沙箱,容器应用和应用扩展都可以访问它。应用群组支持在多个应用之间共享数据---但与共享钥匙串类似,应用必须使用相同的证书进行签名。


输入图片说明


-(void)sharedDataUsingAppGroups{
NSString *sharedGroupId = @"group.com.m10v.hperf";//1群组ID;必须与Capabilities中提供的匹配
NSUserDefaults *defs = [[NSUserDefaults alloc]initWithSuiteName:sharedGroupId];//2初始化NSUserDefaults
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSURL *groupFolder = [fileMgr containerURLForSecurityApplicationGroupIdentifier:sharedGroupId];//3获取共享文件夹

}

相关文章

    无相关信息

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台