React Native: отказ от App Store, поддержка IPv6

У меня есть простой реагирующий клиент для веб-сайта, на странице входа в него есть две опции: ввести код входа вручную или отсканировать его с помощью сканера штрих-кода. Я тестировал приложение на реальном устройстве, и эмулятор много раз работал нормально. На самом деле я тестирую только через ipv4, и для входа в систему я использую fetch, который, я думаю, поддерживает ipv6 по умолчанию.

Они говорят по сети ipv6, когда приложение было в автономном режиме, я не могу понять, что значит быть в автономном режиме и быть в сети IPV6?

Когда приложение находится в автономном режиме, я показываю пользователю ошибку, что нет подключения. так что я не знаю, как это может привести к сбою.

следует ли добавить тайм-аут для получения запроса исправить проблему?

Но приложение отклоняется 3 раза из-за той же ошибки:

Производительность - 2.1

Спасибо за ваше повторное представление.

Ваше приложение падает на iPhone под управлением iOS 9.3.3, подключенным к сети IPv6, когда мы:

В частности, нажатие на логин все еще приводит к сбою приложения.

Это произошло, когда ваше приложение использовалось:

Не в сетиПо Wi-Fi

Мы приложили подробные журналы сбоев, чтобы помочь устранить эту проблему.

Вот логин.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 = codeSplit,ed[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);

ОБНОВЛЕНИЕ: вот журнал аварий от Apple:http://www.ataomega.com/temp..suczkfac.crash http://www.ataomega.com/temp..hsbgdlod.crash

 Ismail Iqbal10 окт. 2016 г., 09:13
@Ataomega, вы нашли решение этой проблемы и могу ли я узнать, какую реагирующую версию вы использовали, ваши журналы аварий были удалены.
 Paulw1105 авг. 2016 г., 09:38
В этом аварийном дампе нет значимой обратной трассировки, поэтому я могу лишь сказать, что какой-то код называется «abort ()». Я понятия не имею, какие инструменты отладки / анализа сбоев доступны для JavaScript.
 Ata Mohammadi10 окт. 2016 г., 13:16
Проблема @IsmailIqbal была не из реактивной системы, функция fetch на самом деле поддерживает ipv6. наш сервер не слушал ipv6
 Ata Mohammadi05 авг. 2016 г., 09:35
Не к сожалению. приложение никогда не падает на моих тестах :( вот журнал сбоев:ataomega.com/temp..suczkfac.crash
 Paulw1105 авг. 2016 г., 09:32
В заметке Apple говорится, что ваше приложение зависало, когда они нажимали кнопку «Войти» в автономном режиме и в режиме WiFi; это два отдельных теста, а не в автономном режиме и Wi-Fiв то же время как это не имеет смысла :). Можете ли вы воспроизвести эту ошибку?
 Ata Mohammadi05 авг. 2016 г., 09:48
Спасибо за ваш ответ @ Paulw11

Ответы на вопрос(1)

Решение Вопроса

учебник, который объясняет, как это сделать.

Загрузите OS X 10.11Убедитесь, что ваш Mac подключен к Интернету, но не через Wi-Fi.Запустите Системные настройки из Dock, LaunchPad или из меню Apple.Нажмите клавишу параметров и нажмите «Общий доступ». Пока не отпускайте ключ Option.Выберите Internet Sharing в списке служб обмена.Отпустите клавишу Option.Установите флажок Создать сеть NAT64.Выберите сетевой интерфейс, обеспечивающий подключение к Интернету, например Thunderbolt Ethernet.Установите флажок Wi-Fi.Нажмите Параметры Wi-Fi и настройте имя сети и параметры безопасности для вашей сети.Настройка параметров локальной сети Wi-FiУстановите флажок «Общий Интернет», чтобы включить локальную сеть.Когда будет предложено подтвердить, что вы хотите начать совместное использование, нажмите Пуск.После того, как общий доступ активен, вы должны увидеть зеленый индикатор состояния и метку с надписью Internet Sharing: On. В меню Wi-Fi вы также увидите небольшую слабую стрелку, указывающую вверх, указывающую, что включен общий доступ к Интернету. Теперь у вас есть сеть IPv6 NAT64, и вы можете подключиться к ней с других устройств, чтобы протестировать ваше приложение.
 Paulw1105 авг. 2016 г., 09:32
Ссылки на внешние ресурсы приветствуются, но, пожалуйста, добавьте контекст вокруг ссылки, чтобы ваши коллеги-пользователи имели представление о том, что это такое и почему оно там. Всегда указывайте наиболее релевантную часть важной ссылки, если целевой сайт недоступен или постоянно недоступен.
 Ata Mohammadi05 авг. 2016 г., 09:32
Спасибо за Ваш ответ. я уже сделал это но когда я это сделаю, сам Mac может получить доступ к сайтам ipv6, таким как ipv6.google.com, но iphone, подключенный к mac, не может получить доступ к сайтам ipv6

Ваш ответ на вопрос