全ての記事

welogger app 開発メモ 3 - appstore review

2017-12-30 @sunderls

welogger react-native

こんにちは、これはweloggr app開発シリーズの三回目の記事です。

二回目の記事からはもう1ヶ月が経ちました。この1ヶ月は色々事情あり、すごく忙しかったです。実際、weloggerはAppleStore reviewを通って、ダウンロードできるようになりました!ここです

でも登録するにはkeyが必要で、今はまだkeyを取得できる仕組みは作っていない。もし試したいなら、まず入ってる人に聞いてください。またはオフィシャルtwitter account @we_are_loggersをフォーローしてみましょう。

今回の記事で出会った問題をまとめます。

iphoneXのサポート

react-native 51からは<SafeAreaView>があります。けどドキュメントにはちゃんと書いてなかった。ググってください。

import { SafeAreaView } from 'react-native';

自分は完全に理解してないかもしれません、weloggerにはカスタマイズfixed tabを作っていて、使いづらいです。 結局 /react-native-iphone-x-helperにしました。

weloggerではデフォルトで全てのタブを隠しています。ヘッダーをフッターにiphoneXであるかどうかを判別し、もしiphoneXだったらpaddingをつける。例えばこれはwelogger top tab のstylesheet

header: {
    justifyContent: 'center',
    flexDirection: 'row',
    justifyContent: 'space-between',
    borderBottomWidth: 0.2,
    borderBottomColor: '#aaa',
    ...ifIphoneX({
        paddingTop: 40,
        height: 80
        }, {
        paddingTop: 20,
        height: 60
    })
},

customize Tab

react-navigationのTabNavigatorにはデフォルトのtabがあります。けど使いづらくて、自由さがない。

weloggerではメタつtabが欲しくない。色々考えて、自作のタブを作ることにした。実現も難しくない。 iconとcallbackが設定できるcomponentにすぎないですね。例えばこれ:

<Header leftIcon="icon-left" rightIcon="icon-left" tapLeft={...} tapRight={...} />

これは実際Tabではない、ただtabに近いナビですね。画面の一部として表示されてるので。

画面の切り替えにより、タブの位置も変わってしまう。デフォルトのタブのようないいアニメションがない。まあ、これは大した問題ではないと判断し、一旦後回しにした。w

最終のページストラクチャー

前回の記事ではこのようなページストラクチャーを説明した:

  1. 一番外はstack(modal)
  2. その中に一番めのscreenはtab
  3. tabの各ページはまたstack(非modal)

実際テストみたら、色々変更が必要と確定して、最終的には:

  1. 一番外はstack(modal)
  2. 中の一番めのscreenはまたstack(default) - Main
  3. Mainの画面の初めはBase、あとは他の画面
  4. Baseの中にはif文があり、loginしてないと判明したらログイン画面をだす。でなければTab(History/Now/Cosmosを含めた)を表示する

Base screenにてinit APIを叩く。エラーになったらログインさせる。

reduxでのページ遷移

redux-navigationのpropsはredux stateになるので、reduxのdispatchを利用しページ繊維を実現する必要がある。

例えば詳細ページに行くには:

import { NavigationActions } from 'react-navigation';
this.props.dispatch(NavigationActions.navigate({
    routeName: 'Detail',
    params: { id: item.id, title: item.title}
}));

例えば前のページに行くには:

this.props.dispatch(NavigationActions.back());

geo cache + image cache

react-nativeは標準APInavigator.geolocation.getCurrentPositionが使える。ただしcacheに注意しよう。ちゃんとしないと、位置の変化は反映されない。ここをみてください

navigator.geolocation.getCurrentPosition((pos) => {
    // ...
}, (error) => {
    if (error.PERMISSION_DENIED) {
        Alert.alert('', t('error.access.geo'));
    }
    reject(error);
}, {
    maximumAge: 1000 // disable cache
});

画像のcacheも要注意、facebookホームページに参考してください。https://facebook.github.io/react-native/docs/images.html

loading レイヤー

weloggerは手間を減らすため、全画面のloading indicator layerを利用する。 redux storeにてresolveされてないrequestの数を管理したら良い

// action
export const loadingStart = ()  => {
    return {
        type: 'LOADING',
        delta: 1
    };
}

export const loadingEnd = ()  => {
    return {
        type: 'LOADING',
        delta: -1
    };
}

// reducer
export default function loading(count = 0, action) {
    switch (action.type) {
        case 'LOADING':
            return count + action.delta;
    }
    return count;
}

// api
get() {
    store.dispatch(loadingStart())
    fetch(...)
        .then(() => {
            store.dispatch(loadingEnd())
            ...
        })
        .catch((e) => {
            store.dispatch(loadingEnd())
            ...
        }};
}
// view
{ this.pros.loading > 0 && <Loading > }

DEV

react-nativeには開発環境を表明するフラッグがある。それは__DEV__で、コンフィグレーションに使える。

マルチ画像アップロード

react-native-image-pickerを使ってましたが、 これのライブラリーではマルチをサポートしてないので、react-native-image-crop-pickerにしました。

最後ビルドするときには、このlibraryをxcodeのembedded librariesに入れないとcrash現象が起こる、ご注意を https://github.com/ivpusic/react-native-image-crop-picker/issues/253#issuecomment-283600340

i18n

react-native-i18nを使っています。使いやすい。

import I18n from 'react-native-i18n';
import en from './locales/en';
import ja from './locales/ja';  
import zh from './locales/zh';  

I18n.fallbacks = true;
I18n.translations = {
  en,
  ja,
  zh
};

export const t = (...params) => {
  return I18n.t(...params);
};

export default I18n;

// view
<Text>{t('label.connect')}</Text>

Nested text

もし<Text>一つだけ使うなら長方形になる。この場合背景は文字がない領域もカウントされる。なのでtextのバックブランドをつけたい場合、<Text>をnestして使いましょう。

react-nativeのドキュメントには書いてあります。nest textにしたら、NSAttributedStringが使われる。

キーボードがインプットをかぶった

webに比べるとここは本当に劣ってる。もしinput fieldが下部にあったら、キーボードに被られやすい。 対策としてはキーボードの表示非表示のイベントリッスンして、input fieldの位置を調整しないといけない。

ぱっと見めんどくさそうですが、そんなにでもありません、ここに参考してください:https://medium.freecodecamp.org/how-to-make-your-react-native-app-respond-gracefully-when-the-keyboard-pops-up-7442c1535580

keychainでtokenを保存する

AsyncStorageを使ってましたけど、ググったらなんか安全ではないと色々ツッコミがありますので、keychainにしました。react-native-keychainを使っています。

import * as Keychain from 'react-native-keychain';
const getToken = async ()=> {
    const cred = await Keychain.getGenericPassword();
    return cred && cred.password || '';
}

sequelize query in migration

weloggerではsequelizeをormとして使っています。個人的にはnodeのormは本当にphpなどのormに勝てない。sequelizeも使いやすいとは言えない。例えばTIMESTAMP(weloggerはINTにしてる)使えないとか。

最近出会った問題は、migrationするときにsql実行したいときは、queryInterface.sequelize.queryを使う:

module.exports = {
  up: async function (queryInterface, Sequelize) {
    const allLogs = await queryInterface.sequelize.query(`
        Select * from logs
    `, { type: Sequelize.QueryTypes.SELECT});
    await Promise.all(allLogs.map((log) => {
        //...
    }));
  },

ここをみてください。https://github.com/sequelize/sequelize/issues/313

AppStore 申請

アプリの開発はほぼ終わっていて、iconを更新しましょう。

iconのデザインに数時間かかりました。自分はデザインのセンスがないので悩みました。できたアイコンはIcon Generator for Corona SDKで一括で全てのアイコンをせいせいしました。

次はbuildしてipaをitunesconnectにアップする。

次の日にreview & rejectされました。理由は二つ:

  1. UGCには「通報」機能が必要 - 仕方ない、追加しました。
  2. ipadではフリーズになる - これはサーバーAPI bugだと判明し、build targetをuniversalにしたら解決

二回目の申請はクリスマスに被り、三日後申請は通りました oh yeah.

Nulbarich

最近Nulbarichっていうバンドにハマり、歌詞が英語か日本語化わからないけど、スタイルが結構いい w

「It's Who We Are」を添付した。この曲はweloggerの価値観にも会うので。

みなさん!Happy new year!