Skip to content

Commit

Permalink
add ios user confirmation
Browse files Browse the repository at this point in the history
  • Loading branch information
juliandramirez committed May 8, 2020
1 parent edcbbf3 commit 72d6d51
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 38 deletions.
66 changes: 53 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ This library aims to provide all of the above in the simplest way possible:

<br>
Note: If you are already using firebase authentication see the FAQ for instructions on how to upgrade its anonymous authentication
<br>
Note: If you are considering using Sign In with Apple see the FAQ section first

## Why shouldn't you use this?
This package requires you to activate the iCloud entitlement on iOS. <br>
As of today apple does not permit transfering ownership of an application that has this entitlement active for **ANY** of it's versions. Read more [here](https://help.apple.com/app-store-connect/#/devaf27784ff) <br>
<br>
If you want to transfer your app in the future and you still want to use this package, consider using an apple developer account exclusively for the app and when the time comes just give away the account.
If you want to transfer your app in the future and you still want to use this package consider using an apple developer account exclusively for the app, and when the time comes transfer the account.<br>
A personal apple developer account can be changed to a corporate account if needed, as is most of its information.

## Installation
Expand Down Expand Up @@ -123,25 +125,58 @@ If you have other UI fragments that you want to trigger the account selection di

* There is only one public function available called ***getUserId***
* The function is marked as ***async***
* On **ios** the resolved value is ***null*** when there is no icloud account configured
* On **android** the resolved value is ***null*** when the user dismisses the account selection dialog or when there is not an account configured in the device

* The resolved value is ***null*** when the user cancels the UI flow
* On **ios** the function will throw ICLOUD_ACCESS_ERROR when there is no icloud account configured
```javascript
import RNUserIdentity from 'react-native-user-identity'
import RNUserIdentity, { ICLOUD_ACCESS_ERROR } from 'react-native-user-identity'

fetchUserIdentity = async () => {
const result = await RNUserIdentity.getUserId()

if (result === null) {
if (Platform.OS === 'ios') {
alert('Please set up an iCloud account in settings')
} else if (Platform.OS === 'android') {
alert('Please select an existing account or create a new one')
}
try {
const result = await RNUserIdentity.getUserId()
if (result === null) {
alert('User canceled UI flow')
}
} catch(error) {
if (error === ICLOUD_ACCESS_ERROR) {
alert('Please set up an iCloud account in settings')
}
}
}
```

### IOS user confirmation

On iOS fetching the user id does not require user intervention. However, it might be useful in some instances to have the user confirm the action.<br>
You may send a *truthy* value for the **iosUserConfirmation** parameter for this to happen.

The following code:

```javascript
RNUserIdentity.getUserId({
iosUserConfirmation: true
})
```

Presents this dialog:

![alt text](https://raw.githubusercontent.com/juliandramirez/react-native-user-identity/master/docs/img/ios-user-confirmation.png)

The resolved value will be ***null*** if the user dismisses the dialog
<br><br>
You can also configure the text shown to the user:

```javascript
RNUserIdentity.getUserId({
iosUserConfirmation: {
title: 'Confirm sign in',
message: 'Sign in requires user confirmation',
signInButtonText: 'Confirm',
cancelButtonText: 'Back'
}
})
```

### Android account chooser options

There is an optional parameter you can send:
Expand All @@ -168,6 +203,11 @@ Use yarn to install the dependencies. Npm installs local dependencies using symb
### Why can't we get the iCloud email and instead we get this long obfuscated string?
The CloudKit framework prevents applications from accesing the user email for privacy purposes.

### How is this different from Sign In with apple on iOS?
Sign in with Apple requires the user to complete a full sign in flow.<br>
The point of using this package is to skip entirely this flow so your users can directly start using your application<br>
Furthermore, activating sign in with Apple also prevents your app from being transferable (see the **Why shouldn't you use this?** section above)

### I can not make this work on iOS...
Make sure you followed all of the steps in the installation and configuration section and pay attention to the verification note at the end of the configuration section

Expand Down
Binary file added docs/img/ios-user-confirmation.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 20 additions & 14 deletions example/js/App.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import React, {Component} from 'react'
import {Platform, StyleSheet, Text, View, Button} from 'react-native'
import {Platform, StyleSheet, Text, View, Button, Alert} from 'react-native'

import RNUserIdentity from 'react-native-user-identity'
import RNUserIdentity, { ICLOUD_ACCESS_ERROR } from 'react-native-user-identity'


export default class App extends Component {
Expand All @@ -12,20 +12,26 @@ export default class App extends Component {
}

buttonPress = async () => {

const result = await RNUserIdentity.getUserId({
androidAccountSelectionMessage: 'Choose an account for testing:'
})

if (result === null) {
if (Platform.OS === 'ios') {
alert('Please set up an iCloud account in settings')
} else if (Platform.OS === 'android') {
alert('Please select an existing account or create a new one')
try {
const result = await RNUserIdentity.getUserId({
androidAccountSelectionMessage: 'Choose an account for testing:',
iosUserConfirmation: {
title: 'Confirm sign in',
message: 'Sign in requires user confirmation',
signInButtonText: 'Confirm',
cancelButtonText: 'Back'
}
})
if (result === null) {
this.setState({result: 'User canceled sign in flow'})
} else {
this.setState({result: result})
}
} catch(error) {
if (error == ICLOUD_ACCESS_ERROR) {
this.setState({result: 'Please set up an iCloud account in settings'})
}
}

this.setState({result: result})
}

render() {
Expand Down
2 changes: 1 addition & 1 deletion example/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5456,7 +5456,7 @@ react-is@^16.12.0, react-is@^16.8.1, react-is@^16.8.4, react-is@^16.9.0:
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==

"react-native-user-identity@file:..":
version "1.3.2"
version "1.4.1"

[email protected]:
version "0.61.5"
Expand Down
54 changes: 45 additions & 9 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,33 +1,69 @@

import { Platform, NativeModules } from 'react-native'
import { Platform, NativeModules, Alert } from 'react-native'

const { RNUserIdentity } = NativeModules


export const ICLOUD_ACCESS_ERROR = 'ICLOUD_ACCESS_ERROR'
export default {
getUserId: async ({ androidAccountSelectionMessage } = {
androidAccountSelectionMessage: null
getUserId: async ({ androidAccountSelectionMessage, iosUserConfirmation } = {
androidAccountSelectionMessage: null,
iosUserConfirmation: null
}) => {
if (Platform.OS === 'ios') {

const showUserConfirmation = () => new Promise((resolve, reject) => {
const {
title = 'Sign in with iCloud',
message = 'Your iCloud account will be used to sign in',
signInButtonText = 'Sign In',
cancelButtonText = 'Cancel'
} = iosUserConfirmation

Alert.alert(
title,
message,
[{
text: signInButtonText,
onPress: () => {
RNUserIdentity.getUserIdentity()
.then(value => resolve(value))
.catch(error => {
reject(error)
})
}
}, {
text: cancelButtonText,
onPress: () => resolve(null),
style: 'cancel',
}]
)
})

try {
return await RNUserIdentity.getUserIdentity();
if (iosUserConfirmation) {
return await showUserConfirmation()
} else {
return await RNUserIdentity.getUserIdentity()
}
} catch (error) {
if (error && error.code == 'NO_ACCOUNT_ACCESS_ERROR') {
// there is no account configured...
return null;
throw ICLOUD_ACCESS_ERROR
} else {
throw error;
throw error
}
}
} else if (Platform.OS === 'android') {
try {
return await RNUserIdentity.triggerAccountSelection(androidAccountSelectionMessage, 'com.google');
return await RNUserIdentity.triggerAccountSelection(androidAccountSelectionMessage, 'com.google')
} catch (error) {
if (error && error.code == 'USER_CANCELED_ACCOUNT_SELECTION') {
// user cancelled the account selection process...
return null;
return null
} else {
// should not happen as long as an activity is available...
throw error;
throw error
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "react-native-user-identity",
"version": "1.3.2",
"version": "1.4.1",
"description": "Get the user id configured for the device (iCloud recordID for iOS, email of a device account for android)",
"main": "index.js",
"author": "Julian Ramírez <[email protected]>",
Expand All @@ -13,6 +13,7 @@
"react-native",
"react-native-user-identity",
"ios",
"apple",
"android",
"fetchUserRecordID",
"CloudKit",
Expand Down

0 comments on commit 72d6d51

Please sign in to comment.