IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    Re: 从零开始的 React Native 之旅(一)

    Hyoban发表于 2024-07-21 16:48:51
    love 0

    为什么要写 rn

    一个是我自己还没正儿八经地写过 rn,想试试它的体验怎么样。加上最近对 Follow 这个 RSS 阅读器很感兴趣,但是它暂时还没移动端,可以作为我边学习边实践的对象。再有就是最近开始上班了,自己老是没什么动力在下班后写点想写的代码,有个目标更容易让自己专注。

    同时也立个 flag,把学习和开发的过程写成每周更新的小博客,欢迎大家关注。

    准备工作

    hello world

    好了,废话不多说,让我们跑起来第一个 app 吧。不过正所谓 “工欲善其事,必先利其器”,我们先准备好环境。

    一般来说你只需要安装好 Xcode 就行了,不过如果你像我一样,最近升级了 macOS beta 的话,就会麻烦一些:

    1. App Store 里的 Xcode 是不能打开的,和系统版本不匹配。
    2. 下载完的 Xcode beta 没办法直接打开,提示 the plug-in or one of its prerequisite plug-ins may be missing or damaged and may need to be reinstalled.,需要手动安装 Xcode.app/Contents/Resources/Packages/ 下的安装包。参见 https://forums.developer.apple.com/forums/thread/660860
    3. 命令行中需要 select 到你在用的 beta 版 Xcode,xcode-select -s /Applications/Xcode-beta.app。

    然后就是需要一个 nice 的脚手架,我不太熟悉 rn 这边的技术栈,看完 State of React Native 就选择了之前在 Twitter 上看到的 Create Expo Stack。

    它除了作为一个 expo 项目的脚手架之外,还给你提供了很多主流技术栈的组合选项,这对于我想尽快开始写 app 非常友好。最终我选择的组合是:

    npx create-expo-stack@latest follow-app --expo-router --tabs --tamagui --pnpm --eas
    

    处理深色模式

    脚手架默认为只支持浅色模式,强迫症表示不能接受,所以首先处理一下先。参考这个 issue,我需要修改 expo 的设置为:

    {
      "expo": {
        "userInterfaceStyle": "automatic",
        "ios": {
          "userInterfaceStyle": "automatic"
        },
        "android": {
          "userInterfaceStyle": "automatic"
        }
      }
    }
    

    然后你的 useColorScheme 就能正常获得用户当前选择的主题模式。不过需要注意的是,修改完这个配置,你需要再执行一次 expo prebuild,确保 Info.plist 文件里 key 为 UIUserInterfaceStyle 的值为 Automatic。

    正戏开始

    好了,现在我们来写 Follow app 吧!

    登录账号

    虽然 expo 文档有很详细的 Authentication 接入文档,但我们不需要使用它。 Follow 的网页端已经处理好了,我们只需要调用网页端的登录,为 app 注册处理网页登录后会跳转的 scheme 链接就好。

    首先设置好 app 的 scheme,在 app config 里面设置 scheme: 'follow',然后运行一下 expo prebuild。

    用 expo-web-browser 打开 Follow 登录页面:

    await WebBrowser.openBrowserAsync('https://dev.follow.is/login')
    

    然后用 expo-linking 注册 url 的监听事件,在接收到登录网页调起的 url 信息后,解析里面的 token。

    Linking.addEventListener('url', ({ url }) => {
      const { hostname, queryParams } = Linking.parse(url)
      if (hostname === 'auth' && queryParams !== null && typeof queryParams.token === 'string') {
        WebBrowser.dismissBrowser()
        if (Platform.OS !== 'web') {
          SecureStore.setItemAsync(SECURE_AUTH_TOKEN_KEY, queryParams.token)
        }
      }
    })
    

    这里还遇到的一个问题是 iPhone 上 Safari 的异步函数里的 window.open 会无效,需要加上 target="_top" 的参数。参考 https://stackoverflow.com/q/20696041/15548365

    因为 url 会跳到 auth 这个页面,我们可以加个让它跳到主页的路由 app/auth.tsx。

    import { router } from 'expo-router'
    
    export default function Auth() {
      router.navigate('/')
      return null
    }
    

    OK,这样我们就已经能够获取到用户的认证凭据了。来试试调个接口看看。

    获取用户信息

    在 rn 中发起网络请求看起来和 web 没有区别,我们仍然可以使用自己喜欢的库。

    function useSession() {
      return useSWR(URL_TO_FOLLOW_SERVER, async (url) => {
        const authToken = await SecureStore.getItemAsync(SECURE_AUTH_TOKEN_KEY)
        const response = await fetch(url, {
          headers: {
            cookie: `authjs.session-token=${authToken}`,
          },
          credentials: 'omit',
        })
        const data = (await response.json()) as Session
        return data
      })
    }
    

    这里我暂时做了一点反常的设置,是因为 rn 中基于 cookie 的身份验证存在一些 已知的问题,如果不设置 credentials: 'omit' 的话,就会在第二次请求时设置不正确的 cookie,导致请求失败。这里是参考 https://github.com/facebook/react-native/issues/23185#issuecomment-1148130842 的做法。

    有了数据我们就可以渲染页面,这里先简单写写:

    export default function UserInfo() {
      const { data: session, mutate } = useSession()
    
      return (
        <YStack flex={1} padding={20}>
          {session ? (
            <YStack>
              <XStack gap={24} alignItems="center">
                <Image
                  source={{
                    uri: session.user.image,
                    height: 100,
                    width: 100,
                  }}
                  borderRadius={50}
                />
                <YStack gap={8}>
                  <Text color="$color12" fontSize="$8" fontWeight="600">
                    {session.user.name}
                  </Text>
                  <Text color="$color12" fontSize="$5">
                    {session.user.email}
                  </Text>
                </YStack>
              </XStack>
            </YStack>
          ) : (
            <Button onPress={handlePressButtonAsync}>Login</Button>
          )}
        </YStack>
      )
    }
    

    好了,来看看现在的效果。

    啊哦,看起来 Follow 的网页端还需要做点移动端适配,我又可以水 PR 了。

    总结

    总算是有个能跑的软件了,下周要做什么呢?或许是给 app 设置数据库?写 app 的话还是希望我们可以在弱网甚至无网环境中正常浏览我们的订阅。欢迎大家聊聊自己的想法。



沪ICP备19023445号-2号
友情链接