safari浏览器 extensions 怎么写

Canisbos Safari Extensions
All My Extensions
Quick introductions to all of my Safari extensions, sorted according to whim.Note: All of these extensions require Safari 5.0 or higher, except where noted.Quickly access your
links in a convenient popover. You can view your links in a searchable list, or just open all your unread links in tabs with one click.Search and open your Pinboard, Delicious, or Kippt bookmarks without leaving the page you&re on. Type Command-J (or any other hotkey you choose) to open the Cloudmarks search bar. Start typing, and Cloudmarks will instantly show you a list of bookmarks that match what you type in name, description, tags, or URL. Then, just highlight the bookmark you want and press Enter to open it. You need to have a , , or
account to make use of Cloudmarks.Restyle Safari Reader as you like. If you like Safari Reader but wish you had more control over how it looks, CustomReader is for you. that doesn&t require Safari 6. Instead of modifying Safari Reader, it generates its own reading layout from contents downloaded from Diffbot.Add any search engine to Safari. PopSearch compensates for Safari&s lack of support for searching from the address bar with search engines other than Google, Bing, and Yahoo. If you use Safari 6, PopSearch lets you do an address bar search with another engine by prefixing the search with a short hotkey, such as &w& for Wikipedia. If you&re still using Safari 5, you can use PopSearch&s own popup search bar by type Command-K or clicking the toolbar button. PopSearch comes with several predefined engines, and it&s easy to add your own.Restyle any web page quickly and conveniently. Quickstyle is a Safari extension to change the appearance of any web page by adding your own CSS rules to it. Quickstyle features a convenient &selector helper& that eases the task of finding the right CSS selector for the rule you want to create, and a built-in stylesheet editor so you can create and edit your rules right on the page. It&s a lot easier and faster than using Safari&s Web Inspector and editing an external User CSS file.Lets you save and automatically run arbitrary bits of JavaScript code on selected web pages. Quickscript is similar in purpose to the Firefox extension Greasemonkey, but is much simpler and less powerful. Enter a script along with a URL pattern to specify what pages it will run on. (You can save as many such scripts as you want.) Whenever you visit a page that matches one of your URL patterns, the corresponding script(s) will run automatically.Take control of links on web pages. When you open a link in a new tab, LinkThing can position the tab on either side of the current tab instead of the end of the tab bar. You can have links open in new tabs by default (with just a plain click), and this can be set independently for offsite and onsite links, globally and for any particular site. You can also have LinkThing treat a right-click on a link as a Command-click. Lastly, you can conveniently add links to your Instapaper account using the context menu.Exercise more control over tabs. Control tab positioning and assign custom keyboard shortcuts for common tab operations. Supported operations are: open a new tab, close the current tab, switch to the last active tab, cycle through all tabs in activation order, switch to the left or right tab, duplicate the current tab, and move the current tab left or right on the tab bar. You can assign up to three hotkeys for each operation. You can choose where new tabs are positioned and which tab becomes active when you close the current tab. Lastly, you can assign hotkeys to your favorite web pages or bookmarklets for quick access to them.Delete, or &nuke&, any element on a web page quickly and easily. To use, click the unwanted element and press the &D& key while keeping the mouse button down. Deletion deleted elements come back when you reload the page.Perform web searches in a small side window and have result links open in your main Safari window. Open Factotum by clicking the toolbar button or typing a configurable hotkey (default: Command-Shift-F). Search results are listed in Factotum&s window, but when you click a result link, it opens in your main window (in a new tab, if you choose). Supported search engines are Google, Bing, DuckDuckGo, Wikipedia, Yahoo!, and Twitter.Open Notepad.cc in a small side window. Click ccNotepad&s toolbar button or type the configurable hotkey (default: Command-Shift-O) to open Notepad.cc, the lightweight note-taking site, in a small window next to your current Safari window. You can quickly paste text selections into your notepad by typing the hotkey after selecting some text. ccNotepad will remember your notepad URL and reuse it the next time.Quickly open a FreshBooks timer window. Great for people who use FreshBooks, the awesome web invoicing and time tracking app for freelancers. A click of the toolbar button opens the window and logs you in.Preserve direct links in Google search results. This extension stops Google from replacing direct links in search results with indirect, click-tracking ones. It can also rewrite image search links to open images directly.Make the header of a tall table stay on screen even as the top of the table scrolls out of sight. Good for people with lousy short-term memory, like me.Save your place on a long web page and come back to it later with a special bookmark. No more hunting for the place where you left off. You can automatically save &placemarks& to Instapaper, Read It Later, Pinboard, or Delicious, or manually save them as Safari bookmarks.&Naturalizes& scrolling with your keyboard. This extension reverses the scrolling direction of the Up/Down and Page Up/Page Down keys on your keyboard when viewing web pages.Reveal hidden passwords in standard password fields when you mouse over them.Enables navigating up a web site&s hierarchy by typing ⌘⌥&. Provides a quick way to go up to the &parent& of the current web page.Disables Safari&s &page cache&. The page cache causes problems for many extensions. If you&ve noticed some of your extensions& features not working after you use the Back button, NoPageCache can help.有钱没钱回家过年,提前祝大家春节快乐!
一年一度的农历春节即将来临,狮子威威在此代表威锋网及威锋网所有工作人员恭祝天下的...
在威锋十周年之际,第九届『WeiPhone 摄影大赛』拉开了帷幕,现在第一季开始征稿了!
黑色的AirPods,和黑色的iPhone更搭哦。
目前最新版本的越狱支持除iPhone 7、iPhone 7 Plus和iPad Air 2外的所有64位设备。
英国运营商 Three 更新为 iPhone 带来了原生 WiFi 通话。
亚马逊较为宽松的认证环境已经催生了250多款兼容Alexa产品,而拿到苹果认证的仅有100...
据《金融时报》网站报道,2016财年营收、利润双双缩水后,外界预计当地时间周二发布财...
《DROP'd》首先在画面上就一改以往的昏沉灰暗,使用了相当明亮的色调,一下子让人倍感...
《米亚夺宝传奇》采用了卡通的设计风格,而与此同时,多种色彩的合理搭配也让游戏的场...
科学原本应该是推动人类发展的力量,但是如果被坏人掌握的话,那对于人类来说就会是一...
这款游戏集快节奏操控与精选音轨于一身,试图打造与众不同的游戏体验。
国粹不愧是国粹,独特的皮影效果令人耳目一新!
经过最近几年的发展,MOBA 类游戏现在已经基本形成了其固定的游戏模式,审美疲劳之下...
游戏在中国区的本周新游推荐里不是太显眼,但当你抱着试一试的心情购买下载之后,就会...
黑色的AirPods,和黑色的iPhone更搭哦。
亚马逊较为宽松的认证环境已经催生了250多款兼容Alexa产品,而拿到苹果认证的仅有100...
周四的时候,美国商标专利局(USPTO)披露了一份来自苹果的“升华器 / 汽化器”专利申...
要知道新功能的作用,以及一些限制。
此前有用户指出,苹果因为收到的差评太多而删除部分评论。
安全摄像头 Dropcam 公司联合创始人和前 CEO Greg Duffy 将离开谷歌,并加入苹果,根...
希望更多我们喜欢的电影或者游戏,能推出相应主题的iPhone配件~
数据居然是不准确的?不管醉没醉,我们都不能酒后驾车!
为什么关于Safari的什么扩展都没有了?
注册时间 最后登录
在线时间2189 小时 UID
主题帖子人气
青苹果, 积分 198, 距离下一级还需 2 积分
以前都是直接通过safari extensions选项进去这个页面选扩展安装,现在打开页面什么扩展都没有了,搜索也搜不到,什么情况?
MC975,i5, 8G, SSD 256G,15'inch Retina
ME088,i5,16G,SSD 256G
注册时间 最后登录
在线时间2189 小时 UID
主题帖子人气
没有人遇到这个问题吗?
MC975,i5, 8G, SSD 256G,15'inch Retina
ME088,i5,16G,SSD 256G
威锋旗下产品
Hi~我是威威!
沪公网安备 29号 | 沪ICP备号-1
新三板上市公司威锋科技(836555)
增值电信业务经营许可证:
Powered by Discuz!2764人阅读
注:转自/xdream86/p/3855932.html
iOS 8&Extensions
一、扩展概述
扩展(Extension)是iOS 8中引入的一个非常重要的新特性。扩展让app之间的数据交互成为可能。用户可以在app中使用其他应用提供的功能,而无需离开当前的应用。
在iOS 8系统之前,每一个app在物理上都是彼此独立的,app之间不能互访彼此的私有数据。
而在引入扩展之后,其他app可以与扩展进行数据交换。基于安全和性能的考虑,每一个扩展运行在一个单独的进程中,它拥有自己的bundle,&bundle后缀名是.appex。扩展bundle必须包含在一个普通应用的bundle的内部。
iOS 8系统有6个支持扩展的系统区域,分别是Today、Share、Action、Photo Editing、Storage Provider、Custom keyboard。支持扩展的系统区域也被称为扩展点。
Today Widget
对于赛事比分,股票、天气、快递这类需要实时获取的信息,可以在通知中心的Today视图中创建一个Today扩展实现。Today扩展又称为Widget。
在iOS 8之前,用户只有Facebook,Twitter等有限的几个分享选项可以选择。如果希望将内容分享到Pinterest,开发者则需要一些额外的努力。在iOS 8中,开发者可以创建自定义的分享选项。
action在所有支持的扩展点中扩展性最强的一个。它可以实现转换另一个app上下文中的内容。苹果在WWDC大会上演示了一个Bing翻译动作扩展,它可以将在Safari中选中的文本翻译成不同的语言。
Photo Editing
在iOS 8之前,如果你想为你的照片添加一个特殊的滤镜,你需要进入第三方app中,这个过程是相当繁琐的。在iOS 8中,你可以直接在Photos中使用第三方app,如Instagram,VSCO cam、Aviary提供的Photo Editing扩展完成对图片的编辑,而无需离开当前的app。
Storage Provider
Storage Provider让跨多个文件存储服务之间的管理变得更简单。类似Dropbox、Google Drive等存储提供商通过在iOS 8中提供一个Storage Provider扩展,app直接可以使用这些扩展检索和存储文件而不再需要创建不必要的拷贝。
Custom Keyboard
苹果公司在2007年率先推出了触摸屏键盘,但一直没多大改进。在这一方面,Android则将键盘权限开放给了第三方开发者,所以出现了许多像Swype,SwiftKey等优秀的键盘输入法。在iOS 8中,苹果终于将键盘权限开发给了第三方开发者,自定义键盘输入法可以让用户在整个系统范围内使用。
二、创建扩展与发布扩展
在创建扩展之前,你需要创建一个用来包含扩展的常规的app项目。这个包含扩展的app被称为containing app。在创建好containg app之后,选择File-&New-&Target菜单,从弹出的对话框中选择一个适当的扩展目标模板。每一个扩展目标模板都包含了与扩展点相关的文件和设置。一个containing app可以包含多个不同类型的扩展。
每一个扩展目标模板包含一个头文件和实现文件,一个Info.plist文件,以及一个storyboard文件。Info.plist文件包含了对扩展的配置信息,其中最重要的键是NSExtension。下面列出了一个NSExtension可能包含的常用键值对。
&key&NSExtension&/key&
&&& &key&NSExtensionAttributes&/key&
&&& &dict&
&&&&&& & &&&&key&NSExtensionActivationRule&/key& &!--1--&
&&&&&& && &&&dict&
&&&&&&&&&&& &&& &key&NSExtensionActivationSupportsImageWithMaxCount&/key&
&&&&&&&&&&&&&&& &integer&10&/integer&
&&&&&&&&&&&&&&& &key&NSExtensionActivationSupportsMovieWithMaxCount&/key&
&&&&&&&&&&&&&&& &integer&1&/integer&
&&&&&&&&&&& && &&&/dict&
&&&&&&&&&&& &key&NSExtensionJavaScriptPreprocessingFile&/key& &!--2--&
&&&&&&&&&&& &string&MyJavaScriptFile&/string&
&&&&&& &&& &key&NSExtensionPointVersion&/key&
&&&&&& &&& &string&1.0&/string&
&& &&& &/dict&
&&&& &key&NSExtensionMainStoryboard&/key&& &!--3--&
&&&& &string&MainInterface&/string&
&& &&& &key&NSExtensionPointIdentifier&/key&& &!--4--&
&&& && &string&com.apple.ui-services&/string&
&&&& &key&NSExtensionPrincipalClass&/key&& &!--5--&
&& &&& &string&ActionViewController&/string&
1)&&&NSExtensionActivationRule定义了当前的扩展支持的数据类型及数据项个数,例如当前的设置只支持图片格式和视频格式的数据,并且最多不超过10张图片和1个视频。
2)&&&NSExtensionJavaScriptPreprocessingFile用于配置与脚本交互的JS脚本文件的名字。
3)&&&NSExtensionMainStoryboard配置扩展的Storyboard文件名。
4)&&&NSExtensionPointIdentifier用于表示扩展点,每一个扩展点拥有一个唯一的名字。
5)&&&NSExtensionPrincipalClass配置当扩展启动时,扩展点首先要实例化的类
为了将扩展提交苹果商店,你需要提交你的containg app。并且需要注意,除了扩展必须包含功能以外,同时containg app还需要提供一些功能,而针对OS X平台的扩展则无此限制。当用户安装了你的containg app,containg app中包含的扩展也会一同被安装。
三、理解扩展如何运作
在安装扩展之后,扩展并不会自动运行,用户必须执行特定的操作来启用扩展。如果是Today扩展,用户可以在通知中心的Today视图中编辑启用扩展。如果是自定义键盘扩展,用户需要在系统设置的通用选项下的键盘选项中启用自定义键盘扩展。而如果是Share扩展,用户只需点击系统提供的分享按钮,即可在分享列表中找到分享扩展。
一个扩展并不是一个app,它的生命周期和运行环境不同于普通app。在生命周期方面,扩展的生命周期从用户在另一个app中选择了扩展开始,一直到扩展完成了用户的请求生命周期结束。在运行环境方面,扩展的限制要比普通app更严格,扩展的可用内存上限以及可用的API都比普通app要少。严格限制扩展的内存是因为在同一时间可能会有多个扩展同时运行,如Widget扩展。如果API声明包含NS_EXTENSION_UNAVAILABLE宏,则此API在扩展中将不可用,常见的API如:
+ (UIApplication *)sharedApplication&NS_EXTENSION_UNAVAILABLE_IOS(&Use view controller based solutions where appropriate instead.&);
调用扩展的应用称为host app,对于Widget扩展,host app就是Today。host app会在扩展的有效生命周期内定义一个扩展上下文。通过扩展上下文,host app可以和扩展互传数据。注意,扩展只和host app直接通信,扩展与containg app以及containing app与host app之间不存在通信关系,如果扩展需要打开containg app,则通过自定义URL scheme方式实现,而不是直接向containg
app发送消息。三者的关系见下图:
扩展是一个单独的个体。扩展拥有独立的target,独立的bundle文件,独立的运行进程,独立的地址空间。这意味着即使你的containing app不在运行,系统也可以启动扩展。或者你的containing app处于挂起状态,同样不会影响扩展的运行。所以系统可以单独对扩展执行优化。扩展与containg app的关系:
四、设计扩展过程中常见的几个问题
1. containg app与扩展如何通过扩展上下文互传数据
在iOS 8中,UIViewController新增了一个扩展上下文属性extensionContext。来处理containing app与扩展之间的通信,上下文的类型是NSExtensionContext。假设你现在需要在host app中将一张图片传递给扩展做滤镜处理,host app中的代码如下:
&&& UIActivityViewController *activityViewController = [[UIActivityViewController alloc] initWithActivityItems:@[[self.imageView image]] applicationActivities:nil];
&&& [self presentViewController:activityViewController animated:YES completion:nil];
当用户在弹出的Action列表中选择了扩展,扩展将被启动,然后在扩展的viewDidLoad方法中,通过extensionContext检索host app传回的数据项。扩展中的代码如下:
- (void)viewDidLoad {
&&& [super viewDidLoad];
&&&&NSExtensionItem&*imageItem = [self.extensionContext.inputItems firstObject];
&&& if(!imageItem){
&&&&NSItemProvider&*imageItemProvider = [[imageItem attachments] firstObject];
&&& if(!imageItemProvider){
&& //&检查是否包含文本
&&& if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){
&&&&&&& [imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage&options:nil completionHandler:^(UIImage *image, NSError *error)
&&&&&&&&&&& if(image){
&&&&&&&&&&&&&&& dispatch_async(dispatch_get_main_queue(), ^{
&&&&&&&&&&&&&&&&&&& self.imageView.image =
&&&&&&&&&&&&&&& });
&&&&&&&&&&& }
&&&&&&& }];
上述代码中,extensionContext表示一个扩展到host app的连接。通过extionContent,你可以访问一个NSExtensionItem的数组,每一个NSExtensionItem项表示从host app传回的一个逻辑数据单元。你可以从NSExtensionItem项的attachments属性中获得附件数据,如音频,视频,图片等。每一个附件用NSItemProvider实例表示。上述代码中NSItemProvider的loadItemForTypeIdentifier实例方法的第一个参数是(NSString
*)kUTTypeImage,如果你需要处理的是文本类型,参数则为(NSString *)kUTTypeText,相应的处理代码则变成:
if([itemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeText]){
&&& [imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeText&options:nil completionHandler:^(NSAttributedString *string, NSError
&&&&&&& if (string) {
&&&&&&&&&&& //&在这里处理文本
当扩展处理完host app传回的图片数据后,它需要将处理好的数据再传给host app。在扩展中的代码如下:
-(IBAction)done:(id)sender{
&&& NSExtensionItem* extensionItem = [[NSExtensionItem alloc] init];
&&& [extensionItem setAttachments:@[[[NSItemProvider alloc] initWithItem:[self.imageView image] typeIdentifier:(NSString*)kUTTypeImage]]];
&&& [self.extensionContext completeRequestReturningItems:@[extensionItem] completionHandler:nil];
最后一步是host app接收来自扩展传回的数据,在host app中的代码如下:
[activityViewController&setCompletionWithItemsHandler:^(NSString *activityType, BOOL completed, NSArray *returnedItems, NSError * error){
&&&&&&& if([returnedItems count] & 0){
&&&&&&&&&&& NSExtensionItem* extensionItem = [returnedItems firstObject];
&&&&&&&&&&& NSItemProvider* imageItemProvider = [[extensionItem attachments] firstObject];
&&&&&&&&&&& if([imageItemProvider hasItemConformingToTypeIdentifier:(NSString *)kUTTypeImage]){
&&&&&&&&&&&&&&& [imageItemProvider loadItemForTypeIdentifier:(NSString *)kUTTypeImage options:nil completionHandler:^(UIImage *item, NSError *error) {
&&&&&&&&&&&&&&&&&&& if(item && !error){
&&&&&&&&&&&&&&&&&&&&&&& dispatch_async(dispatch_get_main_queue(), ^{
&&&&&&&&&&&&&&&&&&&&&&&&&&& [self.imageView setImage:item];
&&&&&&&&&&&&&&&&&&&&&&& });
&&&&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&& }];
&&&&&&&&&&&&&&&
&&&&&&&&&&& }
上述代码主要是通过设置一个completionBlock处理数据回调。
注意,所有的扩展都是一个UIViewController。所以UIViewController的所有生命周期方法,如viewWillAppear:、viewWillDisappear:等在扩展中都是可以使用的。
2.&如何在扩展中打开containing app
在一般情况下,扩展和containing app不存在通信关系。但是有时候需要在扩展中打开containing app,如iOS 7中预置的日历Widget。在常规的app中,可以使用如下代码在A app中打开B app:
&&& if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:customURL]]) {
&&&&&&& [[UIApplication sharedApplication] openURL:[NSURL URLWithString:customURL]];
但是之前有讲到,sharedApplication API在扩展中被禁止使用,所以为了实现同样的功能,NSExtensionContext定义了一个新的方法用来打开containing app:
- (void)openURL:(NSURL *)URL completionHandler:(void (^)(BOOL success))completionH
在调用此方法之前,需要在containg app中定义一个自定义URL Scheme。定义方法可参见,最终的结果如下图:
在扩展中打开containing app的代码如下:
- (IBAction)openContainingApp:(id)sender {
&&& NSURL *url = [NSURL URLWithString:@&ExtensionDemo://&];
&&& [self.extensionContext openURL:url completionHandler:^(BOOL success) {
3.&如何实现containing app与扩展共享数据
扩展和containing app各自拥有自己的数据容器,虽然扩展内嵌在containing app的内部,但是它们并不可以互访彼此的数据。为了实现containing app与扩展的数据共享,苹果在iOS 8中引入了一个新的概念——App Group。为了开启App Group,找到你的containing app目标,在右侧找到Capabilities标签,定位到App Groups分组,如下图所示。
然后选择你需要共享数据的扩展目标,重复执行一次操作,注意两次的App Group名要相同,不要添加新的条目。当开启App Group后,你可以使用NSUserDefaults方法访问共享区域,如下述代码,注意不是[NSUserDefaults standardUserDefaults]:
_sharedUserDefault= [[NSUserDefaults alloc] initWithSuiteName:@&.aegeaon.ExtensionDemo&];
你也可以使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法访问共享数据区:
- (BOOL)saveTextByNSFileManager {
&&& NSError *err =
&&& NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@&group.wangzz&];
&&& containerURL = [containerURL URLByAppendingPathComponent:@&Library/Caches/good&];
&&& NSString *value = _textField.
&&& BOOL result = [value writeToURL:containerURL atomically:YES encoding:NSUTF8StringEncoding error:&err];
&&& if (!result) {
&&& &&&&NSLog(@&%@&,err);
&&& } else {
&&&&&&& NSLog(@&save value:%@ success.&,value);
App Group区域在containing app与扩展之间所处的关系图:
你可能注意到了,在Xcode 6中iPhone模拟器的位置已经发生了变化。与此同时,在中有提到,app的沙盒结构已经发生了改变,现在它被划分成了三个容器,Bundle容器、Data容器、iCloud容器。iOS 8 app沙盒目录结构如下图:
为了具体了解沙盒目录的布局,使用如下代码分别在containing app和扩展中打印出App Group目录,app bundle目录,以及Document目录:
- (void)logAppPath
&&& //app group路径
&&& NSURL *containerURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@&.aegeaon.ExtensionDemo&];
&&& NSLog(@&app group:\n%@&,containerURL.path);
&&& //打印可执行文件路径
&&& NSLog(@&bundle:\n%@&,[[NSBundle mainBundle] bundlePath]);
&&& //打印documents
&&& NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
&&& NSString *path = [paths objectAtIndex:0];
&&& NSLog(@&documents:\n%@&,path);
在containing app中执行logAppPath方法的结果如下:
app group:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31--BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-92147D
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31--BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app
documents:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31--BC94-BF4D29DC0151/data/Containers/Data/Application/95DBF43A-8B4B-426C-9A3A-C1745FCB3FA2/Documents
在扩展中执行logAppPath方法的结果如下:
app group:
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31--BC94-BF4D29DC0151/data/Containers/Shared/AppGroup/5B4CFBD8-D95D-4F01-92147D
/Users/aegaeon/Library/Developer/CoreSimulator/Devices/72CA5D31--BC94-BF4D29DC0151/data/Containers/Bundle/Application/EED1F771-A8AD-4A97-97F3-2B0A57936C17/ExtensionDemo.app/PlugIns/ExpressExt.appex
documents:
~/Documents
其中标注为红色的意思是每次运行目录名都会发生变化。标注为绿色的表示文件名不会变化的,标准为橘色也验证了iOS 8中沙盒目录被划分的说法。其中也可以看出扩展扩展名为appex,它包含在containing app的PlugIns目录内。下图展示了扩展目录在Finder中的结构:
4.&如何让扩展访问到网页内容
在WWDC上,苹果演示了在Safari for iOS中使用Bing Action扩展将当前页面翻译为其他语言。考虑一下,为了完成这个功能,扩展和浏览器之间一定要建立一个连接,浏览器负责将选中的文本发给扩展,扩展将翻译的结果发回浏览器。为了实现这个机制,这里需要借助一个Javascript脚本,使用JS脚本可以访问网页的DOM。脚本的内容很简单,只包含两个方法,脚本文件名为MyJavaScriptFile.js。代码如下:
var MyExtensionJavaScriptClass = function() {};
MyExtensionJavaScriptClass.prototype = {
&&& run: function(arguments) {
&&&&&&& pletionFunction({&baseURI&: document.baseURI});
&&& finalize: function(arguments) {
&&&&&&& var newContent = arguments[&content&];
&&&&&&& document.write(newContent);
var ExtensionPreprocessingJS = new MyExtensionJavaScriptC
其中包含一个run()和finalize()方法。当Safari一加载好你的JS文件,就会立即调用run方法,当你在扩展中调用了completeRequestReturningItems:expirationHandler:completion:方法,Safari会调用finalize()方法。在run()方法中,Safari提供了一个arguments参数,使用它可以利用键值对的形式将数据传给扩展。在上述代码中,传给扩展的键值对是:@{@&baseURI&
: document.baseURI}。在finalize()方法中,当你调用了completeRequestReturningItems:expirationHandler:completion:方法,方法第一个参数的值会传给finalize()方法的arguments形参。在上述代码中,finalize()接收到参数后,将内容写入了当前的文档。
为了Safari能够调用正确调用到JS文件,需要在扩展的Info.plist文件中添加如下配置:
&key&NSExtensionAttributes&/key&
&&&&& &key&NSExtensionJavaScriptPreprocessingFile&/key&
&&&&& &string&MyJavaScriptFile&/string&
在你的扩展中,为了取得从JS脚本传回的键值对,你需要为NSItemProvider的方法loadItemForTypeIdentifier:options:completionHandler:指定kUTTypePropertyList数据类型,在取得返回的键值字典后,使用NSExtensionJavaScriptPreprocessingResultsKey键取值,代码如下:
&&& NSExtensionContext *context = self.extensionC
&&& NSExtensionItem *item = context.inputItems.firstO
&&& NSItemProvider *provider = item.attachments.firstO
&&& [provider loadItemForTypeIdentifier:(NSString *)kUTTypePropertyList
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& options:nil
&&&&&&&&&&&&&&&&&&&&& completionHandler:^(id&NSSecureCoding& item, NSError *error) {
&&&&&&&&&&&&&&&&&&&&&&&&& NSDictionary *results = (NSDictionary *)
&&&&&&&&&&&&&&&&&&&&&&&&& NSString *baseURI = [[results objectForKey:NSExtensionJavaScriptPreprocessingResultsKey] objectForKey:@&baseURI&];
&&&&&&&&&&&&&&&&&&&&&&&&& NSLog(@&%@&, baseURI);&&&&&&&&&&&&&&&&&&&&&&&&&
&&& &&&&&&&&&&&&&&&&&&}];
为了在扩展中将处理后的结果传给脚本,你需要使用NSItemProvider的initWithItem:typeIdentifier:包装键值对。代码如下:
NSExtensionItem *extensionItem = [[NSExtensionItem alloc] init];
extensionItem.attachments = @[[[NSItemProvider alloc] initWithItem: @{NSExtensionJavaScriptFinalizeArgumentKey: @{@&content&:@&Hello World&}} typeIdentifier:(NSString *)kUTTypePropertyList]];
&[[self extensionContext] completeRequestReturningItems:@[extensionItem] expirationHandler:nil completion:nil];
5.&如何在containing app与扩展之间共享代码
iOS 8中,你可以内嵌一个framework文件在扩展和containing app之间共享代码。假设你希望在你的containing app与扩展之间共享图片处理的代码,此时你可以将代码打包成framework文件,内嵌到两个目标中。对于内嵌框架中的代码,确保不包含扩展不允许使用的API。
如何将代码打包成framework文件这里就不敖述了,感兴趣的同学可以参见:。当你创建好.framework文件后,你可以直接将.framework文件同时拖入containing
app和扩展中,如下图所示:
这里使用公司ILSLib目录下的的MagicalRecord21.framework文件作为素材,讲解如何在containing app和自定义键盘扩展之间实现共享Core Data数据库。在你的扩展和containing app中中配置好引用头文件。分别在containing app的AppDelegate文件的application: didFinishLaunchingWithOptions: launchOptions与自定义键盘扩展的UIInputViewController子类文件中viewDidLoad方法中添加如下代码:
[MagicalRecord setupCoreDataStackWithStoreNamed:@&demo.sqlite&];
上述代码分别对containing app和扩展执行Core Data栈初始化,其中包括数据模型、sqlite存储文件等配置。运行containing app,此时AppDelegate中的数据库配置代码会被执行,接着打开系统设置中的通用选项下的键盘选项,在这里启用自定义键盘。然后回到containing app,切换到自定义键盘扩展,此时自定义键盘扩展中viewDidLoad方法中的数据库配置代码执行,但是控制台出现错误提示:
CoreData: error:&-addPersistentStoreWithType:SQLite&configuration:(null) URL:~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite -- file:/// options:(null) ... returned error Error Domain=NSCocoaErrorDomain
Code=512 &The operation couldn’t be completed. (Cocoa error 512.)& UserInfo=0x7b48a720 {reason=F code = 2} with userInfo dictionary {
&&& reason = &Failed to create file; code = 2&;
上述错误表示在扩展的~/Library/Application%20Support/CustomKeyboardExt/demo.sqlite目录创建.sqlite文件失败。翻阅MagicalRecord源代码(需要从github重新下载源代码,.framework看不到源代码),其中在创建.sqlite存储文件路径的代码中会发现:
+ (NSURL *) MR_urlForStoreName:(NSString *)storeFileName {
NSArray *paths = [NSArray arrayWithObjects:[self MR_applicationDocumentsDirectory], [self MR_applicationStorageDirectory], nil];
&&& NSFileManager *fm = [[NSFileManager alloc] init];
&&& for (NSString *path in paths) {
&&&&&&& NSString *filepath = [path stringByAppendingPathComponent:storeFileName];
&&&&&&& if ([fm fileExistsAtPath:filepath]) {
&&&&&&&&&&& return [NSURL fileURLWithPath:filepath];
&&& //set default url
&&& return [NSURL fileURLWithPath:[[self&MR_applicationStorageDirectory] stringByAppendingPathComponent:storeFileName]];
其中MR_applicationStorageDirectory方法返回的是Application Support目录,而这个目录是处在Library目录内的。上文中已经讲过,扩展没有Documents目录,同样也是没有Library目录。所以文件创建会发生失败。为了实现扩展与containing app之间共享.sqlite文件,这里需要将.sqlite文件创建在App Group区域。问题是MagicalRecord21.framework文件只暴露了头文件,无法对其源文件中的MR_urlForStoreName:方法做修改。这里使用Objective-C的动态运行时技术——Method
Swizzling,在运行时将MR_urlForStoreName:方法的实现使用新的实现进行替换。&(注:这里可以直接给setupCoreDataStackWithStoreNamed方法传递一个包含文件路径的URL类型参数实现修改.sqlite文件的存放位置,methodSwizzling只是另一种通用处理方法)
首先需要为自定义键盘扩展创建一个Category文件NSPersistentStore+Tracking.h/m,.m文件中的完整的代码如下:
#import &NSPersistentStore+Tracking.h&
#import &objc/runtime.h&
#import &MagicalRecord21/CoreData+MagicalRecord.h&
static NSString * const kGroupName = @&.aegeaon.ExtensionDemo&;
static NSString * const kContainingDirectory = @&CoreDataStore/&;
@implementation NSPersistentStore (Tracking)
+ (void)load {
&&& static dispatch_once_t onceT
&&& dispatch_once(&onceToken, ^{
&&&&&&& Class class = [self class];
&&&&&&& SEL originalSelector = @selector(MR_urlForStoreName:);
&&&&&&& SEL swizzledSelector = @selector(ILS_urlForStoreName:);
&&&&&&& SwizzleClassMethod(class, originalSelector, swizzledSelector);
void SwizzleClassMethod(Class c, SEL orig, SEL new) {
&&& Method origMethod = class_getClassMethod(c, orig);
&&& Method newMethod = class_getClassMethod(c, new);
&&& c = object_getClass((id)c);
&&& if(class_addMethod(c, orig, method_getImplementation(newMethod), method_getTypeEncoding(newMethod)))
&&&&&&& class_replaceMethod(c, new, method_getImplementation(origMethod), method_getTypeEncoding(origMethod));
&&&&&&& method_exchangeImplementations(origMethod, newMethod);
#pragma mark - Method Swizzling
+ (NSURL *) ILS_urlForStoreName:(NSString *)storeFileName {
&&& NSURL *storeURL = [[NSFileManager defaultManager]&containerURLForSecurityApplicationGroupIdentifier:kGroupName];
&&& storeURL = [storeURL URLByAppendingPathComponent:[kContainingDirectory stringByAppendingString:storeFileName]];
&&& return storeURL;
在当前的代码一载入内存,load方法将被执行,它比AppDelegate的application: didFinishLaunchingWithOptions: launchOptions方法要先被执行,上述代码会将MR_urlForStoreName:的实现替换成ILS_urlForStoreName:,在ILS_urlForStoreName:方法中,使用NSFileManager的containerURLForSecurityApplicationGroupIdentifier方法设定App
Group,最终的.sqlite文件将保存在App Group目录内的CoreDataStore目录下。同样需要为containing app中使用此方法,可以直接将NSPersistentStore+Tracking.h/m拖入containing app目标内。再次运行自定义键盘扩展,数据库文件已成功保存到App Group中。如下图:
同时被共享的代码框架MagicalRecord21.framework被containg app和扩展共享,双方共用一个框架文件,如下图:
6.&如何在扩展中处理长时间任务
用户希望在扩展完成他们的任务之后能够立即返回到host app中。但是如果扩展执行的任务是一个长时间任务,比如下载。在这种情况下,需要使用NSURLSession来创建一个下载session,并初始化一个后台下载任务。当扩展初始化了上传下载任务后,就算是完成了host app的请求,扩展就可以被终止。这不会影响到任务的结果。如果当后台任务完成后,你的扩展不在运行,系统将在后台启动你的contaiing app并调用appdelegate的aplication:handleEventsForBackgroundURLSession:completionHandler:方法。为了在扩展中初始化一个后台的NSURLSession任务,你必须设置一个containing
app和扩展都可以访问的共享容器。
相关代码如下:
NSURLSession *mySession = [self configureMySession];
NSURL *url = [NSURL URLWithString:@&/LargeFile.zip&];
NSURLSessionTask *myTask = [mySession downloadTaskWithURL:url];
[myTask resume];
- (NSURLSession *) configureMySession {
&&&&if (!mySession) {
&&&&&&&&NSURLSessionConfiguration* config = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:@“com.mycompany.myapp.backgroundsession”];
&&&&&&&&config.sharedContainerIdentifier = @&com.mycompany.myappgroupidentifier&;
&&&&&&&&mySession = [NSURLSession sessionWithConfiguration:config delegate:selfdelegateQueue:nil];
&&&&return myS
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:160914次
积分:1804
积分:1804
排名:第17960名
原创:27篇
转载:13篇
评论:21条}

我要回帖

更多关于 safari浏览器 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信