
react-native-share の使い方
「react-native-share」の使い方をまとめました。
【最新版の情報は以下で紹介】
前回
1. react-native-share
「react-native-share」は、React Nativeアプリにアプリ間共有を追加するためのライブラリです。
・react-native-share : アプリからデータを送信
・react-native-share-intent : 他のアプリからデータを受信
2. アプリからデータを送信
2-1. セットアップ
(1) React Nativeプロジェクトの生成。
npx react-native init my_app
cd my_app
(2) パッケージのインストール。
npm install react-native-share
2-2. Androidのセットアップ
特別なセットアップは必要ありません。
2-3. iOSのセットアップ
(1) podのインストール。
cd ios
pod install
cd ..
(2) Xcodeで生成されたxcworkspaceを開き、署名を行い、iOSにインストールできることを確認。
2-4. コードの編集と実行
(1) コードの編集と実行。
import React from 'react';
import { Button, View } from 'react-native';
import Share from 'react-native-share';
// アプリ
const App = () => {
// 共有オプション
const shareOptions = {
title: 'タイトルです。',
message: 'メッセージです。',
url: 'https://example.com',
};
// アプリからデータを送信
const onShare = async () => {
try {
const shareResponse = await Share.open(shareOptions);
console.log('Share Response:', shareResponse);
} catch (error) {
console.log('Error =>', error);
}
};
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Button title="Share" onPress={onShare} />
</View>
);
};
export default App;

サポートされている共有オプションについてはドキュメントを参照。
3. 他のアプリからデータを受信
3-1. セットアップ
(1) パッケージのインストール。
npm install react-native-receive-sharing-intent
3-2. Androidのセットアップ
(1) 「<Project_folder>/android/app/src/main/AndroidManifest.xml」に設定を追加。
今回は、「テキスト共有」の設定のみ追加しています。
他の設定についてはドキュメントを参照。
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
<!--TODO この行を追加 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application
android:name=".MainApplication"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:allowBackup="false"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|screenSize|smallestScreenSize|uiMode"
android:launchMode="singleTask"
android:windowSoftInputMode="adjustResize"
android:exported="true"> <!--TODO IMPORTANT: launchModeはsingleTask推奨 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!--TODO: アプリ内でテキスト共有をサポートする場合は、このフィルタを追加-->
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/*" />
</intent-filter>
</activity>
</application>
</manifest>
(2) 「<Project_folder>/android/app/src/main/java/com/YOUR_APP/MainActivity.kt」に以下のコードを追加。
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
}
詳しくは、以下のドキュメントを参照。
3-3. iOSのセットアップ
(1) podのインストール。
cd ios
pod install
cd ..
(2) Xcodeで生成されたxcworkspaceを開き、署名を行い、iOSにインストールできることを確認。
(3) 「<project_folder>/ios/<project_name>/info.plist」に以下の設定を追加追加。
<plist version="1.0">
<dict>
:
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLSchemes</key>
<array>
<string>ShareMedia</string> <!-- 共有URLプロトコル (アプリごとに一意である必要があり、バンドルID推奨) -->
</array>
</dict>
</array>
<key>NSPhotoLibraryUsageDescription</key>
<string>To upload photos, please allow permission to access your photo library.</string>
:
</dict>
</plist>
(4) 「AppDelegate.mm」に以下のコードを追加。
:
#import <React/RCTLinkingManager.h> // ファイルのヘッダーにこの行を追加
:
@implementation AppDelegate
:
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [RCTLinkingManager application:application openURL:url options:options];
}
@end
(5) 「<project_folder>/ios/<your project name>/<your project name>.entitlements」に以下の設定を追加。
今回は、必要ありません。
:
<!--TODO: URLクリックでのアプリ起動をサポートしたい場合は、このタグを追加-->
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:example.com</string>
</array>
:
(6) 「Share Extention」を追加。


(7) 「<project_folder>/ios/<Your Share Extension Name>/info.plist」に以下の項目を設定。
今回は「テキスト共有」と「URL共有」のみ追加しています。
他の設定についてはドキュメントを参照。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>PHSupportedMediaTypes</key>
<array>
</array>
<key>NSExtensionActivationRule</key>
<dict>
<!--TODO: アプリ内でテキスト共有をサポートする場合は、このフラグを追加-->
<key>NSExtensionActivationSupportsText</key>
<true/>
<!--TODO: アプリ内でのURL共有をサポートする場合は、このタグを追加-->
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionMainStoryboard</key>
<string>MainInterface</string>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
</dict>
</dict>
</plist>
(8) 「<project_folder>/ios/<Your Share Extension Name>/ShareViewController.swift」を次のように編集。
「hostAppBundleIdentifier」は、自分の環境のホストアプリのバンドルIDを指定します。それ以外は、公式サイトのコードのままになります。
import UIKit
import Social
import MobileCoreServices
import Photos
class ShareViewController: SLComposeServiceViewController {
// TODO: IMPORTANT: ホストアプリのバンドルIDを指定してください
let hostAppBundleIdentifier = "com.rnreceivesharingintent"
let shareProtocol = "ShareMedia" //共有URLプロトコル (アプリごとに一意である必要があり、バンドルID(hostAppBundleIdentifier)推奨)
let sharedKey = "ShareKey"
var sharedMedia: [SharedMediaFile] = []
var sharedText: [String] = []
let imageContentType = kUTTypeImage as String
let videoContentType = kUTTypeMovie as String
let textContentType = kUTTypeText as String
let urlContentType = kUTTypeURL as String
let fileURLType = kUTTypeFileURL as String;
override func isContentValid() -> Bool {
return true
}
override func viewDidLoad() {
super.viewDidLoad();
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let content = extensionContext!.inputItems[0] as? NSExtensionItem {
if let contents = content.attachments {
for (index, attachment) in (contents).enumerated() {
if attachment.hasItemConformingToTypeIdentifier(imageContentType) {
handleImages(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(textContentType) {
handleText(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(fileURLType) {
handleFiles(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(urlContentType) {
handleUrl(content: content, attachment: attachment, index: index)
} else if attachment.hasItemConformingToTypeIdentifier(videoContentType) {
handleVideos(content: content, attachment: attachment, index: index)
}
}
}
}
}
override func didSelectPost() {
print("didSelectPost");
}
override func configurationItems() -> [Any]! {
// To add configuration options via table cells at the bottom of the sheet, return an array of SLComposeSheetConfigurationItem here.
return []
}
private func handleText (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: textContentType, options: nil) { [weak self] data, error in
if error == nil, let item = data as? String, let this = self {
this.sharedText.append(item)
// If this is the last item, save imagesData in userDefaults and redirect to host app
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)
}
} else {
self?.dismissWithError()
}
}
}
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 this is the last item, save imagesData in userDefaults and redirect to host app
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)
}
} else {
self?.dismissWithError()
}
}
}
private func handleImages (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: imageContentType, options: nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// this.redirectToHostApp(type: .media)
// Always copy
let fileExtension = this.getExtension(from: url, type: .video)
let newName = UUID().uuidString
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent("\(newName).\(fileExtension)")
let copied = this.copyFile(at: url, to: newPath)
if(copied) {
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .image))
}
// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .media)
}
} else {
self?.dismissWithError()
}
}
}
private func handleVideos (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: videoContentType, options:nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// Always copy
let fileExtension = this.getExtension(from: url, type: .video)
let newName = UUID().uuidString
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent("\(newName).\(fileExtension)")
let copied = this.copyFile(at: url, to: newPath)
if(copied) {
guard let sharedFile = this.getSharedMediaFile(forVideo: newPath) else {
return
}
this.sharedMedia.append(sharedFile)
}
// If this is the last item, save imagesData in userDefaults and redirect to host app
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .media)
}
} else {
self?.dismissWithError()
}
}
}
private func handleFiles (content: NSExtensionItem, attachment: NSItemProvider, index: Int) {
attachment.loadItem(forTypeIdentifier: fileURLType, options: nil) { [weak self] data, error in
if error == nil, let url = data as? URL, let this = self {
// Always copy
let newName = this.getFileName(from :url)
let newPath = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(this.hostAppBundleIdentifier)")!
.appendingPathComponent("\(newName)")
let copied = this.copyFile(at: url, to: newPath)
if (copied) {
this.sharedMedia.append(SharedMediaFile(path: newPath.absoluteString, thumbnail: nil, duration: nil, type: .file))
}
if index == (content.attachments?.count)! - 1 {
let userDefaults = UserDefaults(suiteName: "group.\(this.hostAppBundleIdentifier)")
userDefaults?.set(this.toData(data: this.sharedMedia), forKey: this.sharedKey)
userDefaults?.synchronize()
this.redirectToHostApp(type: .file)
}
} else {
self?.dismissWithError()
}
}
}
private func dismissWithError() {
print("[ERROR] Error loading data!")
let alert = UIAlertController(title: "Error", message: "Error loading data", preferredStyle: .alert)
let action = UIAlertAction(title: "Error", style: .cancel) { _ in
self.dismiss(animated: true, completion: nil)
}
alert.addAction(action)
present(alert, animated: true, completion: nil)
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
private func redirectToHostApp(type: RedirectType) {
let url = URL(string: "\(shareProtocol)://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
}
extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
enum RedirectType {
case media
case text
case file
}
func getExtension(from url: URL, type: SharedMediaType) -> String {
let parts = url.lastPathComponent.components(separatedBy: ".")
var ex: String? = nil
if (parts.count > 1) {
ex = parts.last
}
if (ex == nil) {
switch type {
case .image:
ex = "PNG"
case .video:
ex = "MP4"
case .file:
ex = "TXT"
}
}
return ex ?? "Unknown"
}
func getFileName(from url: URL) -> String {
var name = url.lastPathComponent
if (name == "") {
name = UUID().uuidString + "." + getExtension(from: url, type: .file)
}
return name
}
func copyFile(at srcURL: URL, to dstURL: URL) -> Bool {
do {
if FileManager.default.fileExists(atPath: dstURL.path) {
try FileManager.default.removeItem(at: dstURL)
}
try FileManager.default.copyItem(at: srcURL, to: dstURL)
} catch (let error) {
print("Cannot copy item at \(srcURL) to \(dstURL): \(error)")
return false
}
return true
}
private func getSharedMediaFile(forVideo: URL) -> SharedMediaFile? {
let asset = AVAsset(url: forVideo)
let duration = (CMTimeGetSeconds(asset.duration) * 1000).rounded()
let thumbnailPath = getThumbnailPath(for: forVideo)
if FileManager.default.fileExists(atPath: thumbnailPath.path) {
return SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video)
}
var saved = false
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
// let scale = UIScreen.main.scale
assetImgGenerate.maximumSize = CGSize(width: 360, height: 360)
do {
let img = try assetImgGenerate.copyCGImage(at: CMTimeMakeWithSeconds(600, preferredTimescale: Int32(1.0)), actualTime: nil)
try UIImage.pngData(UIImage(cgImage: img))()?.write(to: thumbnailPath)
saved = true
} catch {
saved = false
}
return saved ? SharedMediaFile(path: forVideo.absoluteString, thumbnail: thumbnailPath.absoluteString, duration: duration, type: .video) : nil
}
private func getThumbnailPath(for url: URL) -> URL {
let fileName = Data(url.lastPathComponent.utf8).base64EncodedString().replacingOccurrences(of: "==", with: "")
let path = FileManager.default
.containerURL(forSecurityApplicationGroupIdentifier: "group.\(hostAppBundleIdentifier)")!
.appendingPathComponent("\(fileName).jpg")
return path
}
class SharedMediaFile: Codable {
var path: String; // can be image, video or url path. It can also be text content
var thumbnail: String?; // video thumbnail
var duration: Double?; // video duration in milliseconds
var type: SharedMediaType;
init(path: String, thumbnail: String?, duration: Double?, type: SharedMediaType) {
self.path = path
self.thumbnail = thumbnail
self.duration = duration
self.type = type
}
// Debug method to print out SharedMediaFile details in the console
func toString() {
print("[SharedMediaFile] \n\tpath: \(self.path)\n\tthumbnail: \(self.thumbnail)\n\tduration: \(self.duration)\n\ttype: \(self.type)")
}
}
enum SharedMediaType: Int, Codable {
case image
case video
case file
}
func toData(data: [SharedMediaFile]) -> Data {
let encodedData = try? JSONEncoder().encode(data)
return encodedData!
}
}
extension Array {
subscript (safe index: UInt) -> Element? {
return Int(index) < count ? self[Int(index)] : nil
}
}
(9) 「ホストアプリ」と「Share Extention」で同一の「App Group」を指定。
「App Group」のIDは、「group.ホストアプリのバンドルID」としてください。


詳しくは、以下のドキュメントを参照。
3-4. コードの編集と実行
(1) コードの編集と実行。
import React, { useEffect } from 'react';
import { View, Text } from 'react-native';
import ReceiveSharingIntent from 'react-native-receive-sharing-intent';
// アプリ
const App = () => {
useEffect(() => {
// 他のアプリからデータを受信
ReceiveSharingIntent.getReceivedFiles(
(files) => {
// 受信したファイルデータを処理
console.log(files);
},
(error) => {
console.log(error);
},
'ShareMedia' // iOS用のグループID
);
return () => {
ReceiveSharingIntent.clearReceivedFiles();
};
}, []);
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<Text>Waiting for shared data...</Text>
</View>
);
};
export default App;
(2) 他のアプリでデータを共有。
ログに受信したデータが出力されます。

【おまけ】 expo-share
React Nativeのアプリ間共有としては、「expo-share」も提供されています。
「expo-share」の機能は限定的なため、基本的に「react-native-share」を使うで問題なさそうです。