javascript - React Native : App Store Rejection, 支持 IPv6

标签 javascript ios iphone reactjs react-native

我有一个简单的网站 react-native 客户端,在登录页面它提供了两个选项,手动输入登录代码或使用条形码扫描仪扫描它。我已经在真实设备和模拟器中多次测试该应用程序,它运行良好。 实际上,我只在 ipv4 上进行测试,并且使用 fetch 进行登录,我认为它默认支持 ipv6。

他们说当应用程序离线时通过 ipv6 网络,我无法理解离线并在 IPV6 网络上是什么意思?

当应用离线时,我会向用户显示没有连接的错误。所以我不知道它怎么会崩溃。

是否应该为提取请求添加超时来解决问题?

但是由于同样的错误,该应用被拒绝了 3 次:

Performance - 2.1

Thank you for your resubmission.

Your app crashes on iPhone running iOS 9.3.3 connected to an IPv6 network when we:

Specifically, tapping the login still leads the app to crash.

This occurred when your app was used:

  • Offline
  • On Wi-Fi

We have attached detailed crash logs to help troubleshoot this issue.

这是 login.js :

'use strict';
import React, { Component } from 'react';
import {
  Text,
  View,
  Image,
  TextInput,
  TouchableOpacity,
  ActivityIndicatorIOS,
  StyleSheet,
  Dimensions,
  AlertIOS,
  NetInfo,
 } from 'react-native';
import Camera from 'react-native-camera';
var { width, height } = Dimensions.get('window');

class Login extends Component {
    constructor(props){
        super(props);
        this.state = {
            showProgress: false,
            showCamera: false,
            cameraType: Camera.constants.Type.back,
            barcode: true,
            isConnected: false,
        }
    }

    componentWillMount(){
      NetInfo.isConnected.fetch().done((data) => {
      this.setState({
        isConnected: data
      })
    });
    }

    _onBarCodeRead(e) {
    this.setState({
      showCamera: false,
      barcodeData: e.data,
      logincode: e.data,
      success: true,
    });
    this.onLoginPressed();
    }

    render(){

        if(this.state.showCamera) {
        return (
            <Camera
                ref="cam"
                style={styles.container}
                onBarCodeRead={this._onBarCodeRead.bind(this)}
                type={this.state.cameraType}>
            </Camera>
        );
        } else {
          var errorCtrl = <View />;
          if(!this.state.success){
              errorCtrl = <Text style={styles.error}>
                  {this.state.message}
              </Text>;
          }
          ///// Check login type
          if(this.state.barcode){
            return(
              <View style={styles.container}>
              <Image style={styles.logo} source={require('image!logo')} />
                  <Text style={styles.heading}>
                    Please use QR-Scanner to login,{'\n'}
                    or enter the Login code manually.
                  </Text>
              <TouchableOpacity
                    onPress={this.onQrPressed.bind(this)}
                    style={styles.button}>
                    <Text style={styles.buttonText}>Use QR-Scanner</Text>
                </TouchableOpacity>
                <TouchableOpacity
                      onPress={this.toManuall.bind(this)}
                      >
                      <Text style={styles.change}>
                  Want to enter code manually?
                  </Text>
                </TouchableOpacity>
                {errorCtrl}

                <ActivityIndicatorIOS
                    animating={this.state.showProgress}
                    size="large"
                    style={styles.loader}
                    />
            </View>
            );
          } else {
            return(
              <View style={styles.container}>
              <Image style={styles.logo} source={require('image!logo')} />
                  <Text style={styles.heading}>
                    Please use QR-Scanner to login,{'\n'}
                    or enter the Login code manually.
                  </Text>
                  <TextInput onChangeText={(text)=> this.setState({logincode: text})} style={styles.loginInput} placeholder={this.state.logincode}>
                  </TextInput>
                    <TouchableOpacity onPress={this.onLoginPressed.bind(this)} style={styles.button} >
                    <Text style={styles.buttonText}>Log in</Text>
                    </TouchableOpacity>
                    <TouchableOpacity
                          onPress={this.toBarcode.bind(this)}
                          >
                          <Text style={styles.change}>
                      Want to use Barcode?
                      </Text>
                    </TouchableOpacity>
                    {errorCtrl}

                    <ActivityIndicatorIOS
                        animating={this.state.showProgress}
                        size="large"
                        style={styles.loader}
                        />
                </View>
            );
          }
          /////
        }
    }

    onLoginPressed(){
      if(this.state.isConnected){
        /// do the validation
        var valid = false;
        if(this.state.logincode != undefined && this.state.logincode.includes('opencampus://') && this.state.logincode.includes('access_token=') && this.state.logincode.includes('refresh_token=') && this.state.logincode.includes('id=') && this.state.logincode.includes('name=') && this.state.logincode.includes('scheme=')){
          var valid = true;
        }
        if(valid){
          console.log('Login.ios: Attempting to log in with logincode ' + this.state.logincode);
          this.setState({showProgress: true});
          console.log('Login.ios: calling AuthService class');
          var AuthService = require('./AuthService');
          AuthService.login({
              logincode: this.state.logincode
          }, (results)=> {
              this.setState(Object.assign({
                  showProgress: false
              }, results));
          console.log('Login.ios: AuthService execution finished.', results);
              if(results.success && this.props.onLogin){
                  this.props.onLogin(results);
              }
          });
        } else {
          AlertIOS.alert(
            'Invalid Input',
            'Login code you entered is not valid. Be sure to paste the whole string starting with opencampus://'
          );
        }
      } else {
        AlertIOS.alert(
          'No Connection',
          'Please check your internet connection.'
        );
      }
    }

    onQrPressed(){
      this.setState({
        showCamera: true,
      });
    }
    toManuall(){
      this.setState({
        barcode: false,
      });
    }
    toBarcode(){
      this.setState({
        barcode: true,
      });
    }
}

var styles = StyleSheet.create({
    container: {
        backgroundColor: '#00a2dd',
        paddingTop: 40,
        padding: 10,
        alignItems: 'center',
        flex: 1,
        justifyContent: 'center'
    },
    logo: {
      width: 141,
      height: 137,
    },
    heading: {
        fontSize: 18,
        margin: 10,
        marginBottom: 20,
        color: '#FFFFFF',
        paddingTop: 50,
    },
    change: {
        fontSize: 12,
        color: '#FFFFFF',
        marginTop:10,
    },
    loginInput: {
        height: 50,
        marginTop: 10,
        padding: 4,
        fontSize: 18,
        borderWidth: 1,
        borderColor: '#FFFFFF',
        borderRadius: 0,
        color: '#FFFFFF'
    },
    button: {
        height: 50,
        backgroundColor: '#48BBEC',
        borderColor: '#48BBEC',
        alignSelf: 'stretch',
        marginTop: 10,
        justifyContent: 'center',
        alignItems: 'center',
        borderRadius: 5
    },
    buttonText: {
        color: '#fff',
        fontSize: 24
    },
    loader: {
        marginTop: 20
    },
    error: {
        color: 'red',
        paddingTop: 10
    }
});

module.exports = Login;

这是 AuthService.js :

'use strict';

import React, { Component } from 'react';
var SQLite = require('react-native-sqlite-storage');
var DeviceInfo = require('react-native-device-info');

class AuthService extends Component {
  constructor(props) {
        super(props);
        this.state = {
            showProgress: false
        }
        this.errorCB = this.errorCB.bind(this);
        this.successCB = this.successCB.bind(this);
    }

  errorCB(err) {
        console.log("Auth Service: error: ", err);
        this.state.progress.push("Error: " + (err.message || err));
        return false;
  }
  successCB() {
  }

  login(creds, cb){

    var db = SQLite.openDatabase({name : "oc.db", location: 'default'}, this.successCB.bind(this), this.errorCB.bind(this));
    var sql = 'CREATE TABLE IF NOT EXISTS users ('
  + 'access_token text NOT NULL,'
  + 'refresh_token text NOT NULL,'
  + 'userName text NOT NULL,'
  + 'userId text NOT NULL,'
  + 'userMail text NOT NULL,'
  + 'userSignature text NOT NULL,'
  + 'userSignatureFormat text NOT NULL,'
  + 'userCreated text NOT NULL,'
  + 'userAccess text NOT NULL,'
  + 'userLogin text NOT NULL,'
  + 'userStatus text NOT NULL,'
  + 'userTimezone text NOT NULL,'
  + 'userLanguage text NOT NULL,'
  + 'userRoles text NOT NULL,'
  + 'deviceId text NOT NULL,'
  + 'deviceName text NOT NULL,'
  + 'host text NOT NULL,'
  + 'active text NOT NULL'
+ ');';
    db.executeSql(sql, [],
            this.successCB.bind(this),
            this.errorCB.bind(this)
            );

    var LCode = creds.logincode;
    var codeSplited = LCode.split("://");
    var codeSplited2 = codeSplited[1].split("?");
    var appName = codeSplited[0];
    var serverName = codeSplited2[0];
    var splitedVars = codeSplited2[1].split("&");
    var access_token = splitedVars[0].split("=");
    var access_token = access_token[1];
    var refresh_token = splitedVars[1].split("=");
    var refresh_token = refresh_token[1];
    var uid = splitedVars[2].split("=");
    var uid = uid[1];
    var uname = splitedVars[3].split("=");
    var uname = uname[1];
    var scheme = splitedVars[4].split("=");
    var scheme = scheme[1];
    var device_id = DeviceInfo.getUniqueID();
    var device_name = DeviceInfo.getDeviceName();
    var locale = DeviceInfo.getDeviceLocale();
    console.log('AuthService: Try to fetch from : ', serverName);
    console.log('request body: ', JSON.stringify({
      uid: uid,
      refresh_token: refresh_token,
      token: access_token,
      device: device_id,
      device_name: device_name,
    }));
    fetch(scheme + '://' + serverName, {
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
        'language': locale,
        'Authorization': 'Bearer ' + access_token,
      },
      body: JSON.stringify({
        uid: uid,
        refresh_token: refresh_token,
        token: access_token,
        device: device_id,
        device_name: device_name,
      })
    })
    .then((response)=> {
      return response;
    })
    .then((response)=> {
      return response.json();
    })
    .then((results)=> {
      console.log(results);
      if(results['result'] == 1){
        console.log('Auth Service: Login was successfull');
        // User data
        var userName = results['session']['user']['name'];
        var userId = results['session']['user']['uid'];
        var userMail = results['session']['user']['mail'];
        var userSignature = results['session']['user']['signature'];
        var userSignatureFormat = results['session']['user']['signature_format'];
        var userCreated = results['session']['user']['created'];
        var userAccess = results['session']['user']['access'];
        var userLogin = results['session']['user']['login'];
        var userStatus = results['session']['user']['status'];
        var userTimezone = results['session']['user']['timezone'];
        var userLanguage = results['session']['user']['language'];
        var userRoles = results['session']['user']['roles']['2'];
        var host = results['session']['user']['host'];
        var active = 'yes';
        //var userPicture = results['session']['user']['picture'];
        console.log('Auth Service: Lets save user data to database');

        var query = "INSERT INTO users (access_token, refresh_token, userName, userId, userMail, userSignature, userSignatureFormat, userCreated, userAccess, userLogin, userStatus, userTimezone, userLanguage, userRoles, deviceId, deviceName, host, active) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
        var params = [access_token, refresh_token, userName,userId,userMail,userSignature,userSignatureFormat,userCreated,userAccess,userLogin,userStatus,userTimezone,userLanguage,userRoles,device_id,device_name,host,active];
        db.executeSql(query,params,
                this.successCB.bind(this),
                this.errorCB.bind(this)
                );
        return cb({
          success: true,
          userData: results['session']['user']
        });
      } else if(results['result'] == 0){
        console.log('Auth Service: Login failed message is ' + results['message']);
        return cb({
          success: false,
          message: results['message']
        });
      } else {
        console.log('Auth Service: Login failed error is ' + results['error_description']);
        return cb({
          success: false,
          message: results['error_description']
        });
      }

    })
    .catch((err)=> {
      console.log('AuthService: ' + err);
      return cb(err);
    })
    .done();

  }
}

module.exports = new AuthService();

这是 Index.js :

"use strict";

import React, {Component, PropTypes} from 'react';
import {
  AppRegistry,
  NavigatorIOS,
  StyleSheet,
  TabBarIOS,
  View,
  Text,
  StatusBar,
} from 'react-native';

var CourseList = require("./app/CourseList");
var Profile = require("./app/Profile");
import Icon from 'react-native-vector-icons/Ionicons';
var SQLite = require('react-native-sqlite-storage');
var Login = require("./app/Login");
var db = SQLite.openDatabase({name : "oc.db", location: 'default'});

StatusBar.setBarStyle('light-content');

class OpenCampus extends Component {
  constructor(props) {
        super(props);
        this.state = {
          selectedTab: "Courses",
          isLoggedIn: false,
          userId: null,
        };
    }

  componentWillMount(){
    var query = "SELECT * FROM users WHERE active='yes'";
      var params = [];
      db.transaction((tx) => {
            tx.executeSql(query,params, (tx, results) => {
                var len = results.rows.length;
                if(len > 0){
                  let row = results.rows.item(0);
                  this.setState({
                    isLoggedIn: true,
                    userId: row.userId
                  });
                }
              }, function(){
                console.log('index: Something went wrong');
              });
          });
    }

  onLogin(results) {
    this.setState({
      isLoggedIn: true,
    });
  }

  logout() {
    console.log("Logout called from index");
    var query = "DELETE FROM users WHERE userId=?";
    var params = [this.state.userId];
    db.transaction((tx) => {
          tx.executeSql(query,params, (tx, results) => {
            ///// check if there is other accounts on database, if yes, make first row active
            var query = "SELECT * FROM users WHERE active='yes'";
              var params = [];
              db.transaction((tx) => {
                    tx.executeSql(query,params, (tx, results) => {
                        var len = results.rows.length;
                        if(len > 0){
                          let row = results.rows.item(0);
                          userId = row.userId;
                          ///// Set new user active
                          var query = "UPDATE users SET active='yes' WHERE userId=?";
                            var params = [userId];
                            db.transaction((tx) => {
                                  tx.executeSql(query,params, (tx, results) => {
                                      console.log('index: Active Account Changed');
                                    }, function(){
                                      console.log('index: Something went wrong');
                                    });
                                });
                          ///////
                          this.setState({
                              isLoggedIn: true,
                              userId: userId,
                            });
                        } else {
                          this.setState({
                            isLoggedIn: false,
                            userId: null,
                          });
                        }
                      }, function(){
                        console.log('index: Something went wrong');
                      });
                  });
            /////
            }, function(){
              console.log('index: Something went wrong when logging out');
            });
        });
  }


  _renderCourses() {
    return (
      <NavigatorIOS style={styles.wrapper}
        barTintColor='#00a2dd'
        titleTextColor='#fff'
        tintColor='#ffffff'
        ref='RCourses'
        initialRoute={{
          component: CourseList,
          title: 'Courses',
          passProps: {filter: 'Courses'},
        }}
      />
    );
  }
  _renderRegister() {
    return (
      <NavigatorIOS style={styles.wrapper}
        barTintColor='#00a2dd'
        titleTextColor='#fff'
        tintColor='#ffffff'
        ref='RRegister'
        initialRoute={{
          component: CourseList,
          title: 'Register',
          passProps: {filter: 'Register'},
        }}
      />
    );
  }
  _renderProfile() {
    return (
      <NavigatorIOS style={styles.wrapper}
        barTintColor='#00a2dd'
        titleTextColor='#fff'
        tintColor='#ffffff'
        ref='RProfile'
        initialRoute={{
          component: Profile,
          title: 'Profile',
          passProps: {filter: 'Profile'},
          rightButtonTitle: 'Logout',
          onRightButtonPress: () => this.logout(),
          leftButtonTitle: 'Add Account',
          onLeftButtonPress: () => this.addnew(),
        }}
      />
    );
  }


  addnew() {
    console.log('Send user to login page to add new account');
    //// Set old user to inactive
    var query = "UPDATE users SET active='no' WHERE active='yes'";
      var params = [this.state.userId];
      db.transaction((tx) => {
            tx.executeSql(query,params, (tx, results) => {
              //// Set login status to false so login screen will be shown
              console.log(results);
              this.setState({
                isLoggedIn: false,
                userId: null,
              });
              }, function(){
                console.log('index: Something went wrong when adding new account');
              });
          });
  }


  popAll(){
    if(typeof this.refs.RCourses !== typeof undefined){
      this.refs.RCourses.popToTop();
    }
    if(typeof this.refs.RRegister !== typeof undefined){
      this.refs.RRegister.popToTop();
    }
    if(typeof this.refs.RProfile !== typeof undefined){
      this.refs.RProfile.popToTop();
    }
  }

  render() {
    if(!this.state.isLoggedIn){
      console.log('index: User not logged in. redirecting to Login page.');
      return(
        <Login onLogin={this.onLogin.bind(this)} />
      );
    } else {
      console.log('index: User is logged in lets show the content');
    return (
      <TabBarIOS tintColor={"#00a2dd"}>
        <Icon.TabBarItem
          title="Courses"
          iconName="ios-list-outline"
          selectedIconName="ios-list-outline"
          selected={this.state.selectedTab === "Courses"}
          onPress={() => {
            this.setState({
              selectedTab: "Courses",
            });
            this.popAll();
          }}>
          {this._renderCourses()}
        </Icon.TabBarItem>
        <Icon.TabBarItem
          title="Register"
          iconName="ios-book"
          selectedIconName="ios-book"
          selected={this.state.selectedTab === "Register"}
          onPress={() => {
            this.setState({
              selectedTab: "Register",
            });
            this.popAll();
          }}>
          {this._renderRegister()}
        </Icon.TabBarItem>
        <Icon.TabBarItem
          title="Profile"
          iconName="ios-person"
          selectedIconName="ios-person"
          selected={this.state.selectedTab === "Profile"}
          onPress={() => {
            this.setState({
              selectedTab: "Profile",
            });
            this.popAll();
          }}>
          {this._renderProfile()}
        </Icon.TabBarItem>
      </TabBarIOS>
    );
  }
  }
}

var styles = StyleSheet.create({
  tabContent: {
    flex: 1,
    alignItems: "center",
  },
  tabText: {
    color: "white",
    margin: 50,
  },
  wrapper: {
    flex: 1,
    backgroundColor: '#00a2dd',
  }
});

AppRegistry.registerComponent('OpenCampus', () => OpenCampus);

更新: 这是苹果的崩溃日志:http://www.ataomega.com/temp..suczkfac.crash http://www.ataomega.com/temp..hsbgdlod.crash

最佳答案

您应该测试您的应用的 ipv6 兼容性。这是 a tutorial that explains how to do that.

  • Boot OS X 10.11
  • Make sure your Mac is connected to the Internet, but not through Wi-Fi.
  • Launch System Preferences from your Dock, LaunchPad, or the Apple menu.
  • Press the Option key and click Sharing. Don’t release the Option key yet.
  • Select Internet Sharing in the list of sharing services.
  • Release the Option key.
  • Select the Create NAT64 Network checkbox.
  • Choose the network interface that provides your Internet connection, such as Thunderbolt Ethernet.
  • Select the Wi-Fi checkbox.
  • Click Wi-Fi Options, and configure the network name and security options for your network.
  • Setting up local Wi-Fi network options
  • Select the Internet Sharing checkbox to enable your local network.
  • When prompted to confirm you want to begin sharing, click Start.
  • Once sharing is active, you should see a green status light and a label that says Internet Sharing: On. In the Wi-Fi menu, you will also see a small, faint arrow pointing up, indicating that Internet Sharing is enabled. You now have an IPv6 NAT64 network and can connect to it from other devices in order to test your app.

关于javascript - React Native : App Store Rejection, 支持 IPv6,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38783443/

相关文章:

javascript - 包括无法加载的本地版本的库

javascript - 按钮发布错误

iphone - Facebook SDK iOS 中的用户未登录错误

objective-c - 我应该在 Objective-C 中使用 ivars 吗?

iphone - 字符串的数值

iphone - 来自 AVPlayer 对象的 AudioQueueLevelMeterState

javascript - 一个 HTML 按钮可连续调用 3 个功能

javascript - 重定向到父窗口中的链接

ios - SwipeCellKit 滑动删除未调用

iphone - 以编程方式强制 UIScrollView 停止滚动,以便与多个数据源共享 TableView