Swift使用Share Extension实现更多的内容分享

前言

这篇文章在写作计划中原名是iOS 使用Share Extension实现更多的内容分享,本应该是在我的内容分享系列文章中完成的,由于之前换公司的适应和新项目的紧张,直到今天才有时间继续未完成的作业。之所以把他称之为Swift 使用Share Extension实现APP之间的内容分享,主要原因就是这个demo是由Swift语言写的,和OC语言非常的不同。而且Swift语言越来越普及,我们在新项目中使用的就是Swift语言,因此希望大家对Swift语言有更多的了解。

App Extension

很多iOS新人,对Share Extension并不清楚,因此在进入正题之前,我觉得有必要描述一下Share Extension的来头。苹果在发布iOS 8时,给开发者提供了一套强大的应用扩展机制,称之为App Extension,这套机制不仅可以在iOS 系统上工作,在Mac系统上同样可以工作。

苹果官方文档中,这样描述App Extension, "An app extension lets you extend custom functionality and content beyond your app and make it available to users while they’re interacting with other apps or the system. ",大概意思是,App Extension是被用来集成到系统或者其他APP中,从而扩展你自己的APP的功能和内容。

这套扩展机制提供了多个类型的App 扩展,下面有几种常用的扩展介绍:

  • Action Extension,用在UIActivityViewController中的第二行的Action List,对这点不熟悉的可以去看我的这篇文章: 通过UIActivityViewController实现更多分享服务
  • Custom Keyboard,提供给开发者自定义键盘的权限,这点猜测苹果也是被逼无奈了
  • Document Provide,提供文档的远程存取服务
  • Photo Editing,提供照片和视频的编辑服务
  • Today,提供在Toady Widget里面展示应用数据的服务
  • Share,用在UIActivityViewController中的第一行的APP List提供iOS实体内容的分享服务

App Extension原理

App Extension不同于一个APP,但是App Extension必须依托于一个APP,这个APP一般称之为Container App (宿主应用)。如果想要开发和发布一个App Extension,首先我们需要开发和发布一个App。尽管如此,App Extension也不会依赖于APP,它是一个独立的二进制包并且可以独立运行。而用户想要使用这个App Extension就需要在系统或者其他APP中,通过特定的操作打开,这时的APP称为Host App(主应用)

App Extension的生命周期不同于APP,是有固定的流程,由系统启动并且关闭的,下图是一个周期图:

app_extensions_lifecycle_2x.png

在上面的周期图中,一个App Extension需要做的就是作为一个桥梁,将Container AppHost App连接起来进行交互,而这个交互流程针对Container AppHost App是不一样的。App Extension可以和Host App直接交互,因为我们需要通过Host App唤起App Extension,因此他们的交互流程是这样的:

simple_communication_2x.png

而相反地,App Extension并不能和Container App直接交互,因为App Extension是不依赖于APP,可以独立运行的,而我们通过App Extension只可以通过固定的几个API来和Container App分享内容、代码,或者是在Today Extension中,我们可以通过URL Scheme的方式打开Container App,因此他们的交互流程是这样的:

detailed_communication_2x.png

Share Extension

上面讲了App Extension的概念和原理,下面我们要进入今天的正题,也就是我们需要创建一个Share Extension来实现我们的文件分享。首先我们需要一个Container App,这里我们仍然使用分享系列文章中之前的Demo应用:ZSDocumentInteractionTest,因为这个Demo中包含UIActivityViewController,我们可以把这个Demo APP既当做是Container App,也当做是Host App

首先我们需要通过New一个target的方式,来创建一个Share Extension的模板:

屏幕快照 2016-06-27 11.30.45.png

选择Application Extensions中的Share Extension模板,并且命名为“ZSShareExtension”, 填入名称并且创建之后,选择激活就可以了:

屏幕快照 2016-06-27 11.33.27.png

此时,我们就可以在工程目录中看到创建好的标准的模板文件了,我们可以到包含了一个info.plist文件,一个默认的MainInterface.storyboard文件和一个默认的ShareViewController作为主要显示的UI。除此之外,我们还可以看到在Products目录下多了一个ZSShareExtension.appex的运行程序,由此我们可以更加直观的了解,App Extension是一个可以独立运行的程序:

屏幕快照 2016-06-27 11.36.53.png

第一次启动

我们创建好模板文件之后,先来看一下启动Share Extension的效果,首先我们需要选中ZSShareExtension这个target,并且运行,这个时候,会提示让我们选择一个Host App,我们选择了哪个APP作为Host App,就必须使用这个APP来唤起Share Extension,从而达到可以调试的效果。

屏幕快照 2016-06-27 11.55.11.png

启动之后,进入到我的文件中,这里我有各种类型的文档,打开其中一个文档之后,选择用其他应用打开,然后就可以看到我们创建的这个ZSShareExtension啦!同时我们可以看出它和Container App显示的图标完全一样的。

IMG_0746.PNG

而如果我们点开ZSShareExtension就可以看到Share Extension的默认UI界面,一个Cancel按钮,一个Post按钮,一个预览图,因为打开的是图片,这个预览图就是该图片, 因为我们是第一次启动,没有作任何的配置,所以ZSShareExtension干不了任何事情,点PostCancel是一个效果,这个Share Extension就被系统终止掉了:

屏幕快照 2016-06-27 13.59.20.png

Info.plist

我们可以试着打开png图片,pdf文件,doc文件等等,发现都可以找到ZSShareExtension这个Share Extension,而如果我只想要pdf文档或者png图片通过这个Share Extension上传到云服务器上,那就需要额外的配置,而这些配置就是在Info.plist文件中。

每一个App Extension和APP一样,都包含一个Info.plist文件,用来描述Share Extension的基本配置信息,这里我们需要用的最重要的俩个属性就是CFBundleDisplayNameNSExtension
前者代表的Share Extension的显示名称,后者是一个Dictionary类型的属性,包含了Share Extension几个主要的配置信息。

屏幕快照 2016-06-27 11.47.04.png

如上图中,我们可以看到,在NSExtension中,有下面几个常用属性:

  • NSExtensionPointIdentifier 这个属性标明了这是哪种类型的App Extension,这里我们显示的是com.apple.share-services,表示这是一个Share Extension,这个属性不需要我们修改
  • NSExtensionMainStoryboard 这个属性表示主程序的storyboard,如果我们自定义storyboard的话,需要改这个值
  • NSExtensionAttributes 很显然这是一些扩展的属性集,也是我们开发者主要关注的,这里面包含了我们可以设置的几个值,下面是常用的一些值,其他的可以从官方文档中看到,地址是 App Extension Keys
    • NSExtensionActionWantsFullScreenPresentation
      一个布尔值,表示是否以全屏modal的样式展示

    • NSExtensionActivationDictionaryVersion
      iOS 9新加的key,number值,特指一个app extension激活的版本号,值为1或2,1代表app extension可以处理所有的host app提供的文档类型,2代表仅支持最少一种类型,意味着可能不会在host app中的UIActivityViewController中显示

    • NSExtensionActivationRule
      指定app extension支持的数据类型,包含以下值,这是一个对开发者来说尤为重要的属性,我们在这里过滤数据类型

      屏幕快照 2016-06-27 14.44.08.png

      同时,如果这里提供的属性并不能完全符合我们的需求,我们还可以自定义NSPredicate值作为过滤规则,代表性的有,如果我们只想使用pdf文档,可以用下面这个值:

      SUBQUERY (
        extensionItems,
        $extensionItem,
        SUBQUERY (
            $extensionItem.attachments,
            $attachment,
            ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "com.adobe.pdf"
        ).@count == $extensionItem.attachments.@count
      
    ).@count == 1
    
    

ShareViewController

在定义好Info.plist文件后,我们就需要在ShareViewController文件中完成我们的功能,我们可以看到ShareViewController是继承自SLComposeServiceViewController的,那我们就可以看一下SLComposeServiceViewController类中常用的属性和方法:

  • public var textView: UITextView! { get }
    输入文字的textView,只读属性,默认被创建并且delegate为ShareViewController
  • public func presentationAnimationDidFinish()
    ShareViewController展示完成时被调用,生命周期比较早,一般用来执行预加载任务
  • public var placeholder: String!
    textView未输入文字时的占位字符
  • public func didSelectPost()
    点击Post按钮时调用,这里就要做上传操作
  • public func didSelectCancel()
    点击Cancel按钮时调用,一般做一些清理工作
  • public func isContentValid() -> Bool
    决定了是否可以点击Post按钮,这里可以做一些内容的过滤限制等任务
  • public var charactersRemaining: NSNumber!
    输入字符限制,一般和isContentValid配合使用
  • public func configurationItems() -> [AnyObject]!
    展示页下面可以点选的item, 返回值必须是<SLComposeSheetConfigurationItem>[]!,个人理解[AnyObject]!有待改进

public func pushConfigurationViewController(viewController: UIViewController!)
public func popConfigurationViewController()

reload操作,push和pop操作
* ```    public func loadPreviewView() -> UIView!```
显示预览视图,如果是图片和Web的话,默认展示图片
* ```     public var autoCompletionViewController: UIViewController!```
一个用来替代`SLComposeSheetConfigurationItem`列表的视图,由系统控制

#App Group
尽管`App Extension`的bundle包含在`Container App`的bundle中,但是`App Extension`和`Container App`也是不能直接互相访问资源的。如果我们想要在`App Extension`中,使用一些`Container App`中的资源,比如用户名密码,或者是token,我们就必须先要创建一个共享域,这个共享域就是`App Group`。

首先我们选择`Container App`的target,并且点开`Capabilities`,可以看到`App Groups`,这时我们还没有开启

![屏幕快照 2016-06-27 15.37.12.png](http://upload-images.jianshu.io/upload_images/1216322-f567f5c15fab3993.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

右侧点击打开后,会默认启动,并添加`App Group`到entitlements文件,我们点击+号,可以新建一个`share container(共享域)`,注意名称必须以`group.`开头,而且注意`Apple ID`必须是开发者,拥有足够的权限。

![屏幕快照 2016-06-27 15.39.05.png](http://upload-images.jianshu.io/upload_images/1216322-cca30b2ef77f22b2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

创建完成之后,我们选择`Share Extension`的target,同样是上述步骤开启`App Groups`,注意不同之处就在于这里仅仅需要选择上面创建的`share container`即可。然后我们就可以在代码中,通过下面的形式,将数据写入到共享域中:

NSUserDefaults(suiteName: "group.YourExtension")!.setObject(YourData,forKey:YourKey)


获取共享域资源时,同样如此:

let shareData = NSUserDefaults(suiteName: "group.YourExtension")!.objectForKey("YourToken")```

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容

  • 通过iOS 8app extensions,我们可以选择多种方式去分享我们app的功能。Document Prov...
    _浅墨_阅读 7,039评论 4 12
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,097评论 18 139
  • 136.泛型 泛型代码让你可以写出灵活,可重用的函数和类型,它们可以使用任何类型,受你定义的需求的约束。你可以写出...
    无沣阅读 1,393评论 0 4
  • 你好,未与你相遇之前,请允许我称呼你为“达西先生”。冒昧的问一句,你……你现在在哪里呢?你的城市是晴天还是阴天?你...
    奋斗的小青年有很多烦恼阅读 565评论 0 0
  • 误区一:以自己为中心 确实,绝大多数人判断事情都是以自身的背景或是周围环境为依据,但有些时候,以你为中心根本不可靠...
    2b167bc664a9阅读 449评论 0 1