I am trying to get the FB SDK to work with my React-Native app on iOS, but I am running into the error:
React-Native – Cannot read property ‘logInWithReadPermissions’ of undefined
on button click.
I’ve already followed these 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.
No luck.
The component flow looks like:
(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, // FIXME: RNFBSDK bug: userId -> userID
accessToken: accessToken.accessToken,
expiresIn: Math.round((accessToken.expirationTime - Date.now()) / 1000),
};
return _authResponse;
}
var FacebookSDK = {
init() {
// This is needed by Parse
window.FB = FacebookSDK;
},
login(callback: LoginCallback, options: LoginOptions) {
loginWithFacebookSDK(options).then(
(authResponse) => callback({authResponse}),
(error) => callback({error})
);
},
getAuthResponse(): ?AuthResponse {
return _authResponse;
},
logout() {
LoginManager.logOut();
},
/**
* Make a API call to Graph server. This is the **real** RESTful API.
*
* Except the path, all arguments to this function are optional. So any of
* these are valid:
*
* FB.api('/me') // throw away the response
* FB.api('/me', function(r) { console.log(r) })
* FB.api('/me', { fields: 'email' }); // throw away response
* FB.api('/me', { fields: 'email' }, function(r) { console.log(r) });
* FB.api('/12345678', 'delete', function(r) { console.log(r) });
* FB.api(
* '/me/feed',
* 'post',
* { body: 'hi there' },
* function(r) { console.log(r) }
* );
*
* param path {String} the url path
* param method {String} the http method
* param params {Object} the parameters for the query
* param cb {Function} the callback function to handle the response
*/
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;
// FIXME: Move this into RNFBSDK
// GraphRequest requires all parameters to be in {string: 'abc'}
// or {uri: 'xyz'} format
const parameters = mapObject(params, (value) => ({string: value}));
function processResponse(error, result) {
// FIXME: RNFBSDK bug: result is Object on iOS and string on Android
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;
2
Answers
I have used the react-native-fbsdk and it works. This module has;
You may need to look at the read permissions of your component.
New updates of
@types/react-native-fbsdk
has removedlogInWithReadPermissions
.Now you can just replace it with
logInWithPermissions
and everything will work fine.