文章目录
  1. 1. 前言
  2. 2. App Extension
  3. 3. App Extension原理
  4. 4. Share Extension
  5. 5. 第一次启动
  6. 6. Info.plist
  7. 7. ShareViewController
  8. 8. App Group

前言

这篇文章在写作计划中原名是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文档,可以用下面这个值:
1
2
3
4
5
6
7
8
9
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, 返回值必须是[]!,个人理解[AnyObject]!有待改进
  • public func reloadConfigurationItems()
    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 ExtensionContainer App也是不能直接互相访问资源的。如果我们想要在App Extension中,使用一些Container App中的资源,比如用户名密码,或者是token,我们就必须先要创建一个共享域,这个共享域就是App Group

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

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

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

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

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

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

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

1
let shareData = NSUserDefaults(suiteName: "group.YourExtension")!.objectForKey("YourToken")
文章目录
  1. 1. 前言
  2. 2. App Extension
  3. 3. App Extension原理
  4. 4. Share Extension
  5. 5. 第一次启动
  6. 6. Info.plist
  7. 7. ShareViewController
  8. 8. App Group