Push-уведомления Expo - когда приложение находится на переднем плане, оно вылетает iOS - PullRequest
0 голосов
/ 25 августа 2018

У меня есть приложение Expo, и я пытаюсь обрабатывать push-уведомления, отправленные, когда приложение находится на переднем плане. Он отлично работает в Android, но iOS вылетает из приложения по мере получения.

У меня есть push-уведомление с сервера Rails:

            params = ({
              to: token.expo_push_token,
              title: user.first_name,
              sound: "default",
              body: msg.body,
            })


            puts params
            puts params.class

            x = Net::HTTP.post_form(URI.parse('https://exp.host/--/api/v2/push/send'), params)
            puts x.body

Я вижу на сервере, что отправляет:

Hash
app[worker.1]: {"data":{"id":"9058abf3-7352-4181-a69d-0b5fc8a8525c","status":"ok"}}
4 TID-godk4ew98 ExpoPushWorker JID-51b823f8feeaf42c313e392e INFO: done: 2.005 sec

А если приложение закрыто, на экране блокировки появляется push-уведомление. Если приложение открыто на переднем плане, ничего не происходит.

Я хочу прослушивать уведомления, когда приложение открыто, и у меня есть это в App.js:

import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import MessagesScreenRouter from './app/components/Router/MessagesScreenRouter';
import Sentry from 'sentry-expo';
import reducers from './app/reducers';

import { Provider } from 'react-redux';
import { createStore } from 'redux';
import { Notifications } from 'expo';


export default class App extends React.Component {

  constructor(props) {
    super(props)
    this.state = {
      notification: {},
    }
    this._handleNotification = this._handleNotification.bind(this)
  }

  _handleNotification = (notification) => {
       console.log(notification)
       this.setState({notification: notification});
   };

  componentWillUnmount(){
    this._notificationSubscription && this._notificationSubscription.remove()
  }

  componentDidMount() {    
    this.notificationSubscription = Notifications.addListener(
      (notification) => this._handleNotification(notification),
    );
  }


  render() {
    return (
      <View style={{flex:1}}>
        <StatusBar hidden={true} />

        <View style={{height: 50, justifyContent: 'center', alignItems: 'center'}}>
          <Text>Origin: {this.state.notification.origin}</Text>
          <Text>Data: {JSON.stringify(this.state.notification)}</Text>
        </View>

        <Provider store={createStore(reducers)}>
            <MessagesScreenRouter/>
        </Provider>
      </View>
    );
  }
}

Я перепробовал много предложений из учебных пособий весь день, но не могу заставить это работать. Что мне здесь не хватает?

Ответы [ 2 ]

0 голосов
/ 26 января 2019

Экспо, вероятно, было обновлено в этом аспекте с тех пор, и теперь оно может даже использовать команду, которую вы упомянули в комментарии (exp start -m tunnel). Поскольку передний план уведомлений до сих пор недоступен в iOS (что, возможно, могло даже вызвать вашу проблему в первую очередь), этот ответ скорее предназначен для людей, желающих внедрить push-уведомления, чем для устранения проблемы, описанной выше.

Я создал загрузчик файлов и средство предварительного просмотра, которые отображают как внутренние, так и внешние уведомления в обеих ОС, не сталкиваясь с такими проблемами. Код доступен на GitHub , а подробное описание приведено в этом SO-ответе .

Наиболее релевантный код для этого поста связан с использованием локальных уведомлений от Expo, когда приложение находится в фоновом режиме, и отображением их на переднем плане с использованием тостов из react-native-toast пакет. Эта функция может быть заменена Экспо Уведомлениями, как только эта функция будет реализована.

Для полноты вот код для проекта:

App.js

import React, { Component } from 'react';
import { View, ScrollView, StyleSheet, Button, Alert, Platform, Text, TouchableWithoutFeedback } from 'react-native';
import { FileSystem, Constants, Notifications, Permissions } from 'expo';
import Toast, {DURATION} from 'react-native-easy-toast';

async function getiOSNotificationPermission() {
  const { status } = await Permissions.getAsync(
    Permissions.NOTIFICATIONS
  );
  if (status !== 'granted') {
    await Permissions.askAsync(Permissions.NOTIFICATIONS);
  }
}

export default class App extends Component {
  constructor(props) {
    super(props);
    // this.toast = null;
    this.listenForNotifications = this.listenForNotifications.bind(this);
    // this.openFile = this.openFile.bind(this);
    this.state = {
      filePreviewText: ''
    }
  }

  _handleButtonPress = () => {
    let fileName = 'document.txt';
    let fileUri = FileSystem.documentDirectory + fileName;
    FileSystem.downloadAsync(
      "https://raw.githubusercontent.com/expo/expo/master/README.md",
      fileUri
    ).then(({ uri }) => {
      console.log('Finished downloading to ', uri);

      const localnotification = {
        title: 'Download has finished',
        body: fileName + " has been downloaded. Tap to open file.",
        android: {
          sound: true,
        },
        ios: {
          sound: true,
        },

        data: {
          fileUri: uri
        },
      };
      localnotification.data.title = localnotification.title;
      localnotification.data.body = localnotification.body;
      let sendAfterFiveSeconds = Date.now();
      sendAfterFiveSeconds += 3000;

      const schedulingOptions = { time: sendAfterFiveSeconds };
      Notifications.scheduleLocalNotificationAsync(
        localnotification,
        schedulingOptions
      );
    })
    .catch(error => {
        console.error(error);
        Alert.alert(error);
    });
  };
  listenForNotifications = () => {
    const _this = this;

    Notifications.addListener(notification => {
      if (notification.origin === 'received') {
        // We could also make our own design for the toast
        // _this.refs.toast.show(<View><Text>hello world!</Text></View>);

        const toastDOM = 
          <TouchableWithoutFeedback 
            onPress={() => {this.openFile(notification.data.fileUri)}}
            style={{padding: '10', backgroundColor: 'green'}}>
            <Text style={styles.toastText}>{notification.data.body}</Text>
          </TouchableWithoutFeedback>;

        _this.toast.show(toastDOM, DURATION.FOREVER);
      } else if (notification.origin === 'selected') {
        this.openFile(notification.data.fileUri);
      }
        // Expo.Notifications.setBadgeNumberAsync(number);
        // Notifications.setBadgeNumberAsync(10);
        // Notifications.presentLocalNotificationAsync(notification);
        // Alert.alert(notification.title, notification.body);
    });
  };
  componentWillMount() {
    getiOSNotificationPermission();
    this.listenForNotifications();
  }
  componentDidMount() {
    // let asset = Asset.fromModule(md);
    // Toast.show('Hello World');
  }
  openFile = (fileUri) => {
    this.toast.close(40);
    console.log('Opening file ' + fileUri);
    FileSystem.readAsStringAsync(fileUri)
    .then((fileContents) => {
      // Get file contents in binary and convert to text
      // let fileTextContent = parseInt(fileContents, 2);
      this.setState({filePreviewText: fileContents});
    });
  }
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.buttonsContainer}>
          <Button style={styles.button}
            title={"Download text file"}
            onPress={this._handleButtonPress}
          />
          <Button style={styles.button}
            title={"Clear File Preview"}
            onPress={() => {this.setState({filePreviewText: ""})}}
          />
        </View>
        <ScrollView style={styles.filePreview}>
          <Text>{this.state.filePreviewText}</Text>
        </ScrollView>
        <Toast ref={ (ref) => this.toast=ref }/>
      </View>
    );
            // <Toast
        //   ref={ (ref) => this.toast=ref }
        //   style={{backgroundColor:'green'}}
        //   textStyle={{color:'white'}}
        //   position={'bottom'}
        //   positionValue={100}
        //   opacity={0.8}
        // />
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
    paddingTop: Constants.statusBarHeight,
    backgroundColor: '#ecf0f1',
  },
  buttonsContainer: {
    flexDirection: 'row',
  },
  button: {
    flex: 1
  },
  filePreview: {
    flex: 1,
    padding: 10,
  },
  toastText: {
    color: 'white',
    padding: 5,
    justifyContent: 'flex-start',
  },
});

package.json: в настоящее время используется разветвленное репо, переключиться назад , когда станет доступна функция .

{
  "name": "local-notification-with-ios",
  "version": "0.0.0",
  "description": "No description",
  "author": null,
  "private": true,
  "main": "node_modules/expo/AppEntry.js",
  "dependencies": {
    "expo": "^32.0.0",
    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz",
    "prop-types": "^15.5.7",
    "react-native-easy-toast": "git+https://github.com/SiavasFiroozbakht/react-native-easy-toast.git#6eed52f4d64c796cb49bdafcd7b3986cf5975d62"
  }
}
0 голосов
/ 28 августа 2018

Вы не можете протестировать его на симуляторе, так как экспо-документы указывают

Примечание. Симуляторы iOS и Android не могут получать push-уведомления. Чтобы проверить их, вам нужно использовать реальное устройство. Кроме того, при вызове Permissions.askAsync на симуляторе он немедленно разрешается с неопределенным статусом, независимо от того, хотите вы разрешить или нет.

Просто запустите exp publish и протестируйте его на клиенте expo. Также вы должны позвонить для разрешения, используя Permissions.askAsync в первую очередь.

Образец документа работает как талисман, зацените его: https://docs.expo.io/versions/v28.0.0/guides/push-notifications#__next

...