I am trying to get the FB SDK to work with my React-Native application on iOS, but I encountered an error:
React-Native - unable to read property "logInWithReadPermissions" from undefined
at the touch of a button.
I have already followed the following instructions:
1. npm install rnpm -g
2. rnpm link react-native-fbsdk
3. remove the following lines from subspecs in ios/PodFile
'Core',
'Login',
'Share',
4. pod install
5. go to https://developers.facebook.com/docs/ios/getting-started, download FB ios SDK, and unzip to ~/Documents/FacebookSDK
6. open F8v2.xcworkspac with xcode, and drag Bolts.framework,FBSDKCoreKit.framework, FBSDKLoginKit.framework, FBSDKShareKit.framework in ~/Documents/FacebookSDK to Frameworks under F8V2 project.
7. run react-native run-ios .It should work now. If have build issue, drag the three FB...Kit.framework to RCTFBSDK.xcodeproj too.
Bad luck.
The flow of components is as follows:
(button)
<LoginButton source="First screen" />
LoginButton:
'use strict';
const React = require('react');
const {StyleSheet} = require('react-native');
const F8Button = require('F8Button');
const { logInWithFacebook } = require('../actions');
const {connect} = require('react-redux');
class LoginButton extends React.Component {
props: {
style: any;
source?: string; // For Analytics
dispatch: (action: any) => Promise;
onLoggedIn: ?() => void;
};
state: {
isLoading: boolean;
};
_isMounted: boolean;
constructor() {
super();
this.state = { isLoading: false };
}
componentDidMount() {
this._isMounted = true;
}
componentWillUnmount() {
this._isMounted = false;
}
render() {
if (this.state.isLoading) {
return (
<F8Button
style={[styles.button, this.props.style]}
caption="Please wait..."
onPress={() => {}}
/>
);
}
return (
<F8Button
style={[styles.button, this.props.style]}
icon={require('../login/img/f-logo.png')}
caption="Log in with Facebook"
onPress={() => this.logIn()}
/>
);
}
async logIn() {
const {dispatch, onLoggedIn} = this.props;
this.setState({isLoading: true});
try {
await Promise.race([
dispatch(logInWithFacebook(this.props.source)),
timeout(15000),
]);
} catch (e) {
const message = e.message || e;
if (message !== 'Timed out' && message !== 'Canceled by user') {
alert(message);
console.warn(e);
}
return;
} finally {
this._isMounted && this.setState({isLoading: false});
}
onLoggedIn && onLoggedIn();
}
}
async function timeout(ms: number): Promise {
return new Promise((resolve, reject) => {
setTimeout(() => reject(new Error('Timed out')), ms);
});
}
var styles = StyleSheet.create({
button: {
alignSelf: 'center',
width: 270,
},
});
module.exports = connect()(LoginButton);
FacebookSDK.js:
'use strict';
var {
LoginManager,
AccessToken,
GraphRequest,
GraphRequestManager,
} = require('react-native-fbsdk');
const emptyFunction = () => {};
const mapObject = require('fbjs/lib/mapObject');
type AuthResponse = {
userID: string;
accessToken: string;
expiresIn: number;
};
type LoginOptions = { scope: string };
type LoginCallback = (result: {authResponse?: AuthResponse, error?: Error}) => void;
let _authResponse: ?AuthResponse = null;
async function loginWithFacebookSDK(options: LoginOptions): Promise<AuthResponse> {
const scope = options.scope || 'public_profile';
const permissions = scope.split(',');
const loginResult = await LoginManager.logInWithReadPermissions(permissions);
if (loginResult.isCancelled) {
throw new Error('Canceled by user');
}
const accessToken = await AccessToken.getCurrentAccessToken();
if (!accessToken) {
throw new Error('No access token');
}
_authResponse = {
userID: accessToken.userID,
accessToken: accessToken.accessToken,
expiresIn: Math.round((accessToken.expirationTime - Date.now()) / 1000),
};
return _authResponse;
}
var FacebookSDK = {
init() {
window.FB = FacebookSDK;
},
login(callback: LoginCallback, options: LoginOptions) {
loginWithFacebookSDK(options).then(
(authResponse) => callback({authResponse}),
(error) => callback({error})
);
},
getAuthResponse(): ?AuthResponse {
return _authResponse;
},
logout() {
LoginManager.logOut();
},
api: function(path: string, ...args: Array<mixed>) {
const argByType = {};
args.forEach((arg) => { argByType[typeof arg] = arg; });
const httpMethod = (argByType['string'] || 'get').toUpperCase();
const params = argByType['object'] || {};
const callback = argByType['function'] || emptyFunction;
const parameters = mapObject(params, (value) => ({string: value}));
function processResponse(error, result) {
if (!error && typeof result === 'string') {
try {
result = JSON.parse(result);
} catch (e) {
error = e;
}
}
const data = error ? {error} : result;
callback(data);
}
const request = new GraphRequest(path, {parameters, httpMethod}, processResponse);
new GraphRequestManager().addRequest(request).start();
}
};
module.exports = FacebookSDK;