@amazon-devices/expo-file-system 2.0.1758683737

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.
Files changed (111) hide show
  1. package/.eslintrc.js +2 -0
  2. package/CHANGELOG.md +317 -0
  3. package/CMakeLists.txt +52 -0
  4. package/LICENSE +7 -0
  5. package/README.kepler.md +260 -0
  6. package/README.md +46 -0
  7. package/THIRD_PARTY_NOTICES.txt +24 -0
  8. package/android/build.gradle +118 -0
  9. package/android/src/main/AndroidManifest.xml +17 -0
  10. package/android/src/main/java/expo/modules/filesystem/AppDirectoriesModule.kt +26 -0
  11. package/android/src/main/java/expo/modules/filesystem/CountingRequestBody.kt +52 -0
  12. package/android/src/main/java/expo/modules/filesystem/FilePermissionModule.kt +48 -0
  13. package/android/src/main/java/expo/modules/filesystem/FileSystemExceptions.kt +48 -0
  14. package/android/src/main/java/expo/modules/filesystem/FileSystemFileProvider.kt +5 -0
  15. package/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt +1110 -0
  16. package/android/src/main/java/expo/modules/filesystem/FileSystemPackage.kt +10 -0
  17. package/android/src/main/java/expo/modules/filesystem/FileSystemRecords.kt +92 -0
  18. package/android/src/main/res/xml/file_system_provider_paths.xml +5 -0
  19. package/app.plugin.js +1 -0
  20. package/build/ExponentFileSystem.d.ts +4 -0
  21. package/build/ExponentFileSystem.d.ts.map +1 -0
  22. package/build/ExponentFileSystem.js +5 -0
  23. package/build/ExponentFileSystem.js.map +1 -0
  24. package/build/ExponentFileSystem.kepler.d.ts +4 -0
  25. package/build/ExponentFileSystem.kepler.d.ts.map +1 -0
  26. package/build/ExponentFileSystem.kepler.js +112 -0
  27. package/build/ExponentFileSystem.kepler.js.map +1 -0
  28. package/build/ExponentFileSystem.web.d.ts +3 -0
  29. package/build/ExponentFileSystem.web.d.ts.map +1 -0
  30. package/build/ExponentFileSystem.web.js +3 -0
  31. package/build/ExponentFileSystem.web.js.map +1 -0
  32. package/build/ExponentFileSystemShim.d.ts +4 -0
  33. package/build/ExponentFileSystemShim.d.ts.map +1 -0
  34. package/build/ExponentFileSystemShim.js +21 -0
  35. package/build/ExponentFileSystemShim.js.map +1 -0
  36. package/build/FileSystem.d.ts +361 -0
  37. package/build/FileSystem.d.ts.map +1 -0
  38. package/build/FileSystem.js +616 -0
  39. package/build/FileSystem.js.map +1 -0
  40. package/build/FileSystem.types.d.ts +304 -0
  41. package/build/FileSystem.types.d.ts.map +1 -0
  42. package/build/FileSystem.types.js +47 -0
  43. package/build/FileSystem.types.js.map +1 -0
  44. package/build/index.d.ts +3 -0
  45. package/build/index.d.ts.map +1 -0
  46. package/build/index.js +3 -0
  47. package/build/index.js.map +1 -0
  48. package/build/types.d.ts +35 -0
  49. package/build/types.d.ts.map +1 -0
  50. package/build/types.js +2 -0
  51. package/build/types.js.map +1 -0
  52. package/expo-module.config.json +11 -0
  53. package/ios/EXFilePermissionModule.h +16 -0
  54. package/ios/EXFilePermissionModule.m +68 -0
  55. package/ios/EXFileSystem.h +25 -0
  56. package/ios/EXFileSystem.m +1084 -0
  57. package/ios/EXFileSystemAssetLibraryHandler.h +6 -0
  58. package/ios/EXFileSystemAssetLibraryHandler.m +142 -0
  59. package/ios/EXFileSystemHandler.h +17 -0
  60. package/ios/EXFileSystemLocalFileHandler.h +7 -0
  61. package/ios/EXFileSystemLocalFileHandler.m +79 -0
  62. package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.h +16 -0
  63. package/ios/EXSessionTasks/EXSessionCancelableUploadTaskDelegate.m +55 -0
  64. package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.h +13 -0
  65. package/ios/EXSessionTasks/EXSessionDownloadTaskDelegate.m +64 -0
  66. package/ios/EXSessionTasks/EXSessionHandler.h +19 -0
  67. package/ios/EXSessionTasks/EXSessionHandler.m +49 -0
  68. package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.h +18 -0
  69. package/ios/EXSessionTasks/EXSessionResumableDownloadTaskDelegate.m +66 -0
  70. package/ios/EXSessionTasks/EXSessionTaskDelegate.h +32 -0
  71. package/ios/EXSessionTasks/EXSessionTaskDelegate.m +58 -0
  72. package/ios/EXSessionTasks/EXSessionTaskDispatcher.h +19 -0
  73. package/ios/EXSessionTasks/EXSessionTaskDispatcher.m +96 -0
  74. package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.h +8 -0
  75. package/ios/EXSessionTasks/EXSessionUploadTaskDelegate.m +52 -0
  76. package/ios/EXSessionTasks/EXTaskHandlersManager.h +21 -0
  77. package/ios/EXSessionTasks/EXTaskHandlersManager.m +56 -0
  78. package/ios/Encoding.swift +88 -0
  79. package/ios/ExpoFileSystem.h +1 -0
  80. package/ios/ExpoFileSystem.podspec +32 -0
  81. package/ios/FileSystemBackgroundSessionHandler.swift +25 -0
  82. package/ios/FileSystemExceptions.swift +69 -0
  83. package/ios/FileSystemHelpers.swift +66 -0
  84. package/ios/FileSystemModule.swift +263 -0
  85. package/ios/FileSystemRecords.swift +74 -0
  86. package/ios/NSData+EXFileSystem.h +9 -0
  87. package/ios/NSData+EXFileSystem.m +19 -0
  88. package/ios/NetworkingHelpers.swift +84 -0
  89. package/ios/Tests/EXFileSystemSpec.swift +55 -0
  90. package/jest.config.js +3 -0
  91. package/kepler/ExponentFileSystem.cpp +498 -0
  92. package/kepler/ExponentFileSystem.h +60 -0
  93. package/kepler/KeplerInit.cpp +28 -0
  94. package/kepler/NetworkingRequestHandler.cpp +47 -0
  95. package/kepler/test/FileSystem.cpp +425 -0
  96. package/package.json +1 -0
  97. package/plugin/build/withFileSystem.d.ts +3 -0
  98. package/plugin/build/withFileSystem.js +12 -0
  99. package/plugin/src/withFileSystem.ts +13 -0
  100. package/plugin/tsconfig.json +9 -0
  101. package/plugin/tsconfig.tsbuildinfo +1 -0
  102. package/react-native.config.js +21 -0
  103. package/src/ExponentFileSystem.kepler.ts +167 -0
  104. package/src/ExponentFileSystem.ts +7 -0
  105. package/src/ExponentFileSystem.web.ts +2 -0
  106. package/src/ExponentFileSystemShim.ts +23 -0
  107. package/src/FileSystem.ts +756 -0
  108. package/src/FileSystem.types.ts +339 -0
  109. package/src/index.ts +2 -0
  110. package/src/types.ts +34 -0
  111. package/tsconfig.json +9 -0
@@ -0,0 +1,96 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoFileSystem/EXSessionTaskDispatcher.h>
4
+ #import <ExpoFileSystem/EXSessionResumableDownloadTaskDelegate.h>
5
+
6
+ @interface EXSessionTaskDispatcher ()
7
+
8
+ @property (nonatomic, strong) NSMutableDictionary<NSURLSessionTask *, EXSessionTaskDelegate *> *tasks;
9
+ @property (nonatomic) BOOL isActive;
10
+ @property (nonatomic, weak, nullable) id<EXSessionHandler> sessionHandler;
11
+
12
+ @end
13
+
14
+ @implementation EXSessionTaskDispatcher
15
+
16
+ - (instancetype)initWithSessionHandler:(nullable id<EXSessionHandler>)sessionHandler;
17
+ {
18
+ if (self = [super init]) {
19
+ _tasks = [NSMutableDictionary dictionary];
20
+ _isActive = true;
21
+ _sessionHandler = sessionHandler;
22
+ }
23
+ return self;
24
+ }
25
+
26
+ #pragma mark - public methods
27
+
28
+ - (void)registerTaskDelegate:(EXSessionTaskDelegate *)delegate forTask:(NSURLSessionTask *)task
29
+ {
30
+ _tasks[task] = delegate;
31
+ }
32
+
33
+ - (void)deactivate
34
+ {
35
+ _isActive = false;
36
+ }
37
+
38
+ #pragma mark - dispatcher
39
+
40
+ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
41
+ {
42
+ if (_isActive) {
43
+ EXSessionTaskDelegate *exTask = _tasks[downloadTask];
44
+ if (exTask) {
45
+ [exTask URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
46
+ [_tasks removeObjectForKey:downloadTask];
47
+ }
48
+ }
49
+ }
50
+
51
+ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
52
+ {
53
+ if (_isActive) {
54
+ EXSessionTaskDelegate *exTask = _tasks[task];
55
+ if (exTask) {
56
+ [exTask URLSession:session task:task didCompleteWithError:error];
57
+ [_tasks removeObjectForKey:task];
58
+ }
59
+ }
60
+ }
61
+
62
+ - (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask
63
+ didWriteData:(int64_t)bytesWritten
64
+ totalBytesWritten:(int64_t)totalBytesWritten
65
+ totalBytesExpectedToWrite:(int64_t)totalBytesExpectedToWrite
66
+ {
67
+ if (_isActive) {
68
+ EXSessionTaskDelegate *exTask = _tasks[downloadTask];
69
+ [exTask URLSession:session downloadTask:downloadTask didWriteData:bytesWritten totalBytesWritten:totalBytesWritten totalBytesExpectedToWrite:totalBytesExpectedToWrite];
70
+ }
71
+ }
72
+
73
+ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
74
+ {
75
+ if (_isActive) {
76
+ EXSessionTaskDelegate *exTask = _tasks[dataTask];
77
+ [exTask URLSession:session dataTask:dataTask didReceiveData:data];
78
+ }
79
+ }
80
+
81
+ - (void)URLSessionDidFinishEventsForBackgroundURLSession:(NSURLSession *)session {
82
+ [_sessionHandler invokeCompletionHandlerForSessionIdentifier:session.configuration.identifier];
83
+ }
84
+
85
+ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
86
+ didSendBodyData:(int64_t)bytesSent
87
+ totalBytesSent:(int64_t)totalBytesSent
88
+ totalBytesExpectedToSend:(int64_t)totalBytesExpectedToSend
89
+ {
90
+ if (_isActive) {
91
+ EXSessionTaskDelegate *exTask = _tasks[task];
92
+ [exTask URLSession:session task:task didSendBodyData:bytesSent totalBytesSent:totalBytesSent totalBytesExpectedToSend:totalBytesExpectedToSend];
93
+ }
94
+ }
95
+
96
+ @end
@@ -0,0 +1,8 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoFileSystem/EXSessionTaskDelegate.h>
4
+
5
+ @interface EXSessionUploadTaskDelegate : EXSessionTaskDelegate
6
+
7
+ @end
8
+
@@ -0,0 +1,52 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoFileSystem/EXSessionUploadTaskDelegate.h>
4
+
5
+ @interface EXSessionUploadTaskDelegate ()
6
+
7
+ @property (strong, nonatomic) NSMutableData *responseData;
8
+
9
+ @end
10
+
11
+ @implementation EXSessionUploadTaskDelegate
12
+
13
+ - (instancetype)initWithResolve:(EXPromiseResolveBlock)resolve reject:(EXPromiseRejectBlock)reject
14
+ {
15
+ if (self = [super initWithResolve:resolve reject:reject]) {
16
+ _responseData = [NSMutableData new];
17
+ }
18
+ return self;
19
+ }
20
+
21
+ - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
22
+ {
23
+ if (!data.length) {
24
+ return;
25
+ }
26
+ [_responseData appendData:data];
27
+ }
28
+
29
+ - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
30
+ {
31
+ if (error) {
32
+ self.reject(@"ERR_FILESYSTEM_CANNOT_UPLOAD",
33
+ [NSString stringWithFormat:@"Unable to upload the file: '%@'", error.description],
34
+ error);
35
+ return;
36
+ }
37
+
38
+ // We only set EXSessionUploadTaskDelegates as delegates of upload tasks
39
+ // so it should be safe to assume that this is what we will receive here.
40
+ NSURLSessionUploadTask *uploadTask = (NSURLSessionUploadTask *)task;
41
+ self.resolve([self parseServerResponse:uploadTask.response]);
42
+ }
43
+
44
+ - (NSDictionary *)parseServerResponse:(NSURLResponse *)response
45
+ {
46
+ NSMutableDictionary *result = [[super parseServerResponse:response] mutableCopy];
47
+ // TODO: add support for others response types (different encodings, files)
48
+ result[@"body"] = EXNullIfNil([[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]);
49
+ return result;
50
+ }
51
+
52
+ @end
@@ -0,0 +1,21 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ #import <Foundation/Foundation.h>
4
+
5
+ NS_ASSUME_NONNULL_BEGIN
6
+
7
+ @interface EXTaskHandlersManager : NSObject
8
+
9
+ - (NSURLSessionTask * _Nullable)taskForId:(NSString *)uuid;
10
+
11
+ - (NSURLSessionDownloadTask * _Nullable)downloadTaskForId:(NSString *)uuid;
12
+
13
+ - (NSURLSessionUploadTask * _Nullable)uploadTaskForId:(NSString *)uuid;
14
+
15
+ - (void)registerTask:(NSURLSessionTask *)task uuid:(NSString *)uuid;
16
+
17
+ - (void)unregisterTask:(NSString *)uuid;
18
+
19
+ @end
20
+
21
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,56 @@
1
+ // Copyright 2015-present 650 Industries. All rights reserved.
2
+
3
+ #import <ExpoFileSystem/EXTaskHandlersManager.h>
4
+
5
+ @interface EXTaskHandlersManager ()
6
+
7
+ @property (nonatomic, strong) NSMutableDictionary<NSString *, NSURLSessionTask *> *resumableDownloads;
8
+
9
+ @end
10
+
11
+ @implementation EXTaskHandlersManager
12
+
13
+ - (instancetype)init
14
+ {
15
+ if (self = [super init]) {
16
+ _resumableDownloads = [NSMutableDictionary dictionary];
17
+ }
18
+ return self;
19
+ }
20
+
21
+ - (void)registerTask:(NSURLSessionTask *)task uuid:(NSString *)uuid
22
+ {
23
+ _resumableDownloads[uuid] = task;
24
+ }
25
+
26
+ - (NSURLSessionTask * _Nullable)taskForId:(NSString *)uuid
27
+ {
28
+ return _resumableDownloads[uuid];
29
+ }
30
+
31
+ - (NSURLSessionDownloadTask * _Nullable)downloadTaskForId:(NSString *)uuid
32
+ {
33
+ NSURLSessionTask *task = [self taskForId:uuid];
34
+ if ([task isKindOfClass:[NSURLSessionDownloadTask class]]) {
35
+ return (NSURLSessionDownloadTask *)task;
36
+ }
37
+
38
+ return nil;
39
+ }
40
+
41
+ - (NSURLSessionUploadTask * _Nullable)uploadTaskForId:(NSString *)uuid
42
+ {
43
+ NSURLSessionTask *task = [self taskForId:uuid];
44
+ if ([task isKindOfClass:[NSURLSessionUploadTask class]]) {
45
+ return (NSURLSessionDownloadTask *)task;
46
+ }
47
+
48
+ return nil;
49
+ }
50
+
51
+ - (void)unregisterTask:(NSString *)uuid
52
+ {
53
+ [_resumableDownloads removeObjectForKey:uuid];
54
+ }
55
+
56
+ @end
@@ -0,0 +1,88 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ enum Encoding: String, Enumerable {
6
+ // Equivalents of String.Encoding
7
+ case ascii
8
+ case nextstep
9
+ case japaneseeuc
10
+ case utf8
11
+ case isolatin1
12
+ case symbol
13
+ case nonlossyascii
14
+ case shiftjis
15
+ case isolatin2
16
+ case unicode
17
+ case windowscp1251
18
+ case windowscp1252
19
+ case windowscp1253
20
+ case windowscp1254
21
+ case windowscp1250
22
+ case iso2022jp
23
+ case macosroman
24
+ case utf16
25
+ case utf16bigendian
26
+ case utf16littleendian
27
+ case utf32
28
+ case utf32bigendian
29
+ case utf32littleendian
30
+
31
+ // Without equivalents in String.Encoding
32
+ case base64
33
+
34
+ func toStringEncoding() -> String.Encoding? {
35
+ switch self {
36
+ case .ascii:
37
+ return .ascii
38
+ case .nextstep:
39
+ return .nextstep
40
+ case .japaneseeuc:
41
+ return .japaneseEUC
42
+ case .utf8:
43
+ return .utf8
44
+ case .isolatin1:
45
+ return .isoLatin1
46
+ case .symbol:
47
+ return .symbol
48
+ case .nonlossyascii:
49
+ return .nonLossyASCII
50
+ case .shiftjis:
51
+ return .shiftJIS
52
+ case .isolatin2:
53
+ return .isoLatin2
54
+ case .unicode:
55
+ return .unicode
56
+ case .windowscp1251:
57
+ return .windowsCP1251
58
+ case .windowscp1252:
59
+ return .windowsCP1252
60
+ case .windowscp1253:
61
+ return .windowsCP1253
62
+ case .windowscp1254:
63
+ return .windowsCP1254
64
+ case .windowscp1250:
65
+ return .windowsCP1250
66
+ case .iso2022jp:
67
+ return .iso2022JP
68
+ case .macosroman:
69
+ return .macOSRoman
70
+ case .utf16:
71
+ return .utf16
72
+ case .utf16bigendian:
73
+ return .utf16BigEndian
74
+ case .utf16littleendian:
75
+ return .utf16LittleEndian
76
+ case .utf32:
77
+ return .utf32
78
+ case .utf32bigendian:
79
+ return .utf32BigEndian
80
+ case .utf32littleendian:
81
+ return .utf32LittleEndian
82
+
83
+ // Cases that don't have their own equivalent in String.Encoding
84
+ case .base64:
85
+ return nil
86
+ }
87
+ }
88
+ }
@@ -0,0 +1 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
@@ -0,0 +1,32 @@
1
+ require 'json'
2
+
3
+ package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
4
+
5
+ Pod::Spec.new do |s|
6
+ s.name = 'ExpoFileSystem'
7
+ s.version = package['version']
8
+ s.summary = package['description']
9
+ s.description = package['description']
10
+ s.license = package['license']
11
+ s.author = package['author']
12
+ s.homepage = package['homepage']
13
+ s.platforms = { :ios => '13.0', :tvos => '13.0'}
14
+ s.swift_version = '5.4'
15
+ s.source = { :git => 'https://github.com/expo/expo.git' }
16
+ s.static_framework = true
17
+
18
+ s.dependency 'ExpoModulesCore'
19
+
20
+ # Swift/Objective-C compatibility
21
+ s.pod_target_xcconfig = {
22
+ 'DEFINES_MODULE' => 'YES'
23
+ }
24
+
25
+ s.source_files = "**/*.{h,m,swift}"
26
+
27
+ s.exclude_files = 'Tests/'
28
+ s.test_spec 'Tests' do |test_spec|
29
+ test_spec.dependency 'ExpoModulesTestCore'
30
+ test_spec.source_files = 'Tests/**/*.{m,swift}'
31
+ end
32
+ end
@@ -0,0 +1,25 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ public final class FileSystemBackgroundSessionHandler: ExpoAppDelegateSubscriber, EXSessionHandlerProtocol {
6
+ public typealias BackgroundSessionCompletionHandler = () -> Void
7
+
8
+ private var completionHandlers: [String: BackgroundSessionCompletionHandler] = [:]
9
+
10
+ public func invokeCompletionHandler(forSessionIdentifier identifier: String) {
11
+ guard let completionHandler = completionHandlers[identifier] else {
12
+ return
13
+ }
14
+ DispatchQueue.main.async {
15
+ completionHandler()
16
+ }
17
+ completionHandlers.removeValue(forKey: identifier)
18
+ }
19
+
20
+ // MARK: - ExpoAppDelegateSubscriber
21
+
22
+ public func application(_ application: UIApplication, handleEventsForBackgroundURLSession identifier: String, completionHandler: @escaping () -> Void) {
23
+ completionHandlers[identifier] = completionHandler
24
+ }
25
+ }
@@ -0,0 +1,69 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ final class FileNotExistsException: GenericException<String> {
6
+ override var reason: String {
7
+ "File '\(param)' does not exist"
8
+ }
9
+ }
10
+
11
+ final class DirectoryNotExistsException: GenericException<String> {
12
+ override var reason: String {
13
+ "Directory '\(param)' does not exist"
14
+ }
15
+ }
16
+
17
+ final class FileNotReadableException: GenericException<String> {
18
+ override var reason: String {
19
+ "File '\(param)' is not readable"
20
+ }
21
+ }
22
+
23
+ final class FileNotWritableException: GenericException<String> {
24
+ override var reason: String {
25
+ "File '\(param)' is not writable"
26
+ }
27
+ }
28
+
29
+ final class FileWriteFailedException: GenericException<String> {
30
+ override var reason: String {
31
+ "Writing to '\(param)' file has failed"
32
+ }
33
+ }
34
+
35
+ final class FileCannotDeleteException: GenericException<String> {
36
+ override var reason: String {
37
+ "File '\(param)' could not be deleted"
38
+ }
39
+ }
40
+
41
+ final class InvalidFileUrlException: GenericException<URL> {
42
+ override var reason: String {
43
+ "'\(param.absoluteString)' is not a file URL"
44
+ }
45
+ }
46
+
47
+ final class UnsupportedSchemeException: GenericException<String?> {
48
+ override var reason: String {
49
+ "Unsupported URI scheme: '\(String(describing: param))'"
50
+ }
51
+ }
52
+
53
+ final class HeaderEncodingFailedException: GenericException<String> {
54
+ override var reason: String {
55
+ "Unable to encode headers for request '\(param)' to UTF8"
56
+ }
57
+ }
58
+
59
+ final class DownloadTaskNotFoundException: GenericException<String> {
60
+ override var reason: String {
61
+ "Cannot find a download task with id: '\(param)'"
62
+ }
63
+ }
64
+
65
+ final class CannotDetermineDiskCapacity: Exception {
66
+ override var reason: String {
67
+ "Unable to determine free disk storage capacity"
68
+ }
69
+ }
@@ -0,0 +1,66 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ import ExpoModulesCore
4
+
5
+ internal func ensureFileDirectoryExists(_ fileUrl: URL) throws {
6
+ let directoryPath = fileUrl.deletingLastPathComponent()
7
+
8
+ if !FileManager.default.fileExists(atPath: directoryPath.path) {
9
+ throw DirectoryNotExistsException(directoryPath.path)
10
+ }
11
+ }
12
+
13
+ internal func readFileAsBase64(path: String, options: ReadingOptions) throws -> String {
14
+ let file = FileHandle(forReadingAtPath: path)
15
+
16
+ guard let file else {
17
+ throw FileNotExistsException(path)
18
+ }
19
+ if let position = options.position, position != 0 {
20
+ // TODO: Handle these errors?
21
+ try? file.seek(toOffset: UInt64(position))
22
+ }
23
+ if let length = options.length {
24
+ return file.readData(ofLength: length).base64EncodedString(options: .endLineWithLineFeed)
25
+ }
26
+ return file.readDataToEndOfFile().base64EncodedString(options: .endLineWithLineFeed)
27
+ }
28
+
29
+ internal func writeFileAsBase64(path: String, string: String) throws {
30
+ let data = Data(base64Encoded: string, options: .ignoreUnknownCharacters)
31
+
32
+ if !FileManager.default.createFile(atPath: path, contents: data) {
33
+ throw FileWriteFailedException(path)
34
+ }
35
+ }
36
+
37
+ internal func removeFile(path: String, idempotent: Bool = false) throws {
38
+ if FileManager.default.fileExists(atPath: path) {
39
+ do {
40
+ try FileManager.default.removeItem(atPath: path)
41
+ } catch {
42
+ throw FileCannotDeleteException(path)
43
+ .causedBy(error)
44
+ }
45
+ } else if !idempotent {
46
+ throw FileCannotDeleteException(path)
47
+ .causedBy(FileNotExistsException(path))
48
+ }
49
+ }
50
+
51
+ internal func getResourceValues(from directory: URL?, forKeys: Set<URLResourceKey>) throws -> URLResourceValues? {
52
+ do {
53
+ return try directory?.resourceValues(forKeys: forKeys)
54
+ } catch {
55
+ throw CannotDetermineDiskCapacity().causedBy(error)
56
+ }
57
+ }
58
+
59
+ internal func ensurePathPermission(_ appContext: AppContext?, path: String, flag: EXFileSystemPermissionFlags) throws {
60
+ guard let permissionsManager: EXFilePermissionModuleInterface = appContext?.legacyModule(implementing: EXFilePermissionModuleInterface.self) else {
61
+ throw Exceptions.PermissionsModuleNotFound()
62
+ }
63
+ guard permissionsManager.getPathPermissions(path).contains(flag) else {
64
+ throw flag == .read ? FileNotReadableException(path) : FileNotWritableException(path)
65
+ }
66
+ }