全部文章

welogger app 开发笔记 3 - appstore review

2017-12-30 @sunderls

welogger react-native

大家好,这是welogger app开发系列第三篇文章。

距离第二篇文章发布已经一个月过去了,这一个月年末各种琐事比较烦。实际上,welogger已经通过了AppleStore review,可以下载了,点击这里

但是注册的话,需要有key。 目前只能通过内部渠道获得key,如果想加入的话可以尝试联系可能已经加入的同学,或者follow 我们的官方账号 @we_are_loggers,可能在不久之后会随机发出key。

这篇文章总结一些遇到的问题吧。

iphoneX支持

react-native 51开始有<SafeAreaView>,可惜文档里面没有怎么叙述,有兴趣的可以google搜索一下。

import { SafeAreaView } from 'react-native';

也许是我没有怎么理解透彻,在welogger里面用了自定义的fixed tab,用起来不是很顺心。所以最后决定用了 /react-native-iphone-x-helper

welogger中hide了所有的默认tab,然后在顶部和底部的固定位置的组建中,判断是否是iphoneX,如果是的话加入额外的padding。比如这是welogger的顶部tab的stylesheet

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

自定义Tab

react-navigation中的TabNavigator有默认的tab,但是用起来不太开心,不自由。

welogger想要一种没有那么明显的tab,最终采用了自定义的方式,实现起来也不麻烦,大概就是一个可以config icon和callback的组件,比如这样:

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

这其实不算一个tab,只是一个有点像tab的导航而已,因为它作为页面的一部分被显示, 在页面切换的时候会随着发生位置变换,没有原生tab那种比较好的切换动画,不过这感觉不是啥大问题,以后有时间研究透了再修改也行。w

最终版页面结构

上一篇文章中我说到页面结构是:

  1. 最外层是一个stack(modal)
  2. 最外层stack的第一个screen是一个tab
  3. tab的各个页面分别又是stack(非modal)

经过实际测试,发现这需有一些改进的地方,最终版如下:

  1. 最外层是一个stack(modal)
  2. 最外层stack的第一个screen是另一个stack(default) - Main
  3. Main的第一个页面是Base,然后是一些二级页面
  4. Base中有一个if判断,如果发现没有login,就现实登录页面,否则渲染一个Tab,包含History/Now/Cosmos三个页面。

Base页面初始化是调用init API,如果报错让登录的话就是没有登录,否则就按照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}
}));

比如返回到上一界面或者关闭modal

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

geo cache + image cache

react-native环境下可以直接使用标准API navigator.geolocation.getCurrentPosition,但是,要注意缓存设置,不然位置移动不会体现在location信息中。 具体看这里

navigator.geolocation.getCurrentPosition((pos) => {
    // ...
}, (error) => {
    if (error.PERMISSION_DENIED) {
        Alert.alert('', t('error.access.geo'));
    }
    reject(error);
}, {
    maximumAge: 1000 // disable 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, 但是这个library不支持多图选择,所以后来换成了react-native-image-crop-picker,感觉还不错。

注意在最终build时候,需要将这个library添加到xcode的embedded libraries,否则会出现crash现象。 具体看这里: https://github.com/ivpusic/react-native-image-crop-picker/issues/253#issuecomment-283600340

i18n

welogger用的是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>的话,它会是一个矩形,如果设置背景颜色的话,没有字的部分也会有颜色。所以如果想要实现inline的效果的话,需要套用<Text>

react-native的文档中有说明,当用嵌套过后会使用iOS的NSAttributedString。

键盘挡住了输入框

这里真是没有web页面方便,如果输入框处于页面下半部分的话,键盘很容易挡住输入框。这里需要监听键盘的出现和消失,然后调整输入框的位置。看起来麻烦,实际上还好,具体可以参考这篇文章https://medium.freecodecamp.org/how-to-make-your-react-native-app-respond-gracefully-when-the-keyboard-pops-up-7442c1535580

使用keychain存储token

之前用的是AsyncStorage,发现网上各种吐槽其不安全,索性就改为了keychain,welogger用的是 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之类的做得好。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生成了iOS所需的各种size,感觉非常方便。

然后就build出ipa,然后提交到itunesconnect了。

送审第二天就被review & reject了,被说了两个问题:

  1. 用户生成内容没有举报功能,貌似这个是硬性要求 - 没办法,强行各种添加。举报功能还得有个处理后台,意外地麻烦
  2. ipad下app挂起,一直加载 - 后来发现这是个服务器APIbug,然后build target选择universal,解决

然后第二次提交赶上美国圣诞节,提交第三天审核通过, oh yeah。

Nulbarich

最近被老婆大人安利了Nulbarich这个刚出的乐队,貌似最近谜一样的冉冉升起,听了一下确实非常风格特别谜一样的魔性,虽然唱的是英语还是日语已经完全混在一起听不懂了。w

这里推荐他们的一首It's Who We Are。 也比较切合 welogger的价值观。祝大家新年愉快!