expo: [RN 0.66][SDK 44][Android] `expo` package no longer compatible with RN 0.66 (`expo-file-system` issue)

Summary

The package expo-file-system no longer compiles on Android on SDK 44 and react-native 0.66.4

Because expo-asset and expo packages depend on expo-file-system, this affects pretty much every ejected expo project

> Task :expo-file-system:compileDebugKotlin FAILED
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (769, 36): Type mismatch: inferred type is String? but String was expected
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (794, 107): Using 'parse(String): MediaType?' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (819, 40): Using 'body(): ResponseBody?' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (820, 39): Using 'code(): Int' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (821, 60): Using 'headers(): Headers' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (869, 18): Function invocation 'isCanceled()' expected
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (879, 29): Using 'body(): ResponseBody?' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (882, 37): Using 'code(): Int' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (883, 58): Using 'headers(): Headers' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (903, 37): Using 'buffer(Source): BufferedSource' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (903, 49): Using 'source(InputStream): Source' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (906, 27): Using 'buffer(Sink): BufferedSink' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (906, 39): Using 'sink(File): Sink' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (937, 31): Using 'buffer(Sink): BufferedSink' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (937, 43): Using 'sink(File): Sink' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (938, 38): Using 'body(): ResponseBody?' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (942, 43): Using 'code(): Int' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (943, 64): Using 'headers(): Headers' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1006, 57): Using 'body(): ResponseBody?' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1101, 37): Using 'body(): ResponseBody?' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1111, 37): Using 'code(): Int' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1112, 58): Using 'headers(): Headers' is an error. moved to val
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1119, 19): Function invocation 'isCanceled()' expected
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1142, 30): Using 'buffer(Source): BufferedSource' is an error. moved to extension function
e: app/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt: (1307, 31): Using 'size(): Int' is an error. moved to val

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

bare

What platform(s) does this occur on?

Android

SDK Version (managed workflow only)

SDK 44

Environment

  Expo CLI 5.0.1 environment info:
    System:
      OS: macOS 12.0.1
      Shell: 5.8 - /bin/zsh
    Binaries:
      Node: 16.13.1 - /opt/homebrew/Cellar/node@16/16.13.1/bin/node
      Yarn: 1.22.17 - /opt/homebrew/bin/yarn
      npm: 8.1.2 - /opt/homebrew/Cellar/node@16/16.13.1/bin/npm
      Watchman: 2021.12.13.00 - /opt/homebrew/bin/watchman
    Managers:
      CocoaPods: 1.11.2 - /opt/homebrew/bin/pod
    SDKs:
      iOS SDK:
        Platforms: DriverKit 21.2, iOS 15.2, macOS 12.1, tvOS 15.2, watchOS 8.3
      Android SDK:
        API Levels: 29, 30, 31
        Build Tools: 29.0.2, 30.0.2, 31.0.0, 32.0.0
        System Images: android-31 | Google Play ARM 64 v8a
        Android NDK: 22.1.7171670
    IDEs:
      Android Studio: 2020.3 AI-203.7717.56.2031.7784292
      Xcode: 13.2/13C90 - /usr/bin/xcodebuild
    npmPackages:
      expo: ~44.0.0 => 44.0.0 
      react: 17.0.1 => 17.0.1 
      react-dom: 17.0.1 => 17.0.1 
      react-native: 0.66.4 => 0.66.4 
      react-native-web: 0.17.1 => 0.17.1 
    npmGlobalPackages:
      eas-cli: 0.41.1
      expo-cli: 5.0.1
    Expo Workflow: bare

Reproducible demo

  • expo init yonom --yes && cd yonom
  • yarn add react-native@0.66.4
  • expo prebuild --skip-dependency-update=react-native
  • expo run:android

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Reactions: 15
  • Comments: 26 (5 by maintainers)

Commits related to this issue

Most upvoted comments

not a permanent solution but for the time being if you’re not using expo-file-system you can exclude it in your package.json

"expo": {
    "autolinking": {
      "exclude": [
        "expo-file-system"
      ]
    }
  },

published expo-file-system@13.2.1 with the fix. please upgrade and try again. thanks!

In the meantime, I am using the following patch

diff --git a/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt b/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
index 53bf40f..edadd27 100644
--- a/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
+++ b/node_modules/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
@@ -56,18 +56,14 @@ import okhttp3.Callback
 import okhttp3.Headers
 import okhttp3.JavaNetCookieJar
 import okhttp3.MediaType
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
 import okhttp3.MultipartBody
 import okhttp3.OkHttpClient
 import okhttp3.Request
 import okhttp3.RequestBody
 import okhttp3.Response
 import okhttp3.ResponseBody
-
-import okio.Buffer
-import okio.BufferedSource
-import okio.ForwardingSource
-import okio.Okio
-import okio.Source
+import okio.*
 
 import org.apache.commons.codec.binary.Hex
 import org.apache.commons.codec.digest.DigestUtils
@@ -766,7 +762,7 @@ open class FileSystemModule(
       }
 
       val body = createRequestBody(options, decorator, fileUri.toFile())
-      return requestBuilder.method(method, body).build()
+      return method?.let { requestBuilder.method(it, body).build() }
     } catch (e: Exception) {
       e.message?.let { Log.e(TAG, it) }
       promise.reject(e)
@@ -791,7 +787,7 @@ open class FileSystemModule(
         } ?: URLConnection.guessContentTypeFromName(file.name)
 
         val fieldName = options["fieldName"]?.let { it as String } ?: file.name
-        bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(MediaType.parse(mimeType), file)))
+        bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(mimeType.toMediaTypeOrNull(), file)))
         bodyBuilder.build()
       }
       else -> {
@@ -816,9 +812,9 @@ open class FileSystemModule(
 
         override fun onResponse(call: Call, response: Response) {
           val result = Bundle().apply {
-            putString("body", response.body()?.string())
-            putInt("status", response.code())
-            putBundle("headers", translateHeaders(response.headers()))
+            putString("body", response.body?.string())
+            putInt("status", response.code)
+            putBundle("headers", translateHeaders(response.headers))
           }
           response.close()
           promise.resolve(result)
@@ -866,7 +862,7 @@ open class FileSystemModule(
     taskHandlers[uuid] = TaskHandler(call)
     call.enqueue(object : Callback {
       override fun onFailure(call: Call, e: IOException) {
-        if (call.isCanceled) {
+        if (call.isCanceled()) {
           promise.resolve(null)
           return
         }
@@ -876,11 +872,11 @@ open class FileSystemModule(
 
       override fun onResponse(call: Call, response: Response) {
         val result = Bundle()
-        val body = response.body()
+        val body = response.body
         result.apply {
           putString("body", body?.string())
-          putInt("status", response.code())
-          putBundle("headers", translateHeaders(response.headers()))
+          putInt("status", response.code)
+          putBundle("headers", translateHeaders(response.headers))
         }
         response.close()
         promise.resolve(result)
@@ -900,10 +896,10 @@ open class FileSystemModule(
           val resources = context.resources
           val packageName = context.packageName
           val resourceId = resources.getIdentifier(url, "raw", packageName)
-          val bufferedSource = Okio.buffer(Okio.source(context.resources.openRawResource(resourceId)))
+          val bufferedSource = context.resources.openRawResource(resourceId).source().buffer()
           val file = uri.toFile()
           file.delete()
-          val sink = Okio.buffer(Okio.sink(file))
+          val sink = file.sink().buffer()
           sink.writeAll(bufferedSource)
           sink.close()
           val result = Bundle()
@@ -934,13 +930,13 @@ open class FileSystemModule(
             override fun onResponse(call: Call, response: Response) {
               val file = uri.toFile()
               file.delete()
-              val sink = Okio.buffer(Okio.sink(file))
-              sink.writeAll(response.body()!!.source())
+              val sink = file.sink().buffer()
+              sink.writeAll(response.body!!.source())
               sink.close()
               val result = Bundle().apply {
                 putString("uri", Uri.fromFile(file).toString())
-                putInt("status", response.code())
-                putBundle("headers", translateHeaders(response.headers()))
+                putInt("status", response.code)
+                putBundle("headers", translateHeaders(response.headers))
                 if (options?.get("md5") == true) {
                   putString("md5", md5(file))
                 }
@@ -1003,7 +999,7 @@ open class FileSystemModule(
         ?.addNetworkInterceptor { chain ->
           val originalResponse = chain.proceed(chain.request())
           originalResponse.newBuilder()
-            .body(ProgressResponseBody(originalResponse.body(), progressListener))
+            .body(ProgressResponseBody(originalResponse.body, progressListener))
             .build()
         }
         ?.build()
@@ -1098,7 +1094,7 @@ open class FileSystemModule(
       val options = params[0]?.options
       return try {
         val response = call!!.execute()
-        val responseBody = response.body()
+        val responseBody = response.body
         val input = BufferedInputStream(responseBody!!.byteStream())
         val output = FileOutputStream(file, isResume == true)
         val data = ByteArray(1024)
@@ -1108,15 +1104,15 @@ open class FileSystemModule(
         }
         val result = Bundle().apply {
           putString("uri", Uri.fromFile(file).toString())
-          putInt("status", response.code())
-          putBundle("headers", translateHeaders(response.headers()))
+          putInt("status", response.code)
+          putBundle("headers", translateHeaders(response.headers))
           options?.get("md5").takeIf { it == true }?.let { putString("md5", file?.let { md5(it) }) }
         }
         response.close()
         promise?.resolve(result)
         null
       } catch (e: Exception) {
-        if (call?.isCanceled == true) {
+        if (call?.isCanceled() == true) {
           promise?.resolve(null)
           return null
         }
@@ -1139,7 +1135,7 @@ open class FileSystemModule(
     override fun contentLength(): Long = responseBody?.contentLength() ?: -1
 
     override fun source(): BufferedSource =
-      bufferedSource ?: Okio.buffer(source(responseBody!!.source()))
+      bufferedSource ?: source(responseBody!!.source()).buffer()
 
     private fun source(source: Source): Source {
       return object : ForwardingSource(source) {
@@ -1304,7 +1300,7 @@ open class FileSystemModule(
   // Copied out of React Native's `NetworkingModule.java`
   private fun translateHeaders(headers: Headers): Bundle {
     val responseHeaders = Bundle()
-    for (i in 0 until headers.size()) {
+    for (i in 0 until headers.size) {
       val headerName = headers.name(i)
       // multiple values for the same header
       if (responseHeaders[headerName] != null) {

Yonom’s code changes work well. I like to use patch-package to apply them.

  1. Create a patches folder
  2. Create a file named expo-file-system+13.1.0.patch in the patches folder
  3. Copy Yonom’s code changes into that file
  4. Run npx patch-package to test the patch, it should apply the patch
  5. Optional: add patch-package to your project: yarn add -D patch-package
  6. Optional: run patch-package whenever you use npm or yarn by changing your package.json:
  "scripts": {
    ...
    "postinstall": "npx patch-package"
  },

same problem using npx install-expo-modules on react-native init projects.

Is this going to be fixed in a release? Just tried expo for the first time and got this exact issue

Here’s a new patch for expo-file-system@~13.1.0 (some line numbers needed updating):

diff --git a/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt b/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
index 53bf40ff83639413de15de19ee4319c3d71fca99..02039b230d42a8153e607b290d0930815610f8cf 100644
--- a/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
+++ b/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
@@ -55,18 +55,14 @@ import okhttp3.Callback
 import okhttp3.Headers
 import okhttp3.JavaNetCookieJar
 import okhttp3.MediaType
+import okhttp3.MediaType.Companion.toMediaTypeOrNull
 import okhttp3.MultipartBody
 import okhttp3.OkHttpClient
 import okhttp3.Request
 import okhttp3.RequestBody
 import okhttp3.Response
 import okhttp3.ResponseBody
-
-import okio.Buffer
-import okio.BufferedSource
-import okio.ForwardingSource
-import okio.Okio
-import okio.Source
+import okio.*
 
 import org.apache.commons.codec.binary.Hex
 import org.apache.commons.codec.digest.DigestUtils
@@ -766,7 +762,7 @@ open class FileSystemModule(
       }
 
       val body = createRequestBody(options, decorator, fileUri.toFile())
-      return requestBuilder.method(method, body).build()
+      return method?.let { requestBuilder.method(it, body).build() }
     } catch (e: Exception) {
       e.message?.let { Log.e(TAG, it) }
       promise.reject(e)
@@ -791,7 +787,7 @@ open class FileSystemModule(
         } ?: URLConnection.guessContentTypeFromName(file.name)
 
         val fieldName = options["fieldName"]?.let { it as String } ?: file.name
-        bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(MediaType.parse(mimeType), file)))
+        bodyBuilder.addFormDataPart(fieldName, file.name, decorator.decorate(RequestBody.create(mimeType.toMediaTypeOrNull(), file)))
         bodyBuilder.build()
       }
       else -> {
@@ -816,9 +812,9 @@ open class FileSystemModule(
 
         override fun onResponse(call: Call, response: Response) {
           val result = Bundle().apply {
-            putString("body", response.body()?.string())
-            putInt("status", response.code())
-            putBundle("headers", translateHeaders(response.headers()))
+            putString("body", response.body?.string())
+            putInt("status", response.code)
+            putBundle("headers", translateHeaders(response.headers))
           }
           response.close()
           promise.resolve(result)
@@ -866,7 +862,7 @@ open class FileSystemModule(
     taskHandlers[uuid] = TaskHandler(call)
     call.enqueue(object : Callback {
       override fun onFailure(call: Call, e: IOException) {
-        if (call.isCanceled) {
+        if (call.isCanceled()) {
           promise.resolve(null)
           return
         }
@@ -876,11 +872,11 @@ open class FileSystemModule(
 
       override fun onResponse(call: Call, response: Response) {
         val result = Bundle()
-        val body = response.body()
+        val body = response.body
         result.apply {
           putString("body", body?.string())
-          putInt("status", response.code())
-          putBundle("headers", translateHeaders(response.headers()))
+          putInt("status", response.code)
+          putBundle("headers", translateHeaders(response.headers))
         }
         response.close()
         promise.resolve(result)
@@ -900,10 +896,10 @@ open class FileSystemModule(
           val resources = context.resources
           val packageName = context.packageName
           val resourceId = resources.getIdentifier(url, "raw", packageName)
-          val bufferedSource = Okio.buffer(Okio.source(context.resources.openRawResource(resourceId)))
+          val bufferedSource = context.resources.openRawResource(resourceId).source().buffer()
           val file = uri.toFile()
           file.delete()
-          val sink = Okio.buffer(Okio.sink(file))
+          val sink = file.sink().buffer()
           sink.writeAll(bufferedSource)
           sink.close()
           val result = Bundle()
@@ -934,13 +930,13 @@ open class FileSystemModule(
             override fun onResponse(call: Call, response: Response) {
               val file = uri.toFile()
               file.delete()
-              val sink = Okio.buffer(Okio.sink(file))
-              sink.writeAll(response.body()!!.source())
+              val sink = file.sink().buffer()
+              sink.writeAll(response.body!!.source())
               sink.close()
               val result = Bundle().apply {
                 putString("uri", Uri.fromFile(file).toString())
-                putInt("status", response.code())
-                putBundle("headers", translateHeaders(response.headers()))
+                putInt("status", response.code)
+                putBundle("headers", translateHeaders(response.headers))
                 if (options?.get("md5") == true) {
                   putString("md5", md5(file))
                 }
@@ -1003,7 +999,7 @@ open class FileSystemModule(
         ?.addNetworkInterceptor { chain ->
           val originalResponse = chain.proceed(chain.request())
           originalResponse.newBuilder()
-            .body(ProgressResponseBody(originalResponse.body(), progressListener))
+            .body(ProgressResponseBody(originalResponse.body, progressListener))
             .build()
         }
         ?.build()
@@ -1098,7 +1094,7 @@ open class FileSystemModule(
       val options = params[0]?.options
       return try {
         val response = call!!.execute()
-        val responseBody = response.body()
+        val responseBody = response.body
         val input = BufferedInputStream(responseBody!!.byteStream())
         val output = FileOutputStream(file, isResume == true)
         val data = ByteArray(1024)
@@ -1108,15 +1104,15 @@ open class FileSystemModule(
         }
         val result = Bundle().apply {
           putString("uri", Uri.fromFile(file).toString())
-          putInt("status", response.code())
-          putBundle("headers", translateHeaders(response.headers()))
+          putInt("status", response.code)
+          putBundle("headers", translateHeaders(response.headers))
           options?.get("md5").takeIf { it == true }?.let { putString("md5", file?.let { md5(it) }) }
         }
         response.close()
         promise?.resolve(result)
         null
       } catch (e: Exception) {
-        if (call?.isCanceled == true) {
+        if (call?.isCanceled() == true) {
           promise?.resolve(null)
           return null
         }
@@ -1139,7 +1135,7 @@ open class FileSystemModule(
     override fun contentLength(): Long = responseBody?.contentLength() ?: -1
 
     override fun source(): BufferedSource =
-      bufferedSource ?: Okio.buffer(source(responseBody!!.source()))
+      bufferedSource ?: source(responseBody!!.source()).buffer()
 
     private fun source(source: Source): Source {
       return object : ForwardingSource(source) {
@@ -1304,7 +1300,7 @@ open class FileSystemModule(
   // Copied out of React Native's `NetworkingModule.java`
   private fun translateHeaders(headers: Headers): Bundle {
     val responseHeaders = Bundle()
-    for (i in 0 until headers.size()) {
+    for (i in 0 until headers.size) {
       val headerName = headers.name(i)
       // multiple values for the same header
       if (responseHeaders[headerName] != null) {

for me, it’s at .yarn/patches/expo-file-system-npm-13.1.0-4d3bf24457

I have this in my package.json to include it (I use yarn v3.1.1):

"resolutions": {
  "expo-file-system@13.1.0": "patch:expo-file-system@npm:13.1.0#.yarn/patches/expo-file-system-npm-13.1.0-4d3bf24457"
}

The error also affects RN 0.65 (expo 44 officially only supports RN 0.64 I believe; I hope they fix this issue before the next SDK though)

sdk 45 includes compatibility with react-native 0.68.1

@aprilmintacpineda please wait for a moment. i’m working on publish new packages with the fixes.