DEV Community

Discussion on: The guide to implement Push Notifications with React Native, Expo and AWS Amplify

Collapse
 
jackatdev profile image
Jackson Santos • Edited

Hello rpostulart, thank you for your guides. Much appreciated.
I would like to share some thoughts.
If we only consider push notifications then Pinpoint isn't really necessary right? Either ExpoServer.
You could go straight ahead and send the payload via fetch in your lambda, like in the Expo Docs:

const message = {
      to: this.state.expoPushToken,
      sound: 'default',
      title: 'Original Title',
      body: 'And here is the body!',
      data: { data: 'goes here' },
      _displayInForeground: true,
    };
    const response = await fetch('https://exp.host/--/api/v2/push/send', {
      method: 'POST',
      headers: {
        Accept: 'application/json',
        'Accept-encoding': 'gzip, deflate',
        'Content-Type': 'application/json',
      },
      body: JSON.stringify(message),
    });
Enter fullscreen mode Exit fullscreen mode

So I assume you have included pinpoint only to demonstrate how these tools can be combined in a user engagement case. But unlikely the email delivery you would still not be able to track whether your push notification has been delivered or not, right? Or am I missing something?

Some observations in case people don't figure it out:

  • This scenario will only work in the Expo Client in Android. For the APK you will need to send push notifications via FCM docs.expo.io/versions/v37.0.0/guid...
  • On IOS push notifications will only show if the app is on the background.
  • There is a missing statement in the PolicyDocument in lambaexecutionpolicy from pinpoint-cloudformation-template.json, this needs to be added in order to be able to create an app in pinpoint:
{
    "Effect": "Allow",
    "Action": [
        "mobiletargeting:*"
    ],
    "Resource": {
        "Fn::Sub": [
            "arn:aws:mobiletargeting:*:${account}:apps/*",
            {
                "account": {
                    "Ref": "AWS::AccountId"
                }
            }
        ]
    }
}
Enter fullscreen mode Exit fullscreen mode

Thanks rpostulart!

Collapse
 
pjsandwich profile image
Patrick Prioletti • Edited

In addition to the missing policy, there is an error in the way the Expo Push Token is written to the API in ./src/Main.js.

class Main extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      profile: {},
      message: "",
      user: ""
    };
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  async componentDidMount() {
    const user = await Auth.currentSession()
      .then(data => {
        this.setState({ user: data.idToken.payload.sub });
        return data.idToken.payload.sub;
      })
      .catch(err => console.log(err));
    const profile = await this.getUserProfile(user);
    // There is no expoToken available yet, so we will request that and save it into the profile
    if (profile.expoToken === null) {
      const { status } = await Notifications.requestPermissionsAsync();
      if (status !== "granted") {
        alert("No notification permissions!");
        return;
      }

      let token = await Notifications.getExpoPushTokenAsync();
      // Only update the profile with the expoToken if it not exists yet
      if (token !== "") {
        const inputParams = {
          id: user,
          expoToken: token
        };
        await API.graphql(
          graphqlOperation(mutations.updateUser, { input: inputParams })
        )
          .then(result => {
            console.log(result);
          })
          .catch(err => console.log(err));
      }
    }
  }

  async getUserProfile(sub) {
    const result = await API.graphql(
      graphqlOperation(queries.getUser, { id: sub })
    )
      .then(result => {
        this.setState({
          profile: result.data.getUser
        });
        return result.data.getUser;
      })
      .catch(err => console.log(err));
    return result;
  }

  async handleSubmit() {
    const inputParams = {
      message: this.state.message,
      token: this.state.profile.expoToken,
      name: this.state.profile.name,
      email: this.state.profile.email,
      id: this.state.user
    };
    await API.graphql(
      graphqlOperation(mutations.pinpoint, { input: inputParams })
    )
      .then(result => {
        console.log(result);
        console.log("success");
        this.setState({ message: "" });
      })
      .catch(err => console.log(err));
  }

  render() {
    return (
      <View style={{ marginTop: 80, marginLeft: 10, marginRight: 10 }}>
        <TextInput
          placeholder="Your push message"
          value={this.state.message}
          onChangeText={input => this.setState({ message: input })}
          style={{
            paddingLeft: 5,
            height: 40,
            fontSize: 16,
            marginBottom: 6,
            marginTop: 2
          }}
        ></TextInput>
        <Button title="Submit" onPress={this.handleSubmit} />
      </View>
    );
  }
}

export default Main;
Enter fullscreen mode Exit fullscreen mode

Where we define token, the cange is token -> token.data:

      let token = await Notifications.getExpoPushTokenAsync();
      // Only update the profile with the expoToken if it not exists yet
      if (token !== "") {
        const inputParams = {
          id: user,
          expoToken: token.data
        };
        await API.graphql(
          graphqlOperation(mutations.updateUser, { input: inputParams })
        )
          .then(result => {
            console.log(result);
          })
          .catch(err => console.log(err));
      }
    }
Enter fullscreen mode Exit fullscreen mode