Swift転職なら=>【LevTech】
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大
↑クリックして拡大

頭痛が減ったので共有です!

rebuild.fmを応援しています!

HOME > App Extensions #5

App Extensions #5(Action)

前回の記事Todayを説明していましたが、今回はActionsの実装方法を解説してみます。まずはActionsが どこのことを指すのか?Safariでメール送信時になんかにでてくる下からビョーンの以下の画像の箇所になります。(actionというアプリを追加した状態。アイコン無し)

actionサンプル


参考:【iOS8】App Extension の実装方法 その1:ActionAdd Starkoogawasawat

まずは作成してみます。

ShareやTodayと同じように、New->TargetからAppExtensionのActionsを選択します。

actionサンプル0

todayサンプル1

プロジェクトに[action]フォルダが生成されます

todayサンプル1

shareフォルダの中には、以下3つのファイルが含まれています

  • ActionViewController.swift: プログラム部分
  • MainInterface.storyboard:ビュー部分
  • Info.plist:設定部分

AppExtensionsはShareやTodayと同様追加時にはこの三つのファイルが含まれているようです。この状態ですでにSafariには追加されている状態です。 SafariのURL取得等はShareと同じですので、こちらの記事(AppExtension(share))等を確認してください。

アイコンを追加

自作していたアプリのアイコンが設置できずにハマったので共有します。
iOS 8 action extension icon is blank on device (works in simulator)
どうやらBuild PhasesのCopy Bundle ResourceにアイコンセットのImages.xcassetsを含めないといけないようでした。

todayサンプル

(追記 2015/3/15)ページタイトルやページ全体のHTMLの取得方法

ActionではページのURLは簡単にとれたのですが、タイトルが取るのが面倒くさかったので共有です

タイトルやHTMLソースは少しトリッキーな方法で取得する必要があります。JavascriptをActionプロジェクトに 埋め込む形でタイトル等を取得するなんだか荒技のような変化球が必要です。

jsファイルを作成してプロジェクトに追加、info.plistにファイル名追加、ソースを調整、の3ステップで取得できます。

サンプル画像 サンプル画像


var MyPreprocessor = function() {};

MyPreprocessor.prototype = {
    run: function(arguments) {
        arguments.completionFunction({"url": document.URL, "title": document.title});
        //arguments.completionFunction({"URL": document.URL, "pageSource": document.documentElement.outerHTML, "title": document.title, "selection": window.getSelection().toString()});
    }
};

var ExtensionPreprocessingJS = new MyPreprocessor;

プロジェクトに実装するJavascriptを追加します。上記はDemoPreprocessor.jsです。これは任意の名称でOK。

サンプル画像

info.plistのNSExtensionActivationRuleと同じ階層にNSExtensionJavaScriptPreprocessingFileと JSファイル名を追加します。


import UIKit
import MobileCoreServices

class ActionViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        for item: AnyObject in self.extensionContext!.inputItems {
            let inputItem = item as NSExtensionItem
            for provider : AnyObject in inputItem.attachments! {
                let itemProvider = provider as NSItemProvider
                itemProvider.loadItemForTypeIdentifier(kUTTypePropertyList, options: nil) {
                    (decoder: NSSecureCoding!, error: NSError!) -> Void in
                    
                    if let dictionary = decoder as? NSDictionary {
                        dispatch_async(dispatch_get_main_queue(), {
                            let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as NSDictionary
                            println(results["url"]);
                            println(results["title"]);
                        })
                    }
                }
            }
        }
      }
    
    override func viewWillAppear(animated: Bool) {
        // Get the item[s] we're handling from the extension context.
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

JSを埋め込んでいるだけで勝手に実行してNSExtensionJavaScriptPreprocessingResultsKeyに結果を格納してくれています。 ここからURLやTitle、HTMLソース等を取得することが可能です!面倒くさい、、、、(※上記処理はXCode6.3だとエラー。対応は下に記載しています)

追加(2015/4/1) ファビコンを取得する方法

上記DemoPreprocessor.jsを改造してサイトのfaviconを取得するサンプルです。



var MyPreprocessor = function() {};

MyPreprocessor.prototype = {
    run: function(arguments) {
        
        //favicon検索。vに格納されます
        var f=document.evaluate('//link[(@rel=\'icon\') or (@rel=\'shortcut icon\') or (@rel=\'ICON\') or (@rel=\'SHORTCUT ICON\')][1]/@href',document,null,XPathResult.STRING_TYPE,null);
        var u=f.stringValue;
        var v=(u=='')?'http://'+location.host+'/favicon.ico':(u.substring(0,7)!='http://')?'http://'+location.host+u:u;
        
        arguments.completionFunction({"url": document.URL, "title": document.title, "favicon": v});
    }
};

var ExtensionPreprocessingJS = new MyPreprocessor;

追記(2014/4/10) XCode 6.3 Errorが大漁ワーイ!

WatchKitアプリ作成、4月24日までにどうにかしてと開発している中、XCodeの最新6.3が!勢いあまってアップデートすると大漁のエラー。おーい、、、、時間あんまりないんですけど。。。 しかしSwiftのパフォーマンスも向上しているとのことですのでトライです。一部動作しなくなっていた処理があったのでここで共有しておきます。今までAppEtensionsのitemprovider.loadItemForTypeIdentifierが 動作していましたが関数に変更があり以下に修正しました。


import UIKit
import MobileCoreServices

class ActionViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        for item: AnyObject in self.extensionContext!.inputItems {
            let inputItem = item as NSExtensionItem
            for provider : AnyObject in inputItem.attachments! {
                let itemProvider = provider as NSItemProvider
                
                //itemProvider.loadItemForTypeIdentifier(kUTTypePropertyList, options: nil) {
                itemProvider.loadItemForTypeIdentifier("public.data", options: nil) {
                    (decoder: NSSecureCoding!, error: NSError!) -> Void in
                    
                    if let dictionary = decoder as? NSDictionary {
                        dispatch_async(dispatch_get_main_queue(), {
                            let results = dictionary[NSExtensionJavaScriptPreprocessingResultsKey] as NSDictionary
                            println(results["url"]);
                            println(results["title"]);
                        })
                    }
                }
            }
        }
      }
    
    override func viewWillAppear(animated: Bool) {
        // Get the item[s] we're handling from the extension context.
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
}

ここを参考にしました

参考:Uniform Type Identifier

前の記事はこちら:App Extensions #4(Today)。よかったらご確認下さい!

追記 2017年2月21日 Swift3用の調整

以下でハマったのでメモです。

参考:今更ですが iOS8 で実装された Share Extension を美味しくいただきました

		
		
		        for item: Any in self.extensionContext!.inputItems {
            let inputItem = item as! NSExtensionItem
            
            if let attachments = inputItem.attachments as? [NSItemProvider] {
                for itemProvider : NSItemProvider in attachments {
                    if itemProvider.hasItemConformingToTypeIdentifier("public.data") {
                        itemProvider.loadItem(forTypeIdentifier: "public.data", options: nil, completionHandler: {
                            (item, error) in
                    })
                }
            }

        }
			
		

↓こんな記事もありますよ!

SqliteのDBファイル保存+レビュー却下

2回既にレビューを通過していたアプリが、バイナリレビューで却下されてしまいました。理由は、2.23 Details On launch and content download, your app stores 26.48MB on the user's iCloud, which does not comply with the iOS Data Storage となっていてiCouldに保存される量にしては多いから管理してください、といった旨の内容でした。別に却下理由に難癖つけるつもりではないのですが、最初のレビュー時にいってほしい。。。

UISlider

横バーをぐりぐり移動させて値変更させるコントロールです。

UITabBarController

スマホ画面下にタブが出てきて画面を切り替えできるUITabBarViewControllerクラスです。UITabBarItemの記事を追加しましたがUITabbarの記事がなかったので追加しました。(※目のチカチカにご注意ください。)
このエントリーをはてなブックマークに追加
右側のFacebookのLikeをクリック頂けると記事更新の際に通知されますので宜しければご利用下さい!