Я всегда был бэкенд-инженером по распределенным системам, но в последнее время у меня возникло желание научиться кое-чему Frontend-разработке. Изучая текущий ландшафт создания приложений, я хотел описать свой опыт работы с Expo, инструментом, который поможет вам запустить кроссплатформенное приложение в React за считанные минуты.

Раньше мой опыт создания чего-то кроссплатформенного, т. е. создания и работы на нескольких платформах, таких как Web, iOS и Android, был довольно болезненным. Некоторыми из этих болевых точек были настройка среды разработки, изучение тонкостей того, как работают собственные разрешения приложений, отладка проблем, которые возникают на одной платформе, но не возникают на другой, и некоторые обыденные вещи, такие как одна платформа вызывает кнопку Button, а другая UIButton. Приходя из мира JVM, где не имело значения, работает ли приложение на Windows, Linux или Mac, разработка фронтенда стала совсем другой и довольно скучной для продолжения. Но открытие таких инструментов, как Expo, постепенно меняет мое восприятие предмета.

Давайте построим что-нибудь

Вместо того, чтобы пытаться продать Expo словами, позвольте мне просто продемонстрировать, как легко начать работу, создать что-то интересное и протестировать его на разных платформах менее чем за 30 минут.

Давайте создадим приложение, которое позволит мне сделать снимок с помощью моего устройства iOS, а затем поделиться им на всех моих устройствах, т. е. я могу просматривать изображение в веб-браузере или везде, где запущено приложение. Архитектура может быть такой же простой, как приложение React-Native Frontend, работающее на моем телефоне, а Backend — это Amazon S3, содержащий изображения. Кроме того, давайте установим ожидания от демонстрации, я не собираюсь публиковать приложение в App/Play Store, что можно легко сделать и с помощью Expo, и приложение будет работать только в моей локальной сети и не будет общедоступно.

Шаг 1: Получение необходимых инструментов

С моего компьютера,

  1. Скачать Node и NPM: Установка по ссылке.
  2. Скачать Экспо: Установка по ссылке.
  3. Скачать Amplify: Установка по ссылке.

С моего айфона,

  1. Скачать приложение Expo: Установка по ссылке.
  2. Вот версия приложения для Android, если я хочу запустить его на телефоне Android.

Шаг 2: Создание приложения

С моего компьютера давайте сначала создадим выставочный проект и протестируем его. Чтобы создать и запустить выставочный проект, мне нужно сделать следующее:

$ expo init my-image-share-app
$ > blank (TypeScript)  same as blank but with TypeScript configuration
$ cd my-image-share-app
$ expo start --tunnel

Когда проект Expo запускается, он запускает веб-сервер для простого тестирования и выдает QR-код.

Теперь с моего iPhone я использую приложение «Камера» для сканирования QR-кода, который вызывает всплывающее окно для открытия приложения Expo, при этом приложение открывается так, как если бы мы просто установили его как отдельное приложение. Сейчас их немного, но скоро я буду щелкать картинки и просматривать их со своего компьютера.

Затем давайте добавим плагин Image Capture, который возьмет на себя всю тяжелую работу по запросу разрешений у пользователя на использование своей камеры и фактическую работу с ОС, чтобы открыть приложение «Камера» и использовать изображение, по которому щелкнули. Чтобы установить плагин,

$ expo install expo-image-picker

Здесь я могу найти пользовательский документ.

Открытие файла App.tsx и добавление бита для запроса разрешения на использование камеры, кнопки для фотографирования и последующего отображения в приложении.

import React, { useState, useEffect } from 'react';
import { View, Button, Image, Platform } from "react-native";
import * as ImagePicker from 'expo-image-picker';
export default const App = () => {
    // 1. Storing the image in memory.
    const [image, setImage] = useState(null);
// 2. Asking for permission to use the Camera App.
    useEffect(() => {
        (async () => {
            if (Platform.OS !== 'web') {
                const { status } = await ImagePicker.requestCameraPermissionsAsync();
                if (status !== 'granted') {
                    alert('Sorry, we need permissions to use your camera to make this work!')
                }
            }
        })();
    }, []);
// 3. Clicking Picture with Image Picker Plugin.
    const takePicture = async () => {
        let result = await ImagePicker.launchCameraAsync({
            mediaTypes: ImagePicker.MediaTypeOptions.All,
            allowsEditing: true,
            aspect: [4, 3],
            quality: 1
        });
if (!result.cancelled) {
            setImage(result.uri);
        }
    };
// 4. The Simple Interface with a Button to click a picture, and viewing it.
    return(
        <View>
            <Button
                title="Take a picture"
                onPress={takePicture}
            />
            {
                image &&
                <Image
                    source={{ uri: image }}
                    style={{ width: 200, height: 200 }}
                />
            }
        </View>
    );
};

Шаг 3. Настройка Amplify и Amazon S3

Amplify позволит мне легко интегрировать приложение с S3. Поскольку я просто демонстрирую Expo, я не буду включать настройку Amplify и Amazon S3. Но я нашел отличную статью от AWS, которая описывает этот процесс. Чтобы избежать путаницы, я пропущу шаги по созданию приложения React и установке aws-amplify и перейду к Инициализации проекта Amplify. Я могу просто инициировать проект amplify в том же каталоге, где находится наш проект Expo, который называется my-image-share-app.

$ ampify init

Затем, следуя остальной части статьи до конца настроек CORS корзины обновления S3, я смогу настроить Amazon S3.

Шаг 4: Сохранение и получение изображений из S3

Я буду использовать другой плагин для работы с Amplify. Чтобы добавить, что я побегу,

$ expo install aws-amplify

Опять же, чтобы найти пользовательский документ. Мне также нужно установить еще несколько зависимостей работы aws-amplify с реакцией. Давайте избавимся от этого,

$ expo install @react-native-async-storage/async-storage
$ expo install @react-native-community/netinfo

Добавление части для хранения изображений в S3.

...
import Amplify, { Storage } from 'aws-amplify';
import awsmobile from './aws-exports';
Amplify.configure(awsmobile);
export default const App = () => {
    ....
// 3. Clicking Picture with Image Picker Plugin.
    const takePicture = async () => {
        let result = await ImagePicker.launchCameraAsync({
            mediaTypes: ImagePicker.MediaTypeOptions.All,
            allowsEditing: true,
            aspect: [4, 3],
            quality: 1
        });
if (!result.cancelled) {
            const img = await fetch(result.uri);
            const imgBlob = await img.blob();
            await uploadToS3(imgBlob);
        }
    };
// 4. Upload clicked picture to S3
    const uploadToS3 = async (imgBlob) => {
        Storage
            .put("image1.jpg", imgBlob, {
                level: "public",
                contentType: "image/jpeg" 
            }).then((response) => {
                console.log(response.key);
                return Storage.get(response.key);
            }).then((imageURL) => {
                console.log(imageURL);
                setImage(imageURL);
            }).catch((error) => {
                console.log(error);
            });
    };
....
};

Теперь позвольте мне добавить кнопку обновления, чтобы получить изображение из S3, которое было нажато последним и сохранено в S3.

...
export default const App = () => {
    ....
    
    
    // 5. Retrieve previously stored picture from S3
    const retrieveImage = async () => {
        Storage
            .get("image1.jpg")
            .then((imageURL) => {
                console.log(imageURL);
                setImage(imageURL);
            })
            .catch((error) => {
                console.log(error);
            });
    };
// 6. The Simple Interface with a Button to click a picture, a Refresh Button to retrieve last clicked picture and viewing it.
    return(
        <View>
            <Button
                title="Take a picture"
                onPress={takePicture}
            />
            <Button
                title="Refresh"
                onPress={retrieveImage}
            />
            {
                image &&
                <Image
                    source={{ uri: image }}
                    style={{ width: 200, height: 200 }}
                />
            }
        </View>
    );
};

Хороший! Позвольте мне добавить весь App.tsx со всеми изменениями.

import React, { useState, useEffect } from 'react';
import { View, Button, Image, Platform } from "react-native";
import * as ImagePicker from 'expo-image-picker';
import Amplify, { Storage } from 'aws-amplify';
import awsmobile from './aws-exports';
Amplify.configure(awsmobile);
export default const App = () => {
    // 1. Storing the image in memory.
    const [image, setImage] = useState(null);
// 2. Asking for permission to use the Camera App.
    useEffect(() => {
        (async () => {
            if (Platform.OS !== 'web') {
                const { status } = await ImagePicker.requestCameraPermissionsAsync();
                if (status !== 'granted') {
                    alert('Sorry, we need permissions to use your camera to make this work!')
                }
            }
        })();
    }, []);
// 3. Clicking Picture with Image Picker Plugin.
    const takePicture = async () => {
        let result = await ImagePicker.launchCameraAsync({
            mediaTypes: ImagePicker.MediaTypeOptions.All,
            allowsEditing: true,
            aspect: [4, 3],
            quality: 1
        });
if (!result.cancelled) {
            const img = await fetch(result.uri);
            const imgBlob = await img.blob();
            await uploadToS3(imgBlob);
        }
    };
// 4. Upload clicked picture to S3
    const uploadToS3 = async (imgBlob) => {
        Storage
            .put("image1.jpg", imgBlob, {
                level: "public",
                contentType: "image/jpeg" 
            }).then((response) => {
                console.log(response.key);
                return Storage.get(response.key);
            }).then((imageURL) => {
                console.log(imageURL);
                setImage(imageURL);
            }).catch((error) => {
                console.log(error);
            });
    };
// 5. Retrieve previously stored picture from S3
    const retrieveImage = async () => {
        Storage
            .get("image1.jpg")
            .then((imageURL) => {
                console.log(imageURL);
                setImage(imageURL);
            })
            .catch((error) => {
                console.log(error);
            });
    };
// 6. The Simple Interface with a Button to click a picture, a Refresh Button to retrieve last clicked picture and viewing it.
    return(
        <View>
            <Button
                title="Take a picture"
                onPress={takePicture}
            />
            <Button
                title="Refresh"
                onPress={retrieveImage}
            />
            {
                image &&
                <Image
                    source={{ uri: image }}
                    style={{ width: 200, height: 200 }}
                />
            }
        </View>
    );
};

Шаг 5. Тестирование приложения

Теперь позвольте мне открыть приложение в моем браузере, перейдя по URL-адресу, с которого expo запускает сервер разработки.

Я могу видеть изображения, которые я нажимаю на своем iPhone, используя приложение, работающее в приложении Expo.

Шаг 6: Очистка

Наконец, я подниму все AWS, созданные Amplify. Для этого я просто побегу,

$ усилить удалить

Amplify значительно упростила мою жизнь в части предоставления, интеграции и очистки всех связанных ресурсов AWS.

Буду ли я рекомендовать Экспо?

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

  1. Инструмент приносит деньги? Что еще более важно, как разработчики этого инструмента получают деньги?
  2. Сколько компаний активно используют этот инструмент для своего производственного варианта использования? Или в настоящее время инструмент поддерживает крупная технологическая компания?
  3. Насколько легко найти ресурсы для инструмента?

Что касается первого вопроса, то его поддерживает крупное предприятие, которое пользуется поддержкой некоторых крупных технологических инвесторов, таких как Пол Грэм и Y Combinator. Я могу прочитать об этом здесь, на странице О нас. Далее, компании, которые его используют, варьируются от крупных государственных организаций до небольших стартапов, я вижу это на странице Клиенты. И, наконец, за этим стоит сильное сообщество, поддерживающее его через этот Форум и через Discord. Expo отвечает всем моим требованиям, и я обязательно буду использовать его для своих будущих проектов, работающих в продакшене, и буду рекомендовать его всем, кто хочет облегчить себе жизнь, когда дело доходит до создания и тестирования кроссплатформенных приложений.