React Native:App Store拒绝,支持IPv6 [英] React Native: App Store Rejection, Support for IPv6

查看:136
本文介绍了React Native:App Store拒绝,支持IPv6的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我为一个网站提供了一个简单的react-native客户端,在登录页面中它提供了两个选项,手动输入登录代码或使用条形码扫描器扫描它。我已经在真实设备和模拟器中测试了应用程序很多时候它工作正常。
实际上我只测试ipv4,并使用fetch登录,我认为默认情况下支持ipv6。

I have a simple react-native client for a website, in login page it provides two option, enter login code manually or scan it using barcode scanner. i've tested the app in real device and emulator lot's of times it's working fine. Actually i test only over ipv4, and for login im using fetch, which i think is supporting ipv6 by default.

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

They say over ipv6 network when app was offline, i cannot understand what does it mean to be OFFLINE and be on IPV6 network?

当应用程序离线时,我向用户显示错误,表示没有连接。所以我不知道它会如何崩溃。

When app is offline, i'm showing error to user that there is no connectivity. so i don't know how it can crash.

应该为获取请求添加超时来解决问题吗?

should adding a timeout to fetch request fix the issue?

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

But the app being rejected 3 times due to same error :


性能 - 2.1

Performance - 2.1

感谢您的重新提交。

当我们运行iOS 9.3.3连接到IPv6
网络的iPhone时,您的应用程序崩溃了:

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:


  • 离线

  • 在Wi-Fi上

我们已附上详细的崩溃日志以帮助解决此问题。

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

这是login.js:

Here is the 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:

Here is 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:

And here is 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);

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

推荐答案

您应该测试您的应用程序的ipv6兼容性。这是解释如何做到这一点的教程。

You should test your app for ipv6 compatibility. Here is a tutorial that explains how to do that.



  • 启动OS X 10.11

  • 制作确保您的Mac已连接到互联网,但不是通过Wi-Fi。

  • 从Dock,LaunchPad或Apple菜单启动系统偏好设置。

  • 按Option键,然后单击共享。请勿释放Option键。

  • 在共享服务列表中选择Internet共享。

  • 释放Option键。

  • 选择Create NAT64 Network复选框。

  • 选择提供Internet连接的网络接口,例如Thunderbolt Ethernet。

  • 选择Wi-Fi复选框。

  • 单击Wi-Fi选项,然后配置网络的网络名称和安全选项。

  • 设置本地Wi-Fi网络选项

  • 选择Internet共享复选框以启用本地网络。

  • 当提示您确认要开始共享时,请单击开始。

  • 一旦共享处于活动状态,您应该会看到一个绿色状态指示灯和一个标记为Internet Sharing:On的标签。在Wi-Fi菜单中,您还会看到一个向上的小箭头,表示已启用Internet共享。您现在拥有IPv6 NAT64网络,可以从其他设备连接到它以测试您的应用。

  • 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.

这篇关于React Native:App Store拒绝,支持IPv6的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆