react-native-cloud-storage 1.4.2 → 1.5.0
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/LICENSE +1 -1
- package/README.md +6 -5
- package/ios/CloudStorage.swift +52 -262
- package/ios/CloudStorage.xcodeproj/project.pbxproj +28 -0
- package/ios/CloudStorageEventEmitter.swift +4 -4
- package/ios/Utils/CloudKitUtils.swift +112 -0
- package/ios/Utils/CloudStorageError.swift +78 -0
- package/ios/Utils/FileUtils.swift +132 -0
- package/ios/Utils/Promise.swift +58 -0
- package/ios/Utils/Types.swift +36 -0
- package/lib/commonjs/RNCloudStorage.js +361 -66
- package/lib/commonjs/RNCloudStorage.js.map +1 -1
- package/lib/commonjs/expo-plugin/types/index.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js +2 -3
- package/lib/commonjs/expo-plugin/withRNCloudStorage.js.map +1 -1
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js +4 -7
- package/lib/commonjs/expo-plugin/withRNCloudStorageIos.js.map +1 -1
- package/lib/commonjs/google-drive/client.js +16 -20
- package/lib/commonjs/google-drive/client.js.map +1 -1
- package/lib/commonjs/google-drive/index.js +42 -64
- package/lib/commonjs/google-drive/index.js.map +1 -1
- package/lib/commonjs/google-drive/types.js +1 -2
- package/lib/commonjs/google-drive/types.js.map +1 -1
- package/lib/commonjs/hooks/useCloudFile.js +14 -17
- package/lib/commonjs/hooks/useCloudFile.js.map +1 -1
- package/lib/commonjs/hooks/useIsCloudAvailable.js +11 -21
- package/lib/commonjs/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/commonjs/index.js +1 -7
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/types/main.js +8 -3
- package/lib/commonjs/types/main.js.map +1 -1
- package/lib/commonjs/types/native.js +3 -3
- package/lib/commonjs/types/native.js.map +1 -1
- package/lib/commonjs/utils/CloudStorageError.js +1 -2
- package/lib/commonjs/utils/CloudStorageError.js.map +1 -1
- package/lib/commonjs/utils/helpers.js +8 -15
- package/lib/commonjs/utils/helpers.js.map +1 -1
- package/lib/module/RNCloudStorage.js +362 -65
- package/lib/module/RNCloudStorage.js.map +1 -1
- package/lib/module/expo-plugin/types/index.js +1 -1
- package/lib/module/expo-plugin/types/index.js.map +1 -1
- package/lib/module/expo-plugin/withRNCloudStorage.js +2 -0
- package/lib/module/expo-plugin/withRNCloudStorage.js.map +1 -1
- package/lib/module/expo-plugin/withRNCloudStorageIos.js +5 -5
- package/lib/module/expo-plugin/withRNCloudStorageIos.js.map +1 -1
- package/lib/module/google-drive/client.js +18 -20
- package/lib/module/google-drive/client.js.map +1 -1
- package/lib/module/google-drive/index.js +41 -62
- package/lib/module/google-drive/index.js.map +1 -1
- package/lib/module/google-drive/types.js +2 -0
- package/lib/module/google-drive/types.js.map +1 -1
- package/lib/module/hooks/useCloudFile.js +15 -16
- package/lib/module/hooks/useCloudFile.js.map +1 -1
- package/lib/module/hooks/useIsCloudAvailable.js +13 -21
- package/lib/module/hooks/useIsCloudAvailable.js.map +1 -1
- package/lib/module/index.js +2 -5
- package/lib/module/index.js.map +1 -1
- package/lib/module/package.json +1 -0
- package/lib/module/types/main.js +9 -0
- package/lib/module/types/main.js.map +1 -1
- package/lib/module/types/native.js +4 -1
- package/lib/module/types/native.js.map +1 -1
- package/lib/module/utils/CloudStorageError.js +2 -0
- package/lib/module/utils/CloudStorageError.js.map +1 -1
- package/lib/module/utils/helpers.js +8 -13
- package/lib/module/utils/helpers.js.map +1 -1
- package/lib/typescript/RNCloudStorage.d.ts +159 -39
- package/lib/typescript/RNCloudStorage.d.ts.map +1 -1
- package/lib/typescript/google-drive/client.d.ts +3 -3
- package/lib/typescript/google-drive/client.d.ts.map +1 -1
- package/lib/typescript/google-drive/index.d.ts +6 -18
- package/lib/typescript/google-drive/index.d.ts.map +1 -1
- package/lib/typescript/hooks/useCloudFile.d.ts +4 -7
- package/lib/typescript/hooks/useCloudFile.d.ts.map +1 -1
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts +3 -2
- package/lib/typescript/hooks/useIsCloudAvailable.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +0 -4
- package/lib/typescript/index.d.ts.map +1 -1
- package/lib/typescript/types/main.d.ts +33 -0
- package/lib/typescript/types/main.d.ts.map +1 -1
- package/lib/typescript/types/native.d.ts +2 -1
- package/lib/typescript/types/native.d.ts.map +1 -1
- package/lib/typescript/utils/helpers.d.ts +2 -9
- package/lib/typescript/utils/helpers.d.ts.map +1 -1
- package/package.json +9 -11
- package/src/RNCloudStorage.ts +387 -68
- package/src/google-drive/client.ts +8 -7
- package/src/google-drive/index.ts +38 -63
- package/src/hooks/useCloudFile.ts +13 -16
- package/src/hooks/useIsCloudAvailable.ts +12 -25
- package/src/index.ts +0 -5
- package/src/types/main.ts +38 -0
- package/src/types/native.ts +2 -1
- package/src/utils/helpers.ts +8 -15
- package/lib/commonjs/createRNCloudStorage.js +0 -48
- package/lib/commonjs/createRNCloudStorage.js.map +0 -1
- package/lib/module/createRNCloudStorage.js +0 -41
- package/lib/module/createRNCloudStorage.js.map +0 -1
- package/lib/typescript/createRNCloudStorage.d.ts +0 -3
- package/lib/typescript/createRNCloudStorage.d.ts.map +0 -1
- package/src/createRNCloudStorage.ts +0 -53
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2023 Kuatsu Digital Agency
|
|
3
|
+
Copyright (c) 2023-2024 Kuatsu Digital Agency
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
|
6
6
|
in the Software without restriction, including without limitation the rights
|
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
  
|
|
4
4
|
|
|
5
|
-
This library provides a unified and streamlined API for accessing cloud storage services on iOS, Android and Web. It supports iCloud on iOS and Google Drive
|
|
5
|
+
This library provides a unified and streamlined API for accessing cloud storage services on iOS, Android and Web. It supports iCloud (on iOS only) and Google Drive (all platforms).
|
|
6
6
|
|
|
7
7
|
- 💾 Read and write files to the cloud
|
|
8
8
|
- 🧪 Fully compatible with Expo
|
|
9
9
|
- 📱 iOS, Android & Web support
|
|
10
10
|
- 🏎️ Lightning fast iCloud performance using native iOS APIs
|
|
11
|
-
- 🌐 Google Drive REST API implementation for
|
|
11
|
+
- 🌐 Google Drive REST API implementation for all platforms
|
|
12
12
|
- 🧬 Easy to use React Hooks API, or use the imperative `fs`-style API
|
|
13
13
|
- 👌 Zero dependencies, small bundle size
|
|
14
14
|
|
|
@@ -36,14 +36,15 @@ Afterwards, [add the provided config plugin](https://react-native-cloud-storage.
|
|
|
36
36
|
```jsx
|
|
37
37
|
import React from 'react';
|
|
38
38
|
import { Platform, View, Text, Button } from 'react-native';
|
|
39
|
-
import { CloudStorage, useIsCloudAvailable } from 'react-native-cloud-storage';
|
|
39
|
+
import { CloudStorage, CloudStorageProvider, useIsCloudAvailable } from 'react-native-cloud-storage';
|
|
40
40
|
|
|
41
41
|
const App = () => {
|
|
42
42
|
const cloudAvailable = useIsCloudAvailable();
|
|
43
43
|
|
|
44
44
|
React.useEffect(() => {
|
|
45
|
-
if (
|
|
46
|
-
|
|
45
|
+
if (CloudStorage.getDefaultProvider() === CloudStorageProvider.GoogleDrive) {
|
|
46
|
+
// get access token via @react-native-google-signin/google-signin or similar
|
|
47
|
+
CloudStorage.setProviderOptions({ accessToken: 'some-access-token' });
|
|
47
48
|
}
|
|
48
49
|
}, []);
|
|
49
50
|
|
package/ios/CloudStorage.swift
CHANGED
|
@@ -3,311 +3,101 @@ import Foundation
|
|
|
3
3
|
@objc(CloudStorage)
|
|
4
4
|
class CloudStorage: NSObject {
|
|
5
5
|
@objc(fileExists:withScope:withResolver:withRejecter:)
|
|
6
|
-
func fileExists(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
} catch let error as NSError {
|
|
11
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
12
|
-
return
|
|
6
|
+
func fileExists(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
7
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
8
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
9
|
+
return try FileUtils.checkFileExists(fileUrl: fileUrl)
|
|
13
10
|
}
|
|
14
|
-
|
|
15
|
-
let fileManager = FileManager.default
|
|
16
|
-
let fileExists = fileManager.fileExists(atPath: fileUrl!.path)
|
|
17
|
-
resolve(fileExists)
|
|
18
11
|
}
|
|
19
12
|
|
|
20
13
|
@objc(appendToFile:withData:withScope:withResolver:withRejecter:)
|
|
21
|
-
func appendToFile(path: String, data: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
do {
|
|
25
|
-
fileUrl = try getFileURL(path, scope)
|
|
26
|
-
} catch let error as NSError {
|
|
27
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
28
|
-
return
|
|
29
|
-
}
|
|
14
|
+
func appendToFile(path: String, data: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
15
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
16
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
30
17
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
try data.write(to: fileUrl!, atomically: true, encoding: .utf8)
|
|
35
|
-
resolve(true)
|
|
36
|
-
} catch {
|
|
37
|
-
reject("ERR_WRITE_ERROR", "Error writing file \(path)", error)
|
|
38
|
-
}
|
|
39
|
-
} else {
|
|
40
|
-
do {
|
|
41
|
-
let fileHandle = try FileHandle(forWritingTo: fileUrl!)
|
|
42
|
-
fileHandle.seekToEndOfFile()
|
|
43
|
-
fileHandle.write(data.data(using: .utf8)!)
|
|
44
|
-
fileHandle.closeFile()
|
|
45
|
-
resolve(true)
|
|
46
|
-
} catch {
|
|
47
|
-
reject("ERR_WRITE_ERROR", "Error writing file \(path)", error)
|
|
18
|
+
var existingData = ""
|
|
19
|
+
if try FileUtils.checkFileExists(fileUrl: fileUrl) {
|
|
20
|
+
existingData = try FileUtils.readFile(fileUrl: fileUrl)
|
|
48
21
|
}
|
|
22
|
+
|
|
23
|
+
let newData = existingData + data
|
|
24
|
+
return try FileUtils.writeFile(fileUrl: fileUrl, content: newData)
|
|
49
25
|
}
|
|
50
26
|
}
|
|
51
27
|
|
|
52
28
|
@objc(createFile:withData:withScope:withOverwrite:withResolver:withRejecter:)
|
|
53
|
-
func createFile(path: String, data: String, scope: String, overwrite: Bool, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
29
|
+
func createFile(path: String, data: String, scope: String, overwrite: Bool, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
30
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
31
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
32
|
+
|
|
33
|
+
if try (FileUtils.checkFileExists(fileUrl: fileUrl) && !overwrite) {
|
|
34
|
+
throw CloudStorageError.fileAlreadyExists(path: path)
|
|
35
|
+
}
|
|
61
36
|
|
|
62
|
-
|
|
63
|
-
try data.write(to: fileUrl!, atomically: true, encoding: .utf8)
|
|
64
|
-
resolve(true)
|
|
65
|
-
} catch {
|
|
66
|
-
reject("ERR_WRITE_ERROR", "Error writing file \(path)", error)
|
|
37
|
+
return try FileUtils.writeFile(fileUrl: fileUrl, content: data)
|
|
67
38
|
}
|
|
68
39
|
}
|
|
69
40
|
|
|
70
41
|
@objc(createDirectory:withScope:withResolver:withRejecter:)
|
|
71
|
-
func createDirectory(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
} catch let error as NSError {
|
|
76
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
77
|
-
return
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
let fileManager = FileManager.default
|
|
81
|
-
do {
|
|
82
|
-
try fileManager.createDirectory(at: directoryUrl!, withIntermediateDirectories: true, attributes: nil)
|
|
83
|
-
resolve(true)
|
|
84
|
-
} catch {
|
|
85
|
-
reject("ERR_WRITE_ERROR", "Error creating directory \(path)", error)
|
|
42
|
+
func createDirectory(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
43
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
44
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
45
|
+
return try FileUtils.createDirectory(directoryUrl: fileUrl)
|
|
86
46
|
}
|
|
87
47
|
}
|
|
88
48
|
|
|
89
49
|
@objc(listFiles:withScope:withResolver:withRejecter:)
|
|
90
|
-
func listFiles(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
} catch let error as NSError {
|
|
95
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let fileManager = FileManager.default
|
|
100
|
-
do {
|
|
101
|
-
let files = try fileManager.contentsOfDirectory(atPath: directoryUrl!.path)
|
|
102
|
-
resolve(files)
|
|
103
|
-
} catch {
|
|
104
|
-
reject("ERR_READ_ERROR", "Error reading directory \(path)", error)
|
|
50
|
+
func listFiles(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
51
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
52
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
53
|
+
return try FileUtils.listFiles(directoryUrl: fileUrl)
|
|
105
54
|
}
|
|
106
55
|
}
|
|
107
56
|
|
|
108
57
|
@objc(readFile:withScope:withResolver:withRejecter:)
|
|
109
|
-
func readFile(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} catch let error as NSError {
|
|
114
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
115
|
-
return
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
do {
|
|
119
|
-
let fileContents = try String(contentsOf: fileUrl!, encoding: .utf8)
|
|
120
|
-
resolve(fileContents)
|
|
121
|
-
} catch {
|
|
122
|
-
reject("ERR_READ_ERROR", "Error reading file \(path)", error)
|
|
58
|
+
func readFile(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
59
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
60
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
61
|
+
return try FileUtils.readFile(fileUrl: fileUrl)
|
|
123
62
|
}
|
|
124
63
|
}
|
|
125
64
|
|
|
126
65
|
@objc(downloadFile:withScope:withResolver:withRejecter:)
|
|
127
|
-
func downloadFile(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
let error = NSError(domain: "", code: 200, userInfo: [NSLocalizedDescriptionKey : "Error reading directory \(scope)"])
|
|
132
|
-
reject("ERR_READ_ERROR", "Error reading directory \(scope)", error)
|
|
133
|
-
return
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// remove leading slashes
|
|
137
|
-
let path = path.replacingOccurrences(of: "^/+", with: "", options: .regularExpression)
|
|
138
|
-
|
|
139
|
-
// append path to scope directory and return URL
|
|
140
|
-
let filePath = directory.appendingPathComponent(path)
|
|
141
|
-
|
|
142
|
-
let isDownloadable = fileManager.isUbiquitousItem(at: filePath)
|
|
143
|
-
|
|
144
|
-
if (!isDownloadable) {
|
|
145
|
-
reject("ERR_FILE_NOT_DOWNLOADABLE", "File or directory \(path) is not an iCloud file", NSError(domain: "", code: 202, userInfo: [NSLocalizedDescriptionKey : "File or directory \(path) is not an iCloud file"]))
|
|
146
|
-
return
|
|
147
|
-
}
|
|
148
|
-
do {
|
|
149
|
-
// trigger download of file
|
|
150
|
-
try fileManager.startDownloadingUbiquitousItem(at: filePath)
|
|
151
|
-
} catch {
|
|
152
|
-
let error = NSError(domain: "", code: 202, userInfo: [NSLocalizedDescriptionKey : "File or directory \(path) not downloadable"])
|
|
153
|
-
reject("ERR_FILE_NOT_DOWNLOADABLE", "File or directory \(path) not downloadable", error)
|
|
154
|
-
return
|
|
66
|
+
func downloadFile(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
67
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
68
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
69
|
+
return try CloudKitUtils.downloadFile(fileUrl: fileUrl)
|
|
155
70
|
}
|
|
156
|
-
resolve(true)
|
|
157
71
|
}
|
|
158
72
|
|
|
159
|
-
|
|
160
73
|
@objc(deleteFile:withScope:withResolver:withRejecter:)
|
|
161
|
-
func deleteFile(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
} catch let error as NSError {
|
|
166
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
167
|
-
return
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (fileUrl!.hasDirectoryPath) {
|
|
171
|
-
reject("ERR_PATH_IS_DIRECTORY", "Path \(path) is a directory", nil)
|
|
172
|
-
return
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
let fileManager = FileManager.default
|
|
176
|
-
do {
|
|
177
|
-
try fileManager.removeItem(at: fileUrl!)
|
|
178
|
-
resolve(true)
|
|
179
|
-
} catch {
|
|
180
|
-
reject("ERR_DELETE_ERROR", "Error deleting file \(path)", error)
|
|
74
|
+
func deleteFile(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
75
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
76
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
77
|
+
return try FileUtils.deleteFileOrDirectory(fileUrl: fileUrl)
|
|
181
78
|
}
|
|
182
79
|
}
|
|
183
80
|
|
|
184
81
|
@objc(deleteDirectory:withRecursive:withScope:withResolver:withRejecter:)
|
|
185
|
-
func deleteDirectory(path: String, recursive: Bool, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
} catch let error as NSError {
|
|
190
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
191
|
-
return
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (!fileUrl!.hasDirectoryPath) {
|
|
195
|
-
reject("ERR_PATH_IS_FILE", "Path \(path) is a file", nil)
|
|
196
|
-
return
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
if (!recursive) {
|
|
200
|
-
// check if directory is empty
|
|
201
|
-
let fileManager = FileManager.default
|
|
202
|
-
do {
|
|
203
|
-
print(fileUrl!.path)
|
|
204
|
-
let files = try fileManager.contentsOfDirectory(atPath: fileUrl!.path)
|
|
205
|
-
print(files)
|
|
206
|
-
if (files.count > 0) {
|
|
207
|
-
reject("ERR_DIRECTORY_NOT_EMPTY", "Directory \(path) is not empty", nil)
|
|
208
|
-
return
|
|
209
|
-
}
|
|
210
|
-
} catch {
|
|
211
|
-
reject("ERR_UNKNOWN", "Error reading directory \(path)", error)
|
|
212
|
-
return
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
let fileManager = FileManager.default
|
|
217
|
-
do {
|
|
218
|
-
try fileManager.removeItem(at: fileUrl!)
|
|
219
|
-
resolve(true)
|
|
220
|
-
} catch {
|
|
221
|
-
reject("ERR_DELETE_ERROR", "Error deleting directory \(path)", error)
|
|
82
|
+
func deleteDirectory(path: String, recursive _: Bool, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
83
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
84
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
85
|
+
return try FileUtils.deleteFileOrDirectory(fileUrl: fileUrl)
|
|
222
86
|
}
|
|
223
87
|
}
|
|
224
88
|
|
|
225
89
|
@objc(statFile:withScope:withResolver:withRejecter:)
|
|
226
|
-
func statFile(path: String, scope: String, resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
} catch let error as NSError {
|
|
231
|
-
reject(error.domain, error.userInfo["message"] as? String, error)
|
|
232
|
-
return
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
let fileManager = FileManager.default
|
|
236
|
-
do {
|
|
237
|
-
let attributes = try fileManager.attributesOfItem(atPath: fileUrl!.path)
|
|
238
|
-
let size = attributes[FileAttributeKey.size] as! UInt64
|
|
239
|
-
let birthtime = attributes[FileAttributeKey.creationDate] as! Date
|
|
240
|
-
let mtime = attributes[FileAttributeKey.modificationDate] as! Date
|
|
241
|
-
let isDirectory = attributes[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeDirectory
|
|
242
|
-
let isFile = attributes[FileAttributeKey.type] as! FileAttributeType == FileAttributeType.typeRegular
|
|
243
|
-
let result = [
|
|
244
|
-
"size": size,
|
|
245
|
-
"birthtimeMs": birthtime.timeIntervalSince1970 * 1000,
|
|
246
|
-
"mtimeMs": mtime.timeIntervalSince1970 * 1000,
|
|
247
|
-
"isDirectory": isDirectory,
|
|
248
|
-
"isFile": isFile
|
|
249
|
-
] as [String : Any]
|
|
250
|
-
resolve(result)
|
|
251
|
-
} catch {
|
|
252
|
-
reject("ERR_STAT_ERROR", "Error getting stats for file \(path)", error)
|
|
90
|
+
func statFile(path: String, scope: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
91
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
92
|
+
let fileUrl = try CloudKitUtils.getFileURL(path: path, scope: scope)
|
|
93
|
+
return try FileUtils.statFile(fileUrl: fileUrl).toDictionary()
|
|
253
94
|
}
|
|
254
95
|
}
|
|
255
96
|
|
|
256
97
|
@objc(isCloudAvailable:withRejecter:)
|
|
257
|
-
func isCloudAvailable(resolve:RCTPromiseResolveBlock,reject:RCTPromiseRejectBlock)
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
/**
|
|
263
|
-
Returns the iCloud directory URL for the given scope.
|
|
264
|
-
|
|
265
|
-
- Parameter scope: The scope of the directory. Can be either "documents" or "app_data".
|
|
266
|
-
- Returns: The URL of the iCloud directory.
|
|
267
|
-
*/
|
|
268
|
-
private func getDirectory(_ scope: String) -> URL? {
|
|
269
|
-
let fileManager = FileManager.default
|
|
270
|
-
let isDocumentDirectory = scope.caseInsensitiveCompare("documents") == .orderedSame
|
|
271
|
-
let ubiquityURL = fileManager.url(forUbiquityContainerIdentifier: nil)
|
|
272
|
-
print(ubiquityURL)
|
|
273
|
-
if (isDocumentDirectory) {
|
|
274
|
-
return fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
|
|
275
|
-
} else {
|
|
276
|
-
return ubiquityURL
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
Parses a given path and directory scope to a full file URL. Does not check if the file exists.
|
|
282
|
-
|
|
283
|
-
- Parameter path: The path of the file.
|
|
284
|
-
- Parameter scope: The scope of the directory. Can be either "documents" or "app_data".
|
|
285
|
-
- Parameter shouldExist: Whether the file should exist. If true, throws an error if the file does not exist. If false, throws an error if the file exists. If nil, does not check if the file exists.
|
|
286
|
-
- Returns: The full URL of the file.
|
|
287
|
-
- Throws: An NSError if the scope directory couldn't be found or the file should exist but doesn't or vice versa.
|
|
288
|
-
*/
|
|
289
|
-
private func getFileURL(_ path: String, _ scope: String, _ shouldExist: Bool? = nil) throws -> URL? {
|
|
290
|
-
let fileManager = FileManager.default
|
|
291
|
-
|
|
292
|
-
guard let directory = getDirectory(scope) else {
|
|
293
|
-
throw NSError(domain: "ERR_DIRECTORY_NOT_FOUND", code: 0, userInfo: ["message": "Directory for scope \(scope) not found"])
|
|
98
|
+
func isCloudAvailable(resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
|
|
99
|
+
withPromise(resolve: resolve, reject: reject) {
|
|
100
|
+
CloudKitUtils.isCloudKitAvailable()
|
|
294
101
|
}
|
|
295
|
-
|
|
296
|
-
// remove leading slashes
|
|
297
|
-
let path = path.replacingOccurrences(of: "^/+", with: "", options: .regularExpression)
|
|
298
|
-
|
|
299
|
-
// append path to scope directory and return URL
|
|
300
|
-
let filePath = directory.appendingPathComponent(path)
|
|
301
|
-
|
|
302
|
-
if (shouldExist != nil) {
|
|
303
|
-
let fileExists = fileManager.fileExists(atPath: filePath.path)
|
|
304
|
-
if (shouldExist! && !fileExists) {
|
|
305
|
-
throw NSError(domain: "ERR_FILE_NOT_FOUND", code: 0, userInfo: ["message": "File or directory \(path) not found"])
|
|
306
|
-
} else if (!shouldExist! && fileExists) {
|
|
307
|
-
throw NSError(domain: "ERR_FILE_EXISTS", code: 0, userInfo: ["message": "File or directory \(path) already exists"])
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return filePath
|
|
312
102
|
}
|
|
313
103
|
}
|
|
@@ -8,6 +8,11 @@
|
|
|
8
8
|
|
|
9
9
|
/* Begin PBXBuildFile section */
|
|
10
10
|
50ADE3732B56EE1300DB5583 /* CloudStorageEventEmitter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5009AB9B2B56ECD30058E83A /* CloudStorageEventEmitter.swift */; };
|
|
11
|
+
50BC353B2CA6ED940085E8B9 /* FileUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BC353A2CA6ED940085E8B9 /* FileUtils.swift */; };
|
|
12
|
+
50BC353F2CA6EF760085E8B9 /* Types.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BC353E2CA6EF760085E8B9 /* Types.swift */; };
|
|
13
|
+
50BC35412CA6F0E80085E8B9 /* CloudKitUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BC35402CA6F0E80085E8B9 /* CloudKitUtils.swift */; };
|
|
14
|
+
50BC35452CA6FA6D0085E8B9 /* CloudStorageError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BC35442CA6FA6D0085E8B9 /* CloudStorageError.swift */; };
|
|
15
|
+
50BC35472CA6FF3F0085E8B9 /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = 50BC35462CA6FF3F0085E8B9 /* Promise.swift */; };
|
|
11
16
|
F4FF95D7245B92E800C19C63 /* CloudStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* CloudStorage.swift */; };
|
|
12
17
|
/* End PBXBuildFile section */
|
|
13
18
|
|
|
@@ -27,6 +32,11 @@
|
|
|
27
32
|
134814201AA4EA6300B7C361 /* libCloudStorage.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libCloudStorage.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
28
33
|
5009AB9B2B56ECD30058E83A /* CloudStorageEventEmitter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudStorageEventEmitter.swift; sourceTree = "<group>"; };
|
|
29
34
|
50ADE3712B56EDFB00DB5583 /* CloudStorageEventEmitter.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CloudStorageEventEmitter.m; sourceTree = "<group>"; };
|
|
35
|
+
50BC353A2CA6ED940085E8B9 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
|
|
36
|
+
50BC353E2CA6EF760085E8B9 /* Types.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Types.swift; sourceTree = "<group>"; };
|
|
37
|
+
50BC35402CA6F0E80085E8B9 /* CloudKitUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudKitUtils.swift; sourceTree = "<group>"; };
|
|
38
|
+
50BC35442CA6FA6D0085E8B9 /* CloudStorageError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudStorageError.swift; sourceTree = "<group>"; };
|
|
39
|
+
50BC35462CA6FF3F0085E8B9 /* Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = "<group>"; };
|
|
30
40
|
B3E7B5891CC2AC0600A0062D /* CloudStorage.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CloudStorage.m; sourceTree = "<group>"; };
|
|
31
41
|
F4FF95D5245B92E700C19C63 /* CloudStorage-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "CloudStorage-Bridging-Header.h"; sourceTree = "<group>"; };
|
|
32
42
|
F4FF95D6245B92E800C19C63 /* CloudStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CloudStorage.swift; sourceTree = "<group>"; };
|
|
@@ -51,9 +61,22 @@
|
|
|
51
61
|
name = Products;
|
|
52
62
|
sourceTree = "<group>";
|
|
53
63
|
};
|
|
64
|
+
50BC35482CA704A60085E8B9 /* Utils */ = {
|
|
65
|
+
isa = PBXGroup;
|
|
66
|
+
children = (
|
|
67
|
+
50BC35402CA6F0E80085E8B9 /* CloudKitUtils.swift */,
|
|
68
|
+
50BC35442CA6FA6D0085E8B9 /* CloudStorageError.swift */,
|
|
69
|
+
50BC353A2CA6ED940085E8B9 /* FileUtils.swift */,
|
|
70
|
+
50BC35462CA6FF3F0085E8B9 /* Promise.swift */,
|
|
71
|
+
50BC353E2CA6EF760085E8B9 /* Types.swift */,
|
|
72
|
+
);
|
|
73
|
+
path = Utils;
|
|
74
|
+
sourceTree = "<group>";
|
|
75
|
+
};
|
|
54
76
|
58B511D21A9E6C8500147676 = {
|
|
55
77
|
isa = PBXGroup;
|
|
56
78
|
children = (
|
|
79
|
+
50BC35482CA704A60085E8B9 /* Utils */,
|
|
57
80
|
50ADE3712B56EDFB00DB5583 /* CloudStorageEventEmitter.m */,
|
|
58
81
|
5009AB9B2B56ECD30058E83A /* CloudStorageEventEmitter.swift */,
|
|
59
82
|
F4FF95D6245B92E800C19C63 /* CloudStorage.swift */,
|
|
@@ -120,8 +143,13 @@
|
|
|
120
143
|
isa = PBXSourcesBuildPhase;
|
|
121
144
|
buildActionMask = 2147483647;
|
|
122
145
|
files = (
|
|
146
|
+
50BC353B2CA6ED940085E8B9 /* FileUtils.swift in Sources */,
|
|
147
|
+
50BC35412CA6F0E80085E8B9 /* CloudKitUtils.swift in Sources */,
|
|
148
|
+
50BC35472CA6FF3F0085E8B9 /* Promise.swift in Sources */,
|
|
149
|
+
50BC35452CA6FA6D0085E8B9 /* CloudStorageError.swift in Sources */,
|
|
123
150
|
F4FF95D7245B92E800C19C63 /* CloudStorage.swift in Sources */,
|
|
124
151
|
50ADE3732B56EE1300DB5583 /* CloudStorageEventEmitter.swift in Sources */,
|
|
152
|
+
50BC353F2CA6EF760085E8B9 /* Types.swift in Sources */,
|
|
125
153
|
);
|
|
126
154
|
runOnlyForDeploymentPostprocessing = 0;
|
|
127
155
|
};
|
|
@@ -10,7 +10,7 @@ class CloudStorageEventEmitter: RCTEventEmitter {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
override func supportedEvents() -> [String]! {
|
|
13
|
-
|
|
13
|
+
["RNCloudStorage.cloud.availability-changed"]
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
override func startObserving() {
|
|
@@ -23,8 +23,8 @@ class CloudStorageEventEmitter: RCTEventEmitter {
|
|
|
23
23
|
NotificationCenter.default.removeObserver(self, name: NSNotification.Name.NSUbiquityIdentityDidChange, object: nil)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
@objc func iCloudIdentityChanged(_
|
|
27
|
-
let
|
|
28
|
-
CloudStorageEventEmitter.shared.sendEvent(withName: "RNCloudStorage.cloud.availability-changed", body: ["available":
|
|
26
|
+
@objc func iCloudIdentityChanged(_: Notification? = nil) {
|
|
27
|
+
let isAvailable = CloudKitUtils.isCloudKitAvailable()
|
|
28
|
+
CloudStorageEventEmitter.shared.sendEvent(withName: "RNCloudStorage.cloud.availability-changed", body: ["available": isAvailable])
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
//
|
|
2
|
+
// CloudKitUtils.swift
|
|
3
|
+
// CloudStorage
|
|
4
|
+
//
|
|
5
|
+
// Created by Maximilian Krause on 27.09.24.
|
|
6
|
+
// Copyright © 2024 Kuatsu App Agency. All rights reserved.
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
import Foundation
|
|
10
|
+
|
|
11
|
+
enum CloudKitUtils {
|
|
12
|
+
private static let fileManager = FileManager.default
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
Checks if the CloudKit service is available.
|
|
16
|
+
|
|
17
|
+
- Returns: True if the CloudKit service is available, false otherwise.
|
|
18
|
+
*/
|
|
19
|
+
static func isCloudKitAvailable() -> Bool {
|
|
20
|
+
fileManager.ubiquityIdentityToken != nil
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
Downloads a file from iCloud.
|
|
25
|
+
|
|
26
|
+
- Parameter fileUrl: The URL of the file to download.
|
|
27
|
+
- Throws: An NSError if the file is not downloadable or the download failed.
|
|
28
|
+
*/
|
|
29
|
+
static func downloadFile(fileUrl: URL) throws {
|
|
30
|
+
let isDownloadable = fileManager.isUbiquitousItem(at: fileUrl)
|
|
31
|
+
|
|
32
|
+
if !isDownloadable {
|
|
33
|
+
throw CloudStorageError.fileNotDownloadable(path: fileUrl.path)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
do {
|
|
37
|
+
// trigger download of file
|
|
38
|
+
try fileManager.startDownloadingUbiquitousItem(at: fileUrl)
|
|
39
|
+
} catch {
|
|
40
|
+
throw CloudStorageError.fileNotDownloadable(path: fileUrl.path)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
Returns the iCloud directory URL for the given scope.
|
|
46
|
+
|
|
47
|
+
- Parameter scope: The scope of the directory.
|
|
48
|
+
- Returns: The URL of the iCloud directory, or nil if no directory is found.
|
|
49
|
+
*/
|
|
50
|
+
private static func getScopeDirectory(scope: DirectoryScope) -> URL? {
|
|
51
|
+
switch scope {
|
|
52
|
+
case .appData:
|
|
53
|
+
appDataDirectory
|
|
54
|
+
case .documents:
|
|
55
|
+
documentsDirectory
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
Parses a given path and directory scope to a full file URL.
|
|
61
|
+
|
|
62
|
+
- Parameter path: The path of the file.
|
|
63
|
+
- Parameter scope: The scope of the directory.
|
|
64
|
+
- Parameter shouldExist: Whether the file should exist. If true, throws an error if the file does not exist. If false, throws an error if the file exists. If nil, does not check if the file exists.
|
|
65
|
+
- Returns: The full URL of the file.
|
|
66
|
+
- Throws: An NSError if the scope directory couldn't be found or the file should exist but doesn't or vice versa.
|
|
67
|
+
*/
|
|
68
|
+
static func getFileURL(path: String, scope: DirectoryScope, _ shouldExist: Bool? = nil) throws -> URL {
|
|
69
|
+
guard let directory = getScopeDirectory(scope: scope) else {
|
|
70
|
+
throw CloudStorageError.directoryNotFound(path: path)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// append path to scope directory
|
|
74
|
+
let fileUrl = directory.appendingPathComponent(FileUtils.sanitizePath(path: path))
|
|
75
|
+
|
|
76
|
+
if shouldExist != nil {
|
|
77
|
+
let fileExists = try FileUtils.checkFileExists(fileUrl: fileUrl)
|
|
78
|
+
if shouldExist! && !fileExists {
|
|
79
|
+
throw CloudStorageError.fileNotFound(path: path)
|
|
80
|
+
} else if !shouldExist! && fileExists {
|
|
81
|
+
throw CloudStorageError.fileAlreadyExists(path: path)
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return fileUrl
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
Parses a given path and unchecked directory scope to a full file URL.
|
|
90
|
+
|
|
91
|
+
- Parameter path: The path of the file.
|
|
92
|
+
- Parameter scope: The scope of the directory. Will be checked for validity.
|
|
93
|
+
- Parameter shouldExist: Whether the file should exist. If true, throws an error if the file does not exist. If false, throws an error if the file exists. If nil, does not check if the file exists.
|
|
94
|
+
- Returns: The full URL of the file.
|
|
95
|
+
- Throws: An NSError if the scope directory couldn't be found or the file should exist but doesn't or vice versa.
|
|
96
|
+
*/
|
|
97
|
+
static func getFileURL(path: String, scope: String, _ shouldExist: Bool? = nil) throws -> URL {
|
|
98
|
+
guard let directoryScope = DirectoryScope(rawValue: scope) else {
|
|
99
|
+
throw CloudStorageError.invalidScope(scope: scope)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return try getFileURL(path: path, scope: directoryScope, shouldExist)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
static var appDataDirectory: URL? {
|
|
106
|
+
fileManager.url(forUbiquityContainerIdentifier: nil)
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
static var documentsDirectory: URL? {
|
|
110
|
+
fileManager.urls(for: .documentDirectory, in: .userDomainMask).first
|
|
111
|
+
}
|
|
112
|
+
}
|