9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Flutter x Share Extension の覚書

Last updated at Posted at 2020-08-19

はじめに

他のアプリから自分のアプリを立ち上げて何かの入力を促すという操作を行いたい場合
share extensionを利用することがあります。
今回はreceive_sharing_intentを利用したので、それの覚書です。
share_extentionというサンプルアプリを作成し、これを様々なアプリから開くようなサンプルを作りたいと思います。

実装

1.receive_sharing_intent 1.4.0+2

2.プロジェクトの作成

$ flutter create share_extensin
$ cd share_extension

3.install

dependencies:
  flutter:
    sdk: flutter
  receive_sharing_intent: ^1.4.0+2

4.main.dartの修正

main.dart

import 'package:flutter/material.dart';
import 'dart:async';
import 'package:receive_sharing_intent/receive_sharing_intent.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  StreamSubscription _intentDataStreamSubscription;
  List<SharedMediaFile> _sharedFiles;
  String _sharedText;

  @override
  void initState() {
    super.initState();
     //アプリが起動されている状態で呼ばれた時、ここからURLが入ってくる
    _intentDataStreamSubscription =
        ReceiveSharingIntent.getTextStream().listen((String value) {
      setState(() {
        _sharedText = value;
      });
    }, onError: (err) {
      print("getLinkStream error: $err");
    });

    // アプリが起動されていない状態で呼ばれた時、ここからURLが入ってくる
    ReceiveSharingIntent.getInitialText().then((String value) {
      setState(() {
        _sharedText = value;
      });
    });
  }

  @override
  void dispose() {
    _intentDataStreamSubscription.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    const textStyleBold = const TextStyle(fontWeight: FontWeight.bold);
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            children: <Widget>[
              Text("Shared urls/text:", style: textStyleBold),
              Text(_sharedText ?? "")
            ],
          ),
        ),
      ),
    );
  }
}

5.XCodeでプロジェクトを開く

open ios/Runner.xcodeproj/

6.Xcodeで、ShareExtensionのディレクトリを作成する

ツールバーからFile > New > Targetを選択し、表示されたダイアログからShare Extensionを選択
スクリーンショット 2020-08-14 15.36.08.png

ProductNameにはMyShareExtensionと入力する。(別に名前はなんでも良い。サービス名+ShareExtensionとかだとわかりやすくて良いかも)

-> サイドバーとTARGETSにMyShareExtensionという項目が出現する
スクリーンショット 2020-08-14 15.37.56.png

6.5 RunnerとMyShareExtensionのBundleIdentifierを確認する

RunnerとExtensionのBundleIdentifierが同じPrefixになるように設定しておく
Runner:
jp.ne.test.share_xtension
MyShareExtension:
jp.ne.test.share_extension.MyShareExtension

(memo)
これはbuild時に
Embedded binary's bundle identifier is not prefixed with the parent app's bundle identifier.
と言われるため
https://qiita.com/koogawa/items/432b9c65035b6ba17c8b

7 Runner/Info.plistを編集する

Runner/Info.plist

<key>CFBundleURLTypes</key>
	<array>
		<dict>
			<key>CFBundleTypeRole</key>
			<string>Editor</string>
			<key>CFBundleURLSchemes</key>
			<array>
				<string>ShareMedia</string>
			</array>
		</dict>
		<dict/>
	</array>

  <key>NSPhotoLibraryUsageDescription</key>
	<string>To upload photos, please allow permission to access your photo library.</string>

8.MyShareExtension/Info.plistを修正する

若干面倒くさいのが、ShareExtensionを利用したいアプリから、自分のアプリが見えるかどうかが
このinfo.plistの書き方によって、変わってくる。
例えば、primevideのアプリから、自分が作ったshare_extensionというアプリを開きたい場合、
下記のように自分のアプリが出てくることが期待されるが、ここに表示されないことがある。
この場合、info.plistのNSExtensionActivationRuleに記載が足りていないケースがある。
(期待されるRuleはアプリによって異なるため、自分が呼び出したアプリによって見直す必要がある)
debug.info.plistの事例のようにTRUEPREDICATEを指定すると、必ず出てくるようになるので、
最初の確認は、TRUEPREDICATEを指定すると良い。

Image from iOS (1).png

debug.info.plist

<key>NSExtension</key>
       <dict>
           <key>NSExtensionAttributes</key>
           <dict>
               <key>NSExtensionActivationRule</key>
               <string>TRUEPREDICATE</string>
           </dict>
           <key>NSExtensionMainStoryboard</key>
           <string>MainInterface</string>
           <key>NSExtensionPointIdentifier</key>
           <string>com.apple.share-services</string>
       </dict>

service.info.plist

    <key>NSExtension</key>
    <dict>
        <key>NSExtensionAttributes</key>
        <dict>
            <key>NSExtensionActivationRule</key>
            <dict>
                <key>NSExtensionActivationSupportsText</key>
                <true/>
                <key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
                <integer>1</integer>
                <key>NSExtensionActivationSupportsTextWithMaxCount</key>
                <integer>1</integer>
                <key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
                <integer>1</integer>
            </dict>
        </dict>
        <key>NSExtensionMainStoryboard</key>
        <string>MainInterface</string>
        <key>NSExtensionPointIdentifier</key>
        <string>com.apple.share-services</string>
    </dict>
    

間違えると..

App Store Connect Operation Error
ERROR ITMS-90362: "Invalid Info.plist value. The value for the key >'NSExtensionActivationRule' in bundle >Runner.app/PlugIns/OrquestShareExtension.appex is invalid.
と怒られます..

9.RunnerとMyShareExtensionを同じApp Groupに登録する

Signing & Capabilities -> +CapabilityボタンApp Groupsを選択。

スクリーンショット 2020-08-14 15.50.27.png

App Groupsという項が表示されるので、Group名を入力する。(この画面の赤色になっている部分)
グループ名はgroup. + RunnerのBundlerIdentifier。
同じようにTARGETS/MyShareExtensionにもグループ名を設定する。

スクリーンショット 2020-08-14 15.43.24.png

スクリーンショット 2020-08-14 15.43.31.png

9.MyShareExtension/ShareViewController.swiftを修正する

今回はURLをシェアしたいだけなので最低限これだけでok。他にも使いたい場合は、
https://pub.dev/packages/receive_sharing_intent 
を参考にdidSelectPost()に条件を増やしていく。

ShareViewController.swift

import UIKit
import Social
import MobileCoreServices
import Photos

class ShareViewController: SLComposeServiceViewController {
    let hostAppBundleIdentifier = "jp.ne.sakura.oggata.share-extension"
    let sharedKey = "ShareKey2"
    var sharedText: [String] = []
    let urlContentType = kUTTypeURL as String

    override func isContentValid() -> Bool {
        return true
    }

    override func viewDidLoad() {
    }
    
    override func didSelectPost() {
        if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
            if let contents = content.attachments {
                for (index, attachment) in (contents).enumerated() {
                    if attachment.hasItemConformingToTypeIdentifier(urlContentType) {
                        handleUrl(content: content, attachment: attachment, index: index)
                    }
                }
            }
        }
    }

    private func handleUrl (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
        attachment.loadItem(forTypeIdentifier: urlContentType, options: nil) { [weak self] data, error in
            if error == nil, let item = data as? URL, let this = self {
                this.sharedText.append(item.absoluteString)
                if index == (content.attachments?.count)! - 1 {
                    let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
                    userDefaults?.set(this.sharedText, forKey: this.sharedKey)
                    userDefaults?.synchronize()
                    this.redirectToHostApp(type: .text)
                }
            }
        }
    }

    private func redirectToHostApp(type: RedirectType) {
        let url = URL(string: "ShareMedia://dataUrl=\(sharedKey)#\(type)")
        var responder = self as UIResponder?
        let selectorOpenURL = sel_registerName("openURL:")
        while (responder != nil) {
            if (responder?.responds(to: selectorOpenURL))! {
                let _ = responder?.perform(selectorOpenURL, with: url)
            }
            responder = responder!.next
        }
    }
    enum RedirectType {
        case media
        case text
        case file
    }
}

10 pod install

cd ./ios
pod install

10.Build Settings > Enable BitcodeをNO

Slice.png

実機にインストールするときに下記のようなエラーになる..
ld: 'ライブラリ名' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE)
https://qiita.com/imanishisatoshi/items/8b64af8262d7fe2eb2ba

11. provisioning profileにAppGroupを付け加える

Conclution

おしまい

9
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?