Compare commits

..

8 Commits

Author SHA1 Message Date
Amir Abbas 72520973e9 Changed FileObject.allValues key type to URLResourceKey
- LocalFileProvider.storageProperties method now uses url resource values
2017-02-08 07:30:41 +03:30
Amir Abbas 63016285af Added UndoManager support to Local provider
- Refactored Local provider operations into one function
2017-02-08 04:54:18 +03:30
Amir Abbas 3245c9df03 Updated travis.yml 2017-02-07 21:59:17 +03:30
Amir Abbas ee1fa89747 Updated travis.yml 2017-02-07 12:07:59 +03:30
Amir Abbas 4a6b25deac Updated travis.yml 2017-02-07 09:48:12 +03:30
Amir Abbas 2423cbd0f6 Updated travis.yml 2017-02-07 02:55:22 +03:30
Amir Abbas 480099ca8a Updated travis.yml 2017-02-06 19:41:57 +03:30
Amir Abbas 755bf6bf84 Updated travis.yaml 2017-02-06 19:38:14 +03:30
11 changed files with 257 additions and 298 deletions
+21 -8
View File
@@ -3,30 +3,43 @@ language: objective-c
osx_image: xcode8.2
xcode_project: FileProvider.xcodeproj
env:
global:
- PROJECTNAME="FileProvider"
- FRAMEWORK_NAME="FileProvider.framework"
matrix:
- SHCEME="FileProvider OSX" SDK="macosx" ACTION="build"
- SHCEME="FileProvider iOS" SDK="iphonesimulator" ACTION="build"
- SHCEME="FileProvider tvOS" SDK="appletvsimulator" ACTION="build"
# matrix:
# - SHCEME="FileProvider OSX" SDK="macosx" ACTION="build"
# - SHCEME="FileProvider iOS" SDK="iphonesimulator" ACTION="build"
# - SHCEME="FileProvider tvOS" SDK="appletvsimulator" ACTION="build"
before_install:
- gem install xcpretty --no-rdoc --no-ri --no-document --quiet
- brew update
- brew outdated carthage || brew upgrade carthage
script:
- set pipefail
- xcodebuild -version
- xcodebuild -project $PROJECT.xcodeproj -scheme "$SCHEME" -sdk $SDK $ACTION ONLY_ACTIVE_ARCH=NO | xcpretty
# - pod lib lint --quick
- pod lib lint --quick
# - xcodebuild -project $PROJECTNAME.xcodeproj -scheme "$SCHEME" -sdk $SDK $ACTION ONLY_ACTIVE_ARCH=NO
- xcodebuild -project $PROJECTNAME.xcodeproj -scheme "FileProvider OSX" -sdk macosx build | xcpretty
- xcodebuild -project $PROJECTNAME.xcodeproj -scheme "FileProvider iOS" -sdk iphonesimulator build ONLY_ACTIVE_ARCH=NO | xcpretty
- xcodebuild -project $PROJECTNAME.xcodeproj -scheme "FileProvider tvOS" -sdk appletvsimulator build ONLY_ACTIVE_ARCH=NO | xcpretty
# after_success:
# - bash <(curl -s https://codecov.io/bash)
before_deploy:
- carthage build --no-skip-current
- brew update
- brew outdated carthage || brew upgrade carthage
- carthage version
- carthage archive $PROJECTNAME
deploy:
file: $PROJECTNAME.framework.zip
provider: releases
file: $FRAMEWORK_NAME.zip
skip_cleanup: true
on:
repo: amosavian/FileProvider
tags: true
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.12.2"
s.version = "0.12.3"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/OneDrive/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
+2 -2
View File
@@ -603,7 +603,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.12.2;
BUNDLE_VERSION_STRING = 0.12.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -633,7 +633,7 @@
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.12.2;
BUNDLE_VERSION_STRING = 0.12.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
+6 -6
View File
@@ -43,28 +43,28 @@ public final class DropboxFileObject: FileObject {
open internal(set) var serverTime: Date? {
get {
return allValues["NSURLServerDateKey"] as? Date
return allValues[.serverDate] as? Date
}
set {
allValues["NSURLServerDateKey"] = newValue
allValues[.serverDate] = newValue
}
}
open internal(set) var id: String? {
get {
return allValues["NSURLDocumentIdentifyKey"] as? String
return allValues[.documentIdentifierKey] as? String
}
set {
allValues["NSURLDocumentIdentifyKey"] = newValue
allValues[.documentIdentifierKey] = newValue
}
}
open internal(set) var rev: String? {
get {
return allValues[URLResourceKey.generationIdentifierKey.rawValue] as? String
return allValues[.generationIdentifierKey] as? String
}
set {
allValues[URLResourceKey.generationIdentifierKey.rawValue] = newValue
allValues[.generationIdentifierKey] = newValue
}
}
}
-1
View File
@@ -123,7 +123,6 @@ extension LocalFileProvider: ExtendedFileProvider {
completionHandler(dic, keys, nil)
}
}
}
+28 -21
View File
@@ -11,14 +11,14 @@ import Foundation
/// Containts path and attributes of a file or resource.
open class FileObject {
/// A `Dictionary` contains file information, using `URLResourceKey` keys.
open internal(set) var allValues: [String: Any]
open internal(set) var allValues: [URLResourceKey: Any]
internal init(allValues: [String: Any]) {
internal init(allValues: [URLResourceKey: Any]) {
self.allValues = allValues
}
internal init(url: URL, name: String, path: String) {
self.allValues = [String: Any]()
self.allValues = [URLResourceKey: Any]()
self.url = url
self.name = name
self.path = path
@@ -34,70 +34,70 @@ open class FileObject {
/// not supported by Dropbox provider.
open internal(set) var url: URL? {
get {
return allValues["NSURLFileURLKey"] as? URL
return allValues[.fileURL] as? URL
}
set {
allValues["NSURLFileURLKey"] = newValue
allValues[.fileURL] = newValue
}
}
/// Name of the file, usually equals with the last path component
open internal(set) var name: String {
get {
return allValues[URLResourceKey.nameKey.rawValue] as! String
return allValues[.nameKey] as! String
}
set {
allValues[URLResourceKey.nameKey.rawValue] = newValue
allValues[.nameKey] = newValue
}
}
/// Relative path of file object
open internal(set) var path: String {
get {
return allValues[URLResourceKey.pathKey.rawValue] as! String
return allValues[.pathKey] as! String
}
set {
allValues[URLResourceKey.pathKey.rawValue] = newValue
allValues[.pathKey] = newValue
}
}
/// Size of file on disk, return -1 for directories.
open internal(set) var size: Int64 {
get {
return allValues[URLResourceKey.fileSizeKey.rawValue] as? Int64 ?? -1
return allValues[.fileSizeKey] as? Int64 ?? -1
}
set {
allValues[URLResourceKey.fileSizeKey.rawValue] = newValue
allValues[.fileSizeKey] = newValue
}
}
/// The time contents of file has been created, returns nil if not set
open internal(set) var creationDate: Date? {
get {
return allValues[URLResourceKey.creationDateKey.rawValue] as? Date
return allValues[.creationDateKey] as? Date
}
set {
allValues[URLResourceKey.creationDateKey.rawValue] = newValue
allValues[.creationDateKey] = newValue
}
}
/// The time contents of file has been modified, returns nil if not set
open internal(set) var modifiedDate: Date? {
get {
return allValues[URLResourceKey.contentModificationDateKey.rawValue] as? Date
return allValues[.contentModificationDateKey] as? Date
}
set {
allValues[URLResourceKey.contentModificationDateKey.rawValue] = newValue
allValues[.contentModificationDateKey] = newValue
}
}
/// return resource type of file, usually directory, regular or symLink
open internal(set) var type: URLFileResourceType? {
get {
return allValues[URLResourceKey.fileResourceTypeKey.rawValue] as? URLFileResourceType
return allValues[.fileResourceTypeKey] as? URLFileResourceType
}
set {
allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue
allValues[.fileResourceTypeKey] = newValue
}
}
@@ -111,20 +111,20 @@ open class FileObject {
/// Setting this value on a file begining with dot has no effect
open internal(set) var isHidden: Bool {
get {
return allValues[URLResourceKey.isHiddenKey.rawValue] as? Bool ?? false
return allValues[.isHiddenKey] as? Bool ?? false
}
set {
allValues[URLResourceKey.isHiddenKey.rawValue] = newValue
allValues[.isHiddenKey] = newValue
}
}
/// File can not be written
open internal(set) var isReadOnly: Bool {
get {
return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true)
return !(allValues[.isWritableKey] as? Bool ?? true)
}
set {
allValues[URLResourceKey.isWritableKey.rawValue] = !newValue
allValues[.isWritableKey] = !newValue
}
}
@@ -296,6 +296,13 @@ extension URLFileResourceType {
}
}
internal extension URLResourceKey {
static let fileURL = URLResourceKey(rawValue: "NSURLFileURLKey")
static let serverDate = URLResourceKey(rawValue: "NSURLServerDateKey")
static let entryTag = URLResourceKey(rawValue: "NSURLEntryTagKey")
static let mimeType = URLResourceKey(rawValue: "NSURLMIMETypeIdentifierKey")
}
internal extension URL {
var uw_scheme: String {
return self.scheme ?? ""
+36
View File
@@ -492,6 +492,42 @@ public protocol FileProviderMonitor: FileProviderBasic {
func isRegisteredForNotification(path: String) -> Bool
}
public protocol FileProvideUndoable: FileProviderOperations {
var undoManager: UndoManager? { get set }
func canUndo(handle: OperationHandle) -> Bool
func canUndo(operation: FileOperationType) -> Bool
}
public extension FileProvideUndoable {
public func canUndo(operation: FileOperationType) -> Bool {
return undoOperation(for: operation) != nil
}
public func canUndo(handle: OperationHandle) -> Bool {
return canUndo(operation: handle.operationType)
}
internal func undoOperation(for operation: FileOperationType) -> FileOperationType? {
switch operation {
case .create(path: let path):
return .remove(path: path)
case .modify(path: _):
return nil
case .copy(source: _, destination: let dest):
return .remove(path: dest)
case .move(source: let source, destination: let dest):
return .move(source: dest, destination: source)
case .link(link: let link, target: _):
return .remove(path: link)
case .remove(path: _):
return nil
default:
return nil
}
}
}
public protocol FileProvider: FileProviderBasic, FileProviderOperations, FileProviderReadWrite, NSCopying {
}
+129 -243
View File
@@ -8,7 +8,7 @@
import Foundation
open class LocalFileProvider: FileProvider, FileProviderMonitor {
open class LocalFileProvider: FileProvider, FileProviderMonitor, FileProvideUndoable {
open class var type: String { return "Local" }
open var isPathRelative: Bool
open fileprivate(set) var baseURL: URL?
@@ -22,6 +22,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open private(set) var opFileManager = FileManager()
fileprivate var fileProviderManagerDelegate: LocalFileProviderManagerDelegate? = nil
open var undoManager: UndoManager? = nil
/**
Forces file operations to use `NSFileCoordinating`, should be set `true` if:
- Files are on ubiquity (iCloud) container.
@@ -112,7 +114,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
open func contentsOfDirectory(path: String, completionHandler: @escaping ((_ contents: [FileObject], _ error: Error?) -> Void)) {
dispatch_queue.async {
do {
let contents = try self.fileManager.contentsOfDirectory(at: self.url(of: path), includingPropertiesForKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .isHiddenKey, .volumeIsReadOnlyKey], options: .skipsSubdirectoryDescendants)
let contents = try self.fileManager.contentsOfDirectory(at: self.url(of: path), includingPropertiesForKeys: nil, options: .skipsSubdirectoryDescendants)
let filesAttributes = contents.flatMap({ (fileURL) -> LocalFileObject? in
let path = self.relativePathOf(url: fileURL)
return LocalFileObject(fileWithPath: path, relativeTo: self.baseURL)
@@ -125,9 +127,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
}
open func storageProperties(completionHandler: (@escaping (_ total: Int64, _ used: Int64) -> Void)) {
let dict = (try? FileManager.default.attributesOfFileSystem(forPath: baseURL?.path ?? "/"))
let totalSize = (dict?[.systemSize] as? NSNumber)?.int64Value ?? -1;
let freeSize = (dict?[.systemFreeSize] as? NSNumber)?.int64Value ?? 0;
let values = try? baseURL?.resourceValues(forKeys: [.volumeTotalCapacityKey, .volumeAvailableCapacityKey])
let totalSize = Int64(values??.volumeTotalCapacity ?? -1)
let freeSize = Int64(values??.volumeAvailableCapacity ?? 0)
completionHandler(totalSize, totalSize - freeSize)
}
@@ -142,245 +144,165 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@discardableResult
open func create(folder folderName: String, at atPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(folderName) + "/")
let url = self.url(of: atPath).appendingPathComponent(folderName)
let operationHandler: (URL) -> Void = { url in
do {
try self.opFileManager.createDirectory(at: url, withIntermediateDirectories: true, attributes: [:])
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
if isCoorinating {
let intent = NSFileAccessIntent.writingIntent(with: url, options: .forReplacing)
self.coordinated(intents: [intent], completionHandler: operationHandler, errorHandler: { error in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
})
} else {
operation_queue.addOperation {
operationHandler(url)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, completionHandler: completionHandler)
}
@discardableResult
open func create(file fileName: String, at atPath: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.create(path: (atPath as NSString).appendingPathComponent(fileName))
let url = self.url(of: atPath).appendingPathComponent(fileName, isDirectory: false)
let fileName = fileName.trimmingCharacters(in: CharacterSet(charactersIn: "/"))
let path = (atPath as NSString).appendingPathComponent(fileName)
let opType = FileOperationType.create(path: path)
let operationHandler: (URL) -> Void = { url in
let success = self.opFileManager.createFile(atPath: url.path, contents: data, attributes: nil)
if success {
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} else {
completionHandler?(self.throwError(atPath, code: URLError.cannotCreateFile as FoundationErrorEnum))
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
if isCoorinating {
let intent = NSFileAccessIntent.writingIntent(with:url, options: .forReplacing)
self.coordinated(intents: [intent], completionHandler: operationHandler, errorHandler: { error in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
})
} else {
operation_queue.addOperation {
operationHandler(url)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, data: data, atomically: true, completionHandler: completionHandler)
}
@discardableResult
open func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.move(source: path, destination: toPath)
let sourceUrl = self.url(of: path)
let destUrl = self.url(of: toPath)
let sourceIntent = NSFileAccessIntent.writingIntent(with: sourceUrl, options: .forDeleting)
let destIntent = NSFileAccessIntent.writingIntent(with: destUrl, options: .forReplacing)
let operationHandler: (URL, URL) -> Void = { sourceUrl, destUrl in
if !overwrite && self.fileManager.fileExists(atPath: destUrl.path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
return
}
do {
try self.opFileManager.moveItem(at: sourceUrl, to: destUrl)
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
if !overwrite && self.fileManager.fileExists(atPath: self.url(of: toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
return nil
}
if isCoorinating {
coordinated(intents: [sourceIntent, destIntent], completionHandler: operationHandler) { (error) in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
} else {
operation_queue.addOperation {
operationHandler(sourceUrl, destUrl)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, completionHandler: completionHandler)
}
@discardableResult
open func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: path, destination: toPath)
let sourceUrl = self.url(of: path)
let destUrl = self.url(of: toPath)
let sourceIntent = NSFileAccessIntent.readingIntent(with: sourceUrl, options: .withoutChanges)
let destIntent = NSFileAccessIntent.writingIntent(with: destUrl, options: .forDeleting)
let operationHandler: (URL, URL) -> Void = { sourceUrl, destUrl in
if !overwrite && self.fileManager.fileExists(atPath: destUrl.path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotMoveFile as FoundationErrorEnum))
return
}
do {
try self.opFileManager.copyItem(at: sourceUrl, to: destUrl)
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
if !overwrite && self.fileManager.fileExists(atPath: self.url(of: toPath).path) {
completionHandler?(self.throwError(toPath, code: URLError.cannotWriteToFile as FoundationErrorEnum))
return nil
}
if isCoorinating {
coordinated(intents: [sourceIntent, destIntent], moving: true, completionHandler: operationHandler) { (error) in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
} else {
operation_queue.addOperation {
operationHandler(sourceUrl, destUrl)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, completionHandler: completionHandler)
}
@discardableResult
open func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.remove(path: path)
let url = self.url(of: path)
let operationHandler: (URL) -> Void = { url in
do {
let successfulSecurityScopedResourceAccess = url.startAccessingSecurityScopedResource()
try self.opFileManager.removeItem(at: url)
if successfulSecurityScopedResourceAccess {
url.stopAccessingSecurityScopedResource()
}
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
if isCoorinating {
let intent = NSFileAccessIntent.writingIntent(with:url, options: .forReplacing)
self.coordinated(intents: [intent], completionHandler: operationHandler, errorHandler: { error in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
})
} else {
operation_queue.addOperation {
operationHandler(url)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, completionHandler: completionHandler)
}
@discardableResult
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
// TODO: Make use of overwrite parameter
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
operation_queue.addOperation {
do {
try self.opFileManager.copyItem(at: localFile, to: self.url(of: toPath))
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, completionHandler: completionHandler)
}
@discardableResult
open func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: path, destination: toLocalURL.absoluteString)
operation_queue.addOperation {
return self.doOperation(opType, completionHandler: completionHandler)
}
dynamic func doSimpleOperation(_ box: UndoBox) {
_ = self.doOperation(box.operation) { (_) in
return
}
}
@discardableResult
fileprivate func doOperation(_ opType: FileOperationType, data: Data? = nil, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
guard let sourcePath = opType.source else { return nil }
let destPath = opType.destination
let source: URL, dest: URL?
if sourcePath.hasPrefix("file://") {
source = URL(string: sourcePath)!
} else {
source = self.url(of: sourcePath)
}
if let destPath = destPath {
if destPath.hasPrefix("file://") {
dest = URL(string: destPath)!
} else {
dest = self.url(of: destPath)
}
} else {
dest = nil
}
let operationHandler: (URL, URL?) -> Void = { source, dest in
let successfulSecurityScopedResourceAccess = source.startAccessingSecurityScopedResource()
do {
try self.opFileManager.copyItem(at: self.url(of: path), to: toLocalURL)
switch opType {
case .create:
if sourcePath.hasSuffix("/") {
try self.opFileManager.createDirectory(at: source, withIntermediateDirectories: true, attributes: [:])
} else {
try data?.write(to: source, options: Data.WritingOptions.atomic)
}
case .modify:
try data?.write(to: source, options: atomically ? [.atomic] : [])
case .copy:
guard let dest = dest else { return }
try self.opFileManager.copyItem(at: source, to: dest)
case .move:
guard let dest = dest else { return }
try self.opFileManager.moveItem(at: source, to: dest)
case.remove:
try self.opFileManager.removeItem(at: source)
default:
return
}
if successfulSecurityScopedResourceAccess {
source.stopAccessingSecurityScopedResource()
}
if let undoOp = self.undoOperation(for: opType) {
let undoBox = UndoBox(provider: self, operation: undoOp)
self.undoManager?.registerUndo(withTarget: self, selector: #selector(LocalFileProvider.doSimpleOperation(_:)), object: undoBox)
}
completionHandler?(nil)
DispatchQueue.main.async {
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
if successfulSecurityScopedResourceAccess {
source.stopAccessingSecurityScopedResource()
}
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
if isCoorinating {
var intents = [NSFileAccessIntent]()
switch opType {
case .create, .remove, .modify:
intents.append(NSFileAccessIntent.writingIntent(with: source, options: .forReplacing))
case .copy:
guard let dest = dest else { return nil }
intents.append(NSFileAccessIntent.readingIntent(with: source, options: .withoutChanges))
intents.append(NSFileAccessIntent.writingIntent(with: dest, options: .forReplacing))
case .move:
guard let dest = dest else { return nil }
intents.append(NSFileAccessIntent.writingIntent(with: source, options: .forDeleting))
intents.append(NSFileAccessIntent.writingIntent(with: dest, options: .forReplacing))
default:
return nil
}
self.coordinated(intents: intents, completionHandler: operationHandler, errorHandler: { error in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
})
} else {
operation_queue.addOperation {
operationHandler(source, dest)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
}
@@ -433,11 +355,11 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
let operationHandler: (URL) -> Void = { url in
guard self.fileManager.fileExists(atPath: url.path) && !url.fileIsDirectory else {
completionHandler(nil, self.throwError(path, code: URLError.fileDoesNotExist as FoundationErrorEnum))
completionHandler(nil, self.throwError(path, code: CocoaError.fileNoSuchFile as FoundationErrorEnum))
return
}
guard let handle = FileHandle(forReadingAtPath: url.path) else {
completionHandler(nil, self.throwError(path, code: URLError.cannotOpenFile as FoundationErrorEnum))
completionHandler(nil, self.throwError(path, code: CocoaError.fileReadNoPermission as FoundationErrorEnum))
return
}
@@ -480,45 +402,7 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@discardableResult
open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
let url = self.url(of: path)
var options: Data.WritingOptions = []
if atomically {
options.insert(.atomic)
}
if overwrite {
options.insert(.withoutOverwriting)
}
let operationHandler: (URL) -> Void = { url in
do {
try data.write(to: url, options: atomically ? [.atomic] : [])
completionHandler?(nil)
DispatchQueue.main.async{
self.delegate?.fileproviderSucceed(self, operation: opType)
}
} catch let e {
completionHandler?(e)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
}
}
if isCoorinating {
let intent = NSFileAccessIntent.writingIntent(with: url, options: .forReplacing)
coordinated(intents: [intent], completionHandler: operationHandler, errorHandler: { error in
completionHandler?(error)
DispatchQueue.main.async {
self.delegate?.fileproviderFailed(self, operation: opType)
}
})
} else {
operation_queue.addOperation {
operationHandler(url)
}
}
return LocalOperationHandle(operationType: opType, baseURL: self.baseURL)
return self.doOperation(opType, data: data, atomically: atomically, completionHandler: completionHandler)
}
open func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -641,19 +525,21 @@ internal extension LocalFileProvider {
}
}
func coordinated(intents: [NSFileAccessIntent], moving: Bool = false, completionHandler: @escaping (_ sourceUrl: URL, _ destURL: URL) -> Void, errorHandler: ((_ error: Error) -> Void)? = nil) {
func coordinated(intents: [NSFileAccessIntent], moving: Bool = false, completionHandler: @escaping (_ sourceUrl: URL, _ destURL: URL?) -> Void, errorHandler: ((_ error: Error) -> Void)? = nil) {
let coordinator = NSFileCoordinator(filePresenter: nil)
coordinator.coordinate(with: intents, queue: operation_queue) { (error) in
if let error = error {
errorHandler?(error)
return
}
if moving {
coordinator.item(at: intents[0].url, willMoveTo: intents[1].url)
let newSource: URL = intents[0].url
let newDest: URL? = intents.count > 1 ? intents[1].url : nil
if moving, let newDest = newDest {
coordinator.item(at: newSource, willMoveTo: newDest)
}
completionHandler(intents[0].url, intents[1].url)
if moving {
coordinator.item(at: intents[0].url, didMoveTo: intents[1].url)
completionHandler(newSource, newDest)
if moving, let newDest = newDest {
coordinator.item(at: newSource, didMoveTo: newDest)
}
}
}
+24 -6
View File
@@ -37,12 +37,12 @@ public final class LocalFileObject: FileObject {
public convenience init?(fileWithURL fileURL: URL) {
do {
let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey])
let values = try fileURL.resourceValues(forKeys: [.nameKey, .fileSizeKey, .fileAllocatedSizeKey, .creationDateKey, .contentModificationDateKey, .fileResourceTypeKey, .isHiddenKey, .isWritableKey, .typeIdentifierKey, .generationIdentifierKey, .documentIdentifierKey])
let path = fileURL.relativePath.hasPrefix("/") ? fileURL.relativePath : "/" + fileURL.relativePath
self.init(url: fileURL, name: values.name ?? fileURL.lastPathComponent, path: path)
for (key, value) in values.allValues {
self.allValues[key.rawValue] = value
self.allValues[key] = value
}
} catch {
return nil
@@ -51,20 +51,28 @@ public final class LocalFileObject: FileObject {
open internal(set) var allocatedSize: Int64 {
get {
return allValues[URLResourceKey.fileAllocatedSizeKey.rawValue] as? Int64 ?? 0
return allValues[.fileAllocatedSizeKey] as? Int64 ?? 0
}
set {
allValues[URLResourceKey.fileAllocatedSizeKey.rawValue] = Int(exactly: newValue) ?? Int.max
allValues[.fileAllocatedSizeKey] = Int(exactly: newValue) ?? Int.max
}
}
open internal(set) var id: Int? {
get {
return allValues[.documentIdentifierKey] as? Int
}
set {
allValues[.documentIdentifierKey] = newValue
}
}
open var rev: String? {
get {
let data = allValues[URLResourceKey.generationIdentifierKey.rawValue] as? Data
let data = allValues[.generationIdentifierKey] as? Data
return data?.map { String(format: "%02hhx", $0) }.joined()
}
}
}
internal final class LocalFolderMonitor {
@@ -297,6 +305,16 @@ open class LocalOperationHandle: OperationHandle {
}
}
class UndoBox: NSObject {
weak var provider: FileProvideUndoable?
let operation: FileOperationType
init(provider: FileProvideUndoable, operation: FileOperationType) {
self.provider = provider
self.operation = operation
}
}
internal extension URL {
var fileIsDirectory: Bool {
return (try? self.resourceValues(forKeys: [.isDirectoryKey]))?.isDirectory ?? false
+6 -6
View File
@@ -48,28 +48,28 @@ public final class OneDriveFileObject: FileObject {
open internal(set) var id: String? {
get {
return allValues["NSURLDocumentIdentifyKey"] as? String
return allValues[.documentIdentifierKey] as? String
}
set {
allValues["NSURLDocumentIdentifyKey"] = newValue
allValues[.documentIdentifierKey] = newValue
}
}
open internal(set) var contentType: String {
get {
return allValues["NSURLContentTypeKey"] as? String ?? ""
return allValues[.mimeType] as? String ?? ""
}
set {
allValues["NSURLContentTypeKey"] = newValue
allValues[.mimeType] = newValue
}
}
open internal(set) var entryTag: String? {
get {
return allValues["NSURLEntryTagKey"] as? String
return allValues[.entryTag] as? String
}
set {
allValues["NSURLEntryTagKey"] = newValue
allValues[.entryTag] = newValue
}
}
}
+4 -4
View File
@@ -582,20 +582,20 @@ public final class WebDavFileObject: FileObject {
/// MIME type of the file
open internal(set) var contentType: String {
get {
return allValues["NSURLContentTypeKey"] as? String ?? ""
return allValues[.mimeType] as? String ?? ""
}
set {
allValues["NSURLContentTypeKey"] = newValue
allValues[.mimeType] = newValue
}
}
/// HTTP E-Tag, can be used to mark changed files
open internal(set) var entryTag: String? {
get {
return allValues["NSURLEntryTagKey"] as? String
return allValues[.entryTag] as? String
}
set {
allValues["NSURLEntryTagKey"] = newValue
allValues[.entryTag] = newValue
}
}
}