expo-file-system 16.0.6 → 16.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -10,6 +10,13 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 16.0.7 — 2024-02-27
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [iOS] Fix downloadAsync for local files. ([#27187](https://github.com/expo/expo/pull/27187) by [@gabrieldonadel](https://github.com/gabrieldonadel))
18
+ - On `iOS`, fix an issue with `copyAsync` where the copy fails if it is a photo library asset. ([#27208](https://github.com/expo/expo/pull/27208) by [@alanjhughes](https://github.com/alanjhughes))
19
+
13
20
  ## 16.0.6 — 2024-02-06
14
21
 
15
22
  ### 🐛 Bug fixes
@@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
3
3
  apply plugin: 'maven-publish'
4
4
 
5
5
  group = 'host.exp.exponent'
6
- version = '16.0.6'
6
+ version = '16.0.7'
7
7
 
8
8
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
9
9
  if (expoModulesCorePlugin.exists()) {
@@ -94,7 +94,7 @@ android {
94
94
  namespace "expo.modules.filesystem"
95
95
  defaultConfig {
96
96
  versionCode 30
97
- versionName "16.0.6"
97
+ versionName "16.0.7"
98
98
  }
99
99
  }
100
100
 
@@ -8,6 +8,12 @@ final class FileNotExistsException: GenericException<String> {
8
8
  }
9
9
  }
10
10
 
11
+ final class FileAlreadyExistsException: GenericException<String> {
12
+ override var reason: String {
13
+ "File '\(param)' already exists"
14
+ }
15
+ }
16
+
11
17
  final class DirectoryNotExistsException: GenericException<String> {
12
18
  override var reason: String {
13
19
  "Directory '\(param)' does not exist"
@@ -79,3 +85,15 @@ final class FailedToAccessDirectoryException: Exception {
79
85
  "Failed to access `Caches` directory"
80
86
  }
81
87
  }
88
+
89
+ final class FailedToCopyAssetException: GenericException<String> {
90
+ override var reason: String {
91
+ "Failed to copy photo library asset: \(param)"
92
+ }
93
+ }
94
+
95
+ final class FailedToFindAssetException: GenericException<String> {
96
+ override var reason: String {
97
+ "Failed to find photo library asset: \(param)"
98
+ }
99
+ }
@@ -1,6 +1,9 @@
1
1
  // Copyright 2023-present 650 Industries. All rights reserved.
2
2
 
3
3
  import ExpoModulesCore
4
+ import Photos
5
+
6
+ private let assetIdentifier = "ph://"
4
7
 
5
8
  internal func ensureFileDirectoryExists(_ fileUrl: URL) throws {
6
9
  let directoryPath = fileUrl.deletingLastPathComponent()
@@ -64,3 +67,42 @@ internal func ensurePathPermission(_ appContext: AppContext?, path: String, flag
64
67
  throw flag == .read ? FileNotReadableException(path) : FileNotWritableException(path)
65
68
  }
66
69
  }
70
+
71
+ internal func isPHAsset(path: String) -> Bool {
72
+ return path.contains(assetIdentifier)
73
+ }
74
+
75
+ internal func copyPHAsset(fromUrl: URL, toUrl: URL, with resourceManager: PHAssetResourceManager, promise: Promise) {
76
+ if isPhotoLibraryStatusAuthorized() {
77
+ if FileManager.default.fileExists(atPath: toUrl.path) {
78
+ promise.reject(FileAlreadyExistsException(toUrl.path))
79
+ return
80
+ }
81
+
82
+ let identifier = fromUrl.absoluteString.replacingOccurrences(of: assetIdentifier, with: "")
83
+
84
+ guard let asset = PHAsset.fetchAssets(withLocalIdentifiers: [identifier], options: nil).firstObject else {
85
+ promise.reject(FailedToFindAssetException(fromUrl.absoluteString))
86
+ return
87
+ }
88
+
89
+ let firstResource = PHAssetResource.assetResources(for: asset).first
90
+ if let firstResource {
91
+ resourceManager.writeData(for: firstResource, toFile: toUrl, options: nil) { error in
92
+ if let error {
93
+ promise.reject(FailedToCopyAssetException(fromUrl.absoluteString))
94
+ }
95
+ }
96
+ } else {
97
+ promise.reject(FailedToCopyAssetException(fromUrl.absoluteString))
98
+ }
99
+ }
100
+ }
101
+
102
+ internal func isPhotoLibraryStatusAuthorized() -> Bool {
103
+ if #available(iOS 14, tvOS 14, *) {
104
+ let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
105
+ return status == .authorized || status == .limited
106
+ }
107
+ return PHPhotoLibrary.authorizationStatus() == .authorized
108
+ }
@@ -1,6 +1,7 @@
1
1
  // Copyright 2023-present 650 Industries. All rights reserved.
2
2
 
3
3
  import ExpoModulesCore
4
+ import Photos
4
5
 
5
6
  private let EVENT_DOWNLOAD_PROGRESS = "expo-file-system.downloadProgress"
6
7
  private let EVENT_UPLOAD_PROGRESS = "expo-file-system.uploadProgress"
@@ -8,6 +9,7 @@ private let EVENT_UPLOAD_PROGRESS = "expo-file-system.uploadProgress"
8
9
  public final class FileSystemModule: Module {
9
10
  private lazy var sessionTaskDispatcher = EXSessionTaskDispatcher(sessionHandler: ExpoAppDelegate.getSubscriberOfType(FileSystemBackgroundSessionHandler.self))
10
11
  private lazy var taskHandlersManager = EXTaskHandlersManager()
12
+ private lazy var resourceManager = PHAssetResourceManager()
11
13
 
12
14
  private lazy var backgroundSession = createUrlSession(type: .background, delegate: sessionTaskDispatcher)
13
15
  private lazy var foregroundSession = createUrlSession(type: .foreground, delegate: sessionTaskDispatcher)
@@ -100,6 +102,11 @@ public final class FileSystemModule: Module {
100
102
  AsyncFunction("copyAsync") { (options: RelocatingOptions, promise: Promise) in
101
103
  let (fromUrl, toUrl) = try options.asTuple()
102
104
 
105
+ if isPHAsset(path: fromUrl.absoluteString) {
106
+ copyPHAsset(fromUrl: fromUrl, toUrl: toUrl, with: resourceManager, promise: promise)
107
+ return
108
+ }
109
+
103
110
  try ensurePathPermission(appContext, path: fromUrl.path, flag: .read)
104
111
  try ensurePathPermission(appContext, path: toUrl.path, flag: .write)
105
112
 
@@ -134,6 +141,11 @@ public final class FileSystemModule: Module {
134
141
  try ensureFileDirectoryExists(localUrl)
135
142
  try ensurePathPermission(appContext, path: localUrl.path, flag: .write)
136
143
 
144
+ if sourceUrl.isFileURL {
145
+ try ensurePathPermission(appContext, path: sourceUrl.path, flag: .read)
146
+ EXFileSystemLocalFileHandler.copy(from: sourceUrl, to: localUrl, resolver: promise.resolver, rejecter: promise.legacyRejecter)
147
+ return
148
+ }
137
149
  let session = options.sessionType == .background ? backgroundSession : foregroundSession
138
150
  let request = createUrlRequest(url: sourceUrl, headers: options.headers)
139
151
  let downloadTask = session.downloadTask(with: request)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-file-system",
3
- "version": "16.0.6",
3
+ "version": "16.0.7",
4
4
  "description": "Provides access to the local file system on the device.",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
@@ -41,5 +41,5 @@
41
41
  "peerDependencies": {
42
42
  "expo": "*"
43
43
  },
44
- "gitHead": "4f3dcf3e23eae997f884117fdd34ad734efad9fd"
44
+ "gitHead": "e94e2aac39cbb7f8788869cd6798bbdeadc58f42"
45
45
  }