FilesystemPlugin.swift 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import Foundation
  2. import Capacitor
  3. import IONFilesystemLib
  4. typealias FileService = any IONFILEDirectoryManager & IONFILEFileManager
  5. /**
  6. * Please read the Capacitor iOS Plugin Development Guide
  7. * here: https://capacitorjs.com/docs/plugins/ios
  8. */
  9. @objc(FilesystemPlugin)
  10. public class FilesystemPlugin: CAPPlugin, CAPBridgedPlugin {
  11. public let identifier = "FilesystemPlugin"
  12. public let jsName = "Filesystem"
  13. public let pluginMethods: [CAPPluginMethod] = [
  14. CAPPluginMethod(name: "readFile", returnType: CAPPluginReturnPromise),
  15. CAPPluginMethod(name: "readFileInChunks", returnType: CAPPluginReturnCallback),
  16. CAPPluginMethod(name: "writeFile", returnType: CAPPluginReturnPromise),
  17. CAPPluginMethod(name: "appendFile", returnType: CAPPluginReturnPromise),
  18. CAPPluginMethod(name: "deleteFile", returnType: CAPPluginReturnPromise),
  19. CAPPluginMethod(name: "mkdir", returnType: CAPPluginReturnPromise),
  20. CAPPluginMethod(name: "rmdir", returnType: CAPPluginReturnPromise),
  21. CAPPluginMethod(name: "readdir", returnType: CAPPluginReturnPromise),
  22. CAPPluginMethod(name: "getUri", returnType: CAPPluginReturnPromise),
  23. CAPPluginMethod(name: "stat", returnType: CAPPluginReturnPromise),
  24. CAPPluginMethod(name: "rename", returnType: CAPPluginReturnPromise),
  25. CAPPluginMethod(name: "copy", returnType: CAPPluginReturnPromise),
  26. CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
  27. CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
  28. CAPPluginMethod(name: "downloadFile", returnType: CAPPluginReturnPromise)
  29. ]
  30. private let legacyImplementation = LegacyFilesystemImplementation()
  31. private var fileService: FileService?
  32. override public func load() {
  33. self.fileService = IONFILEManager()
  34. }
  35. func getService() -> Result<FileService, FilesystemError> {
  36. if fileService == nil { load() }
  37. return fileService.map(Result.success) ?? .failure(.bridgeNotInitialised)
  38. }
  39. @objc override public func checkPermissions(_ call: CAPPluginCall) {
  40. call.handlePermissionSuccess()
  41. }
  42. @objc override public func requestPermissions(_ call: CAPPluginCall) {
  43. call.handlePermissionSuccess()
  44. }
  45. }
  46. // MARK: - Public API Methods
  47. private extension FilesystemPlugin {
  48. /**
  49. * Read a file from the filesystem.
  50. */
  51. @objc func readFile(_ call: CAPPluginCall) {
  52. let encoding = call.getEncoding(Constants.MethodParameter.encoding)
  53. performSinglePathOperation(call) {
  54. .readFile(url: $0, encoding: encoding)
  55. }
  56. }
  57. @objc func readFileInChunks(_ call: CAPPluginCall) {
  58. let encoding = call.getEncoding(Constants.MethodParameter.encoding)
  59. guard let chunkSize = call.getInt(Constants.MethodParameter.chunkSize) else {
  60. return call.handleError(.invalidInput(method: call.getIONFileMethod()))
  61. }
  62. performSinglePathOperation(call) {
  63. .readFileInChunks(url: $0, encoding: encoding, chunkSize: chunkSize)
  64. }
  65. }
  66. /**
  67. * Write a file to the filesystem.
  68. */
  69. @objc func writeFile(_ call: CAPPluginCall) {
  70. guard let encodingMapper = call.getEncodingMapper() else {
  71. return call.handleError(.invalidInput(method: call.getIONFileMethod()))
  72. }
  73. let recursive = call.getBool(Constants.MethodParameter.recursive, false)
  74. performSinglePathOperation(call) {
  75. .write(url: $0, encodingMapper: encodingMapper, recursive: recursive)
  76. }
  77. }
  78. /**
  79. * Append to a file.
  80. */
  81. @objc func appendFile(_ call: CAPPluginCall) {
  82. guard let encodingMapper = call.getEncodingMapper() else {
  83. return call.handleError(.invalidInput(method: call.getIONFileMethod()))
  84. }
  85. let recursive = call.getBool(Constants.MethodParameter.recursive, false)
  86. performSinglePathOperation(call) {
  87. .append(url: $0, encodingMapper: encodingMapper, recursive: recursive)
  88. }
  89. }
  90. /**
  91. * Delete a file.
  92. */
  93. @objc func deleteFile(_ call: CAPPluginCall) {
  94. performSinglePathOperation(call) {
  95. .delete(url: $0)
  96. }
  97. }
  98. /**
  99. * Make a new directory, optionally creating parent folders first.
  100. */
  101. @objc func mkdir(_ call: CAPPluginCall) {
  102. let recursive = call.getBool(Constants.MethodParameter.recursive, false)
  103. performSinglePathOperation(call) {
  104. .mkdir(url: $0, recursive: recursive)
  105. }
  106. }
  107. /**
  108. * Remove a directory.
  109. */
  110. @objc func rmdir(_ call: CAPPluginCall) {
  111. let recursive = call.getBool(Constants.MethodParameter.recursive, false)
  112. performSinglePathOperation(call) {
  113. .rmdir(url: $0, recursive: recursive)
  114. }
  115. }
  116. /**
  117. * Read the contents of a directory.
  118. */
  119. @objc func readdir(_ call: CAPPluginCall) {
  120. performSinglePathOperation(call) {
  121. .readdir(url: $0)
  122. }
  123. }
  124. @objc func stat(_ call: CAPPluginCall) {
  125. performSinglePathOperation(call) {
  126. .stat(url: $0)
  127. }
  128. }
  129. @objc func getUri(_ call: CAPPluginCall) {
  130. performSinglePathOperation(call) {
  131. .getUri(url: $0)
  132. }
  133. }
  134. /**
  135. * Rename a file or directory.
  136. */
  137. @objc func rename(_ call: CAPPluginCall) {
  138. performDualPathOperation(call) {
  139. .rename(source: $0, destination: $1)
  140. }
  141. }
  142. /**
  143. * Copy a file or directory.
  144. */
  145. @objc func copy(_ call: CAPPluginCall) {
  146. performDualPathOperation(call) {
  147. .copy(source: $0, destination: $1)
  148. }
  149. }
  150. /**
  151. * [DEPRECATED] Download a file
  152. */
  153. @available(*, deprecated, message: "Use @capacitor/file-transfer plugin instead.")
  154. @objc func downloadFile(_ call: CAPPluginCall) {
  155. guard let url = call.getString("url") else { return call.reject("Must provide a URL") }
  156. let progressEmitter: LegacyFilesystemImplementation.ProgressEmitter = { bytes, contentLength in
  157. self.notifyListeners("progress", data: [
  158. "url": url,
  159. "bytes": bytes,
  160. "contentLength": contentLength
  161. ])
  162. }
  163. do {
  164. try legacyImplementation.downloadFile(call: call, emitter: progressEmitter, config: bridge?.config)
  165. } catch let error {
  166. call.reject(error.localizedDescription)
  167. }
  168. }
  169. }
  170. // MARK: - Operation Execution
  171. private extension FilesystemPlugin {
  172. func performSinglePathOperation(_ call: CAPPluginCall, operationBuilder: (URL) -> FilesystemOperation) {
  173. executeOperation(call) { service in
  174. FilesystemLocationResolver(service: service)
  175. .resolveSinglePath(from: call)
  176. .map { operationBuilder($0) }
  177. }
  178. }
  179. func performDualPathOperation(_ call: CAPPluginCall, operationBuilder: (URL, URL) -> FilesystemOperation) {
  180. executeOperation(call) { service in
  181. FilesystemLocationResolver(service: service)
  182. .resolveDualPaths(from: call)
  183. .map { operationBuilder($0.source, $0.destination) }
  184. }
  185. }
  186. func executeOperation(_ call: CAPPluginCall, operationProvider: (FileService) -> Result<FilesystemOperation, FilesystemError>) {
  187. switch getService() {
  188. case .success(let service):
  189. switch operationProvider(service) {
  190. case .success(let operation):
  191. let executor = FilesystemOperationExecutor(service: service)
  192. executor.execute(operation, call)
  193. case .failure(let error):
  194. call.handleError(error)
  195. }
  196. case .failure(let error):
  197. call.handleError(error)
  198. }
  199. }
  200. }