search
LoginSignup
19
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

React Native Advent Calendar 2017 Day 17

posted at

updated at

Twitter APIをExpoでデタッチせず、かつAuth0を使わず、javascriptのみで叩く

こちらはReact Nativeアドベントカレンダー 17日目の記事になります。
空いていたので勝手に書いてしまってすみません。。。

自己紹介

こんにちは!
いつもは適当なブログにつらつらと仕事で使ったことなどを投稿しているのですが、年末だしアドベントカレンダーあるからと思ってQiitaに書いてみることにしました。
ハムカツおじさん🤘という名前でTwitterやってます。
g&hという会社でエンジニアをやっていて、サーバだったりフロントだったり、アプリだったりのなんでも屋です。

はじめに

自分はReact NativeというかExpoを使ってアプリを複数作っています。
ちなみに最近出した使い道がよくわからないアプリはこちらです。

Expoは便利なんですが、使っていると限界というのがそろそろ見えてきたなぁと思いつつも、割と便利なので離れづらいなぁと思っています。

で、最近の悩みがソーシャル系のAPI関連が強くないなと。
Facebookに関してはExpoのモジュールがあるんですが、Twitterなど他のものに対応をしていない状況です。
対応していないというかAuth0を使って構築してねという形であり、そうなると無料枠を超えてしまったらどうなってしまうんだ?と心配になってしまいます。
ちなみにFirebaseのAuthenticationにも現状対応していないので困りますね。
さらにいうとAPIクライアントが全然見つからない…

とりわけ今作っているアプリはTwitterのAPIを使うものなので、TwitterのAPIだけとりあえず叩ければ大丈夫だろうということで、NativeModulesも使用せず、Auth0も使用しないAPIクライアントを作ることにしました。

もちろんExpoを使っていない通常のReact Nativeプロジェクトでも動きます。

作ったもの

ちっちゃいですがプレビューはこちらです。
1513930944.gif

ソースコードはこちら。
https://github.com/watanabeyu/react-native-simple-twitter

Expo上で動くサンプルはこちら。
https://expo.io/@watanabe_yu/react-native-simple-twitter-example

どうやって設置するの?

インストール

npm install react-native-simple-twitter --save

まずはインストールしてください。

App.jsにてキーとシークレットをセットする

App.js
import React from 'react'
import { AppLoading, Asset, Font, Constants } from 'expo'
import Navigation from 'app/src'

/* npm */
import twitter from 'react-native-simple-twitter'

export default class App extends React.Component {
  constructor(props) {
    super(props)

    this.state = {
      isLoadingComplete: false
    }
  }

  render() {
    if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
      return (
        <AppLoading
          startAsync={this._loadResourcesAsync}
          onError={(error) => console.warn(error)}
          onFinish={() => this.setState({ isLoadingComplete: true })}
        />
      )
    }
    else {
      return <Navigation />
    }
  }

  _loadResourcesAsync = async () => {
    return Promise.all([
      Asset.loadAsync([
        require('app/assets/images/icon.png'),
        require('app/assets/images/ok_man.png')
      ]),
      twitter.setConsumerKey(Constants.manifest.extra.twitter.consumerKey, Constants.manifest.extra.twitter.consumerKeySecret)
    ])
  };
}

最初にTwitterアプリのキーとシークレットをセットしてあげます。
App.jsじゃなくても大丈夫ですが、App.jsの方がわかりやすいかと。

ログイン画面にボタンを設置する

LoginScreen.js
import React from 'react'
import {
  View,
  Text,
  Alert,
  StyleSheet
} from 'react-native'
import { NavigationActions } from 'react-navigation'
import { connect } from 'react-redux'
import { Constants } from 'expo'

/* import twitter */
import twitter, { TWLoginButton } from 'react-native-simple-twitter'

@connect(
  state => ({
    user: state.user
  })
)
export default class LoginScreen extends React.Component {
  static navigationOptions = ({ navigation }) => {
    const { state, setParams } = navigation
    const { params = {} } = navigation.state

    return {
      header: null
    }
  }

  constructor(props) {
    super(props)

    this.state = {
      isVisible: false,
      authUrl: null
    }
  }

  async componentWillMount() {
    if (this.props.user.token) {
      twitter.setAccessToken(this.props.user.token, this.props.user.token_secret)

      try {
        const user = await twitter.get("account/verify_credentials.json", { include_entities: false, skip_status: true, include_email: true })
        this.props.dispatch({ type: "USER_SET", user: user })

        this.props.dispatch(NavigationActions.reset({
          index: 0,
          actions: [
            NavigationActions.navigate({ routeName: 'Home' })
          ]
        }))
      } catch (err) {
        console.log(err)
      }
    }
  }

  onGetAccessToken = ({ oauth_token, oauth_token_secret }) => {
    this.props.dispatch({ type: "TOKEN_SET", token: oauth_token, token_secret: oauth_token_secret })
  }

  onSuccess = (user) => {
    this.props.dispatch({ type: "USER_SET", user: user })

    Alert.alert(
      "Success",
      "ログインできました",
      [
        {
          text: 'Go HomeScreen',
          onPress: () => {
            this.props.dispatch(NavigationActions.reset({
              index: 0,
              actions: [
                NavigationActions.navigate({ routeName: 'Home' })
              ]
            }))
          }
        }
      ]
    )
  }

  onClose = (e) => {
    console.log("press close button")
  }

  onError = (err) => {
    console.log(err)
  }

  render() {
    return (
      <View style={styles.container}>
        <View style={styles.title}>
          <Text style={styles.titleText}>Login</Text>
        </View>
        <TWLoginButton headerColor={Constants.manifest.primaryColor}
          containerStyle={styles.loginContainer}
          style={styles.loginButton}
          textStyle={styles.loginButtonText}
          onGetAccessToken={this.onGetAccessToken}
          onSuccess={this.onSuccess}
          closeText="閉じる"
          closeTextStyle={styles.loginCloseText}
          onClose={this.onClose}
          onError={this.onError}>Twitter IDではじめる</TWLoginButton>
      </View>
    )
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: Constants.manifest.primaryColor
  },
  title: {
    flex: 1,
    padding: 64
  },
  titleText: {
    textAlign: "center",
    fontSize: 24,
    color: "#fff",
    fontWeight: "bold"
  },
  loginContainer: {
    paddingHorizontal: 32,
    marginBottom: 64,
    backgroundColor: "transparent"
  },
  loginButton: {
    backgroundColor: "#fff",
    paddingVertical: 16,
    borderRadius: 64,
    overflow: "hidden"
  },
  loginButtonText: {
    color: Constants.manifest.primaryColor,
    fontSize: 16,
    fontWeight: "bold",
    textAlign: "center"
  },
  loginCloseText: {
    color: "#fff",
    fontWeight: "bold"
  }
})

そしてTWLoginButtonを設置してあげれば、ツイッターログインができます。
ちなみにトークンを取得した際、およびログインが成功した際にコールバックを設定することができるので、その中でreduxに投げてあげるということもできます。

なおこのクライアント自体シングルトンで作っているので、ログインしたらトークンはアプリ自体が切れるまでは有効です。
なのでトークンを取得した際にAsyncStorageに保存しておくとかして、アプリ立ち上げ時にAsyncStorageからトークンを取得していつでもAPIを叩ける状況にしておくことも可能です。

サンプルURLはこちらです。
https://github.com/watanabeyu/react-native-simple-twitter/tree/master/example

ライブラリ構成は下記になります。
* react-navigation
* react-redux
* redux

苦労したところ

OAuth1.0のsignatureを作るのがなかなか面倒でした。
パラメーターを全てエンコードして文字列にして、hashしてbase64をし、さらに云々という形で地味に大変だなぁと。
文字列にする際にパラメーターのkeyの順番が影響してきたり、さらに半角スペースや他の文字のエンコードもちゃんとしてあげないとダメでした。

まとめ

つらつらと自分の作ったAPIクライアントおよびコンポーネントの紹介となってしまいましたが、何かを介せずにTwitterのAPIを叩けるというのは便利なんじゃないかと個人的には思っています。

使ってくれる優しい方がおりましたら、ぜひ使ってフィードバックをください。
せこせこと改善していきたいと思います。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
19
Help us understand the problem. What are the problem?