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;
New updates of @types/react-native-fbsdk has removed logInWithReadPermissions.
Now you can just replace it with logInWithPermissions and everything will work fine.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With