Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion RNShareMenu.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ Pod::Spec.new do |s|
s.source_files = "ios/**/*.{h,m,mm,swift}"
s.exclude_files = [
"ios/ShareViewController.swift",
"ios/ReactShareViewController.swift"
"ios/ReactShareViewController.swift",
"ios/ReactShareViewControllerNewArchitecture.swift"
]


Expand Down
4 changes: 3 additions & 1 deletion SHARE_EXTENSION_VIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

## Create Bridging Header

For React Native 0.82 and newer, the new-architecture controller imports its own React Native modules, so you can skip this section. The share-extension target must include React Native and `ReactAppDependencyProvider` through its Podfile.

Right click on your Share Extension folder, and choose `New File...`

Select Objective-C, make sure the target is your Share Extension
Expand All @@ -25,7 +27,7 @@ Add the following to the new `Bridging-Header.h` file:

Right click on your Share Extension folder, and choose `Add Files to "ProjectName"`

On the pop-up, select `node_modules/react-native-share-menu/ios/ReactShareViewController.swift`. Make sure `Copy items if needed` is NOT selected and that the selected target is your newly created Share Extension
On the pop-up, select `node_modules/react-native-share-menu/ios/ReactShareViewController.swift`. For React Native 0.82 and newer, select `ReactShareViewControllerNewArchitecture.swift` instead. Add only one of these files. Make sure `Copy items if needed` is NOT selected and that the selected target is your newly created Share Extension

![React View Controller Instructions](screenshots/Xcode-06.png)

Expand Down
2 changes: 1 addition & 1 deletion example/.eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = {
root: true,
extends: '@react-native-community',
extends: '@react-native',
};
2 changes: 2 additions & 0 deletions example/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ DerivedData
build/
.idea
.gradle
.cxx/
local.properties
*.iml

Expand Down Expand Up @@ -57,3 +58,4 @@ buck-out/

# CocoaPods
/ios/Pods/
/ios/.xcode.env.local
20 changes: 15 additions & 5 deletions example/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,44 @@ type SharedItem = {
data: string,
};

type SharePayload = {
mimeType?: string,
data: string | null | Array<?SharedItem> | Array<string>,
extraData?: {[string]: mixed},
};

const App: () => React$Node = () => {
const [sharedData, setSharedData] = useState('');
const [sharedMimeType, setSharedMimeType] = useState('');
const [sharedExtraData, setSharedExtraData] = useState(null);

const handleShare = useCallback((item: ?SharedItem) => {
const handleShare = useCallback((item: ?SharePayload) => {
if (!item) {
return;
}

const {mimeType, data, extraData} = item;
const sharedItem = Array.isArray(item.data) && typeof item.mimeType !== 'string' ? item.data[0] : item;
if (!sharedItem || typeof sharedItem.mimeType !== 'string') {
return;
}
const {mimeType, data} = sharedItem;

setSharedData(data);
setSharedExtraData(extraData);
setSharedExtraData(item.extraData);
setSharedMimeType(mimeType);
}, []);

useEffect(() => {
ShareMenu.getInitialShare(handleShare);
}, []);
}, [handleShare]);

useEffect(() => {
const listener = ShareMenu.addNewShareListener(handleShare);

return () => {
listener.remove();
};
}, []);
}, [handleShare]);

return (
<View style={styles.container}>
Expand Down
7 changes: 6 additions & 1 deletion example/Share.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ const Share = () => {
const [sending, setSending] = useState(false);

useEffect(() => {
ShareMenuReactView.data().then(({mimeType, data}) => {
ShareMenuReactView.data().then(result => {
const item = Array.isArray(result?.data) && typeof result?.mimeType !== 'string' ? result.data[0] : result;
if (!item || typeof item.mimeType !== 'string') {
return;
}
const {mimeType, data} = item;
setSharedData(data);
setSharedMimeType(mimeType);
});
Expand Down
48 changes: 47 additions & 1 deletion example/__tests__/App-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,56 @@
import 'react-native';
import React from 'react';
import App from '../App';
import ShareMenu from 'react-native-share-menu';

// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';

jest.mock('react-native-share-menu', () => ({
getInitialShare: jest.fn(),
addNewShareListener: jest.fn(() => ({remove: jest.fn()})),
}));

it('renders correctly', () => {
renderer.create(<App />);
renderer.act(() => {
renderer.create(<App />);
});
expect(ShareMenu.getInitialShare).toHaveBeenCalled();
expect(ShareMenu.addNewShareListener).toHaveBeenCalled();
});

it('displays an initial text share and removes its listener', () => {
const remove = jest.fn();
ShareMenu.getInitialShare.mockImplementationOnce(callback => callback({mimeType: 'text/plain', data: 'Hello'}));
ShareMenu.addNewShareListener.mockReturnValueOnce({remove});

let tree;
renderer.act(() => {
tree = renderer.create(<App />);
});
expect(JSON.stringify(tree.toJSON())).toContain('Hello');

renderer.act(() => tree.unmount());
expect(remove).toHaveBeenCalled();
});

it('ignores an empty iOS share on startup', () => {
ShareMenu.getInitialShare.mockImplementationOnce(callback => callback({data: null}));

renderer.act(() => {
renderer.create(<App />);
});
});

it('displays an iOS share and its extra data', () => {
ShareMenu.getInitialShare.mockImplementationOnce(callback =>
callback({data: [{mimeType: 'text/plain', data: 'Shared from iOS'}], extraData: {hello: 'world'}}),
);

let tree;
renderer.act(() => {
tree = renderer.create(<App />);
});
expect(JSON.stringify(tree.toJSON())).toContain('Shared from iOS');
expect(JSON.stringify(tree.toJSON())).toContain('hello');
});
32 changes: 32 additions & 0 deletions example/__tests__/Share-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import 'react-native';
import React from 'react';
import Share from '../Share';
import {ShareMenuReactView} from 'react-native-share-menu';
import renderer from 'react-test-renderer';

jest.mock('react-native-share-menu', () => ({
ShareMenuReactView: {
data: jest.fn(),
dismissExtension: jest.fn(),
continueInApp: jest.fn(),
},
}));

it('displays the first item from the iOS share payload', async () => {
ShareMenuReactView.data.mockResolvedValue({data: [{mimeType: 'text/plain', data: 'Shared text'}]});

let tree;
await renderer.act(async () => {
tree = renderer.create(<Share />);
});

expect(JSON.stringify(tree.toJSON())).toContain('Shared text');
});

it('handles an empty share payload', async () => {
ShareMenuReactView.data.mockResolvedValue({data: []});

await renderer.act(async () => {
renderer.create(<Share />);
});
});
Loading