八.数据共享

八.数据共享

共享数据场景:

  • 与其他应用集成(例如,让用户使用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<UIApplicationOpenURLOptionsKey,id> *)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)的所有安全规则都适用。https://zh.wikipedia.org/wiki/行程間通訊
  • 因为不能控制哪个应用会访问剪贴板,所以使用时总是不安全的,除非数据被加密。
  • 不要在剪贴板中使用大量数据。虽然剪贴板支持交换图像以及多种格式,但每个条目不仅消耗内存,也需要额外的时间来读写。
  • 当应用将使用applicationDidEnterBackgroundNotification通知或applicationWillResignActiveNotification通知进入后台时,消除剪贴板。更好的做法是,可以实现UIApplocationDelegate响应的回调方法。通过将item设置为nil。可以消除剪贴板,myPasteboard.items = nil;
  • 为防止任何类型的复制/粘贴,继承UITextView,并在canPerformAction的copy:动作中返回NO。

8.3 共享内容

8.3.1 文档交互

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

输入图片说明

1.发布者

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

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

#import <MobileCoreServices/MobileCoreServices.h>//1.UIDocumentInteraction类,相关类型以及常量定义在MobileCoreServices中
@interface ZJDocumentViewController : UIViewController<UIDocumentInteractionControllerDelegate>//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<UIApplicationOpenURLOptionsKey,id> *)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类型,并使用应用扩展键提供更细粒度的控制。http://www.bijishequ.com/detail/50781
  • 目标产品名称:当创建新扩展时,使用产品名称字段中提供的名称创建新目标。

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 ()<UITableViewDelegate , UITableViewDataSource>//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获取共享文件夹
	
}

转载于:https://my.oschina.net/u/2319073/blog/1584871

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值