Compare commits

...

20 Commits

Author SHA1 Message Date
Amir Abbas d2cf657ab2 Added Thumbnail and Meta-information generator to Local and Dropbox 2017-01-16 15:20:44 +03:30
Alek Slater f2d54c7f92 incorrect usage of string.remove in correctPath method
- string remove needs to be given the index before the endIndex or it will crash (#22) needs to be given the index before the endIndex or it will crash
2016-12-19 18:10:14 +03:30
Amir Abbas 0e9a82a288 Resolves #20, Dropbox/WebDav file size 2016-12-15 14:42:00 +03:30
Amir Abbas ea7e089718 Updated for Xcode 8.2 2016-12-13 19:12:08 +03:30
Amir Abbas 19ea14ebc1 Added FileObjectSorting to sort FileObjects conveniently 2016-12-12 01:57:34 +03:30
Amir Abbas 47885c0a4e Deprecated FileObject.fileType with .type 2016-12-09 21:06:11 +03:30
Alek Slater ac44aa190f Overwrite param added to copyItem + minor bugfix and improvements (#18)
- Added overwrite param to copyItem from local to remote
- fixed an issue with file upload on dropbox that wouldnt work for people in non UTC timezones.
- made filebased upload for dropbox not read file data into memory let URLSession stream from the disk instead
2016-12-09 20:58:50 +03:30
Amir Abbas 50f6a393a0 Compiler Optimizations, Dropbox readme 2016-12-09 19:07:46 +03:30
Amir Abbas 779b38f381 Default values implemented in protocol level 2016-12-09 10:04:41 +03:30
Amir Abbas a374841883 Refined SPM/manual installation guide in Readme 2016-12-08 20:41:52 +03:30
Amir Abbas 0123d8b117 Overwrite option to writeContents 2016-12-07 21:24:19 +03:30
Amir Abbas Mousavian fdc4bb818d Merge pull request #17 from skela/master
Added overwrite option to DropboxProvider.writeContents()
2016-12-07 20:13:17 +03:30
Amir Abbas Mousavian a08a9fe7a0 Updated semantics
and changed default overwrite value to false
2016-12-07 20:12:15 +03:30
Alek Slater 792ac6b015 Update DropboxFileProvider.swift
Exposes the overwrite parameter for writeContents in DropboxFileProvider
2016-12-07 11:27:51 +08:00
Amir Abbas 8bad5944bc Updated pod spec version to 0.8.1 2016-12-07 02:36:49 +03:30
Amir Abbas 6ef2ab11c4 Fixed bug in Data(value:) initializer. Switch uuid init via this 2016-12-07 02:34:34 +03:30
Amir Abbas Mousavian 7f27d46c70 Merge pull request #16 from RndmTsk/master
Fixes #15, FileObject.fileType always returns nil.
2016-12-07 02:29:28 +03:30
Terry Latanville b1ec99b1b8 Fixes #15, FileObject.fileType always returns nil. 2016-12-06 17:18:41 -05:00
Amir Abbas Mousavian c4b8065cd3 Merge pull request #14 from skela/master
Update DropboxFileProvider.swift
2016-12-06 10:51:58 +03:30
Alek Slater 2d8454c711 Update DropboxFileProvider.swift
Using the same request dict creation method that can be found in the contents method, to avoid error when trying to use copyItem to copy a remote file to a local file destination.
2016-12-06 14:51:48 +08:00
18 changed files with 1221 additions and 276 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ Pod::Spec.new do |s|
#
s.name = "FileProvider"
s.version = "0.8.0"
s.version = "0.9.0"
s.summary = "FileManager replacement for Local and Remote (WebDAV/Dropbox/SMB2) files on iOS and macOS."
# This description is used to generate tags and improve search results.
+88 -6
View File
@@ -92,6 +92,26 @@
799396E01D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
799396E11D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
799396E21D48C02300086753 /* WebDAVFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 799396A61D48C02300086753 /* WebDAVFileProvider.swift */; };
79BD638C1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD638B1E2CC2300035128C /* ExtendedLocalFileProvider.swift */; };
79BD638D1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD638B1E2CC2300035128C /* ExtendedLocalFileProvider.swift */; };
79BD638E1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79BD638B1E2CC2300035128C /* ExtendedLocalFileProvider.swift */; };
79BD63A81E2CC2940035128C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63A71E2CC2940035128C /* CoreGraphics.framework */; };
79BD63AA1E2CC2BB0035128C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63A91E2CC2BB0035128C /* AVFoundation.framework */; };
79BD63AC1E2CC2C20035128C /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63AB1E2CC2C20035128C /* ImageIO.framework */; };
79BD63AE1E2CC2EB0035128C /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63AD1E2CC2EB0035128C /* MediaPlayer.framework */; };
79BD63B01E2CC3300035128C /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63AF1E2CC3300035128C /* libxml2.tbd */; };
79BD63B21E2CC3350035128C /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63B11E2CC3350035128C /* ImageIO.framework */; };
79BD63B41E2CC33D0035128C /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63B31E2CC33D0035128C /* MediaPlayer.framework */; };
79BD63B61E2CC3860035128C /* CoreFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63B51E2CC3860035128C /* CoreFoundation.framework */; };
79BD63B81E2CC38D0035128C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63B71E2CC38D0035128C /* AVFoundation.framework */; };
79BD63BA1E2CC39B0035128C /* libxml2.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63B91E2CC39B0035128C /* libxml2.tbd */; };
79BD63BC1E2CC3B90035128C /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63BB1E2CC3B90035128C /* MediaPlayer.framework */; };
79BD63BE1E2CC3C20035128C /* ImageIO.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63BD1E2CC3C20035128C /* ImageIO.framework */; };
79BD63C01E2CC3CD0035128C /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63BF1E2CC3CD0035128C /* CoreGraphics.framework */; };
79BD63C21E2CC3D30035128C /* AVFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 79BD63C11E2CC3D30035128C /* AVFoundation.framework */; };
79F5745B1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
79F5745C1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
79F5745D1DFDB10B00179ABF /* FileObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 79F5745A1DFDB10A00179ABF /* FileObject.swift */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
@@ -131,6 +151,22 @@
799396A31D48C02300086753 /* SMB2Types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMB2Types.swift; sourceTree = "<group>"; };
799396A41D48C02300086753 /* SMBErrorType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SMBErrorType.swift; sourceTree = "<group>"; };
799396A61D48C02300086753 /* WebDAVFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = WebDAVFileProvider.swift; sourceTree = "<group>"; };
79BD638B1E2CC2300035128C /* ExtendedLocalFileProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ExtendedLocalFileProvider.swift; sourceTree = "<group>"; };
79BD63A71E2CC2940035128C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
79BD63A91E2CC2BB0035128C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
79BD63AB1E2CC2C20035128C /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; };
79BD63AD1E2CC2EB0035128C /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS10.2.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; };
79BD63AF1E2CC3300035128C /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = usr/lib/libxml2.tbd; sourceTree = SDKROOT; };
79BD63B11E2CC3350035128C /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = System/Library/Frameworks/ImageIO.framework; sourceTree = SDKROOT; };
79BD63B31E2CC33D0035128C /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = System/Library/Frameworks/MediaPlayer.framework; sourceTree = SDKROOT; };
79BD63B51E2CC3860035128C /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = System/Library/Frameworks/CoreFoundation.framework; sourceTree = SDKROOT; };
79BD63B71E2CC38D0035128C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = System/Library/Frameworks/AVFoundation.framework; sourceTree = SDKROOT; };
79BD63B91E2CC39B0035128C /* libxml2.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libxml2.tbd; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/usr/lib/libxml2.tbd; sourceTree = DEVELOPER_DIR; };
79BD63BB1E2CC3B90035128C /* MediaPlayer.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaPlayer.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/MediaPlayer.framework; sourceTree = DEVELOPER_DIR; };
79BD63BD1E2CC3C20035128C /* ImageIO.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ImageIO.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/ImageIO.framework; sourceTree = DEVELOPER_DIR; };
79BD63BF1E2CC3CD0035128C /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; };
79BD63C11E2CC3D30035128C /* AVFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AVFoundation.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS10.1.sdk/System/Library/Frameworks/AVFoundation.framework; sourceTree = DEVELOPER_DIR; };
79F5745A1DFDB10A00179ABF /* FileObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FileObject.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -138,6 +174,10 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
79BD63AA1E2CC2BB0035128C /* AVFoundation.framework in Frameworks */,
79BD63AC1E2CC2C20035128C /* ImageIO.framework in Frameworks */,
79BD63A81E2CC2940035128C /* CoreGraphics.framework in Frameworks */,
79BD63AE1E2CC2EB0035128C /* MediaPlayer.framework in Frameworks */,
791950F51DE58A5400B4426E /* libxml2.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -146,6 +186,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
79BD63B81E2CC38D0035128C /* AVFoundation.framework in Frameworks */,
79BD63B61E2CC3860035128C /* CoreFoundation.framework in Frameworks */,
79BD63B21E2CC3350035128C /* ImageIO.framework in Frameworks */,
79BD63B41E2CC33D0035128C /* MediaPlayer.framework in Frameworks */,
79BD63B01E2CC3300035128C /* libxml2.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -153,6 +198,11 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
79BD63C21E2CC3D30035128C /* AVFoundation.framework in Frameworks */,
79BD63C01E2CC3CD0035128C /* CoreGraphics.framework in Frameworks */,
79BD63BE1E2CC3C20035128C /* ImageIO.framework in Frameworks */,
79BD63BC1E2CC3B90035128C /* MediaPlayer.framework in Frameworks */,
79BD63BA1E2CC39B0035128C /* libxml2.tbd in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -162,6 +212,20 @@
791950F31DE58A5300B4426E /* Frameworks */ = {
isa = PBXGroup;
children = (
79BD63C11E2CC3D30035128C /* AVFoundation.framework */,
79BD63BF1E2CC3CD0035128C /* CoreGraphics.framework */,
79BD63BD1E2CC3C20035128C /* ImageIO.framework */,
79BD63BB1E2CC3B90035128C /* MediaPlayer.framework */,
79BD63B91E2CC39B0035128C /* libxml2.tbd */,
79BD63B71E2CC38D0035128C /* AVFoundation.framework */,
79BD63B51E2CC3860035128C /* CoreFoundation.framework */,
79BD63B31E2CC33D0035128C /* MediaPlayer.framework */,
79BD63B11E2CC3350035128C /* ImageIO.framework */,
79BD63AF1E2CC3300035128C /* libxml2.tbd */,
79BD63AD1E2CC2EB0035128C /* MediaPlayer.framework */,
79BD63AB1E2CC2C20035128C /* ImageIO.framework */,
79BD63A91E2CC2BB0035128C /* AVFoundation.framework */,
79BD63A71E2CC2940035128C /* CoreGraphics.framework */,
791950F41DE58A5400B4426E /* libxml2.tbd */,
);
name = Frameworks;
@@ -182,6 +246,7 @@
7993965B1D48B7BF00086753 = {
isa = PBXGroup;
children = (
79E34A101E2AC6C600E1293B /* Extra */,
799396911D48C02300086753 /* Sources */,
7993968A1D48B8C700086753 /* Pod */,
799396681D48B7F600086753 /* Products */,
@@ -218,8 +283,10 @@
794C21FD1D58912A00EC49B8 /* DropboxHelper.swift */,
799396941D48C02300086753 /* FileProvider.h */,
799396951D48C02300086753 /* FileProvider.swift */,
79F5745A1DFDB10A00179ABF /* FileObject.swift */,
799396961D48C02300086753 /* LocalFileProvider.swift */,
792572401DF23BDA006A1526 /* LocalHelper.swift */,
79BD638B1E2CC2300035128C /* ExtendedLocalFileProvider.swift */,
7902C0851D61B56D00564440 /* RemoteSession.swift */,
7924B1A81D89F79200589DB7 /* FPSStreamTask.swift */,
799396971D48C02300086753 /* SMBClient.swift */,
@@ -249,6 +316,13 @@
path = SMBTypes;
sourceTree = "<group>";
};
79E34A101E2AC6C600E1293B /* Extra */ = {
isa = PBXGroup;
children = (
);
name = Extra;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -402,6 +476,7 @@
799396D41D48C02300086753 /* SMB2Tree.swift in Sources */,
7924B1A21D89DAE000589DB7 /* Options.swift in Sources */,
792572411DF23BDA006A1526 /* LocalHelper.swift in Sources */,
79F5745B1DFDB10B00179ABF /* FileObject.swift in Sources */,
7924B1991D89DAE000589DB7 /* Element.swift in Sources */,
799396C81D48C02300086753 /* SMB2IOCtl.swift in Sources */,
799396D71D48C02300086753 /* SMB2Types.swift in Sources */,
@@ -422,6 +497,7 @@
799396C21D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CB1D48C02300086753 /* SMB2Query.swift in Sources */,
799396AA1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
79BD638C1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */,
7924B1B31D89FD6400589DB7 /* SMBClient.swift in Sources */,
7924B1A51D89DAE000589DB7 /* Parser.swift in Sources */,
799396B01D48C02300086753 /* FileProvider.swift in Sources */,
@@ -437,6 +513,7 @@
799396D51D48C02300086753 /* SMB2Tree.swift in Sources */,
7924B1A31D89DAE000589DB7 /* Options.swift in Sources */,
792572421DF23BDA006A1526 /* LocalHelper.swift in Sources */,
79F5745C1DFDB10B00179ABF /* FileObject.swift in Sources */,
7924B1B01D89F7DE00589DB7 /* FPSStreamTask.swift in Sources */,
7924B19A1D89DAE000589DB7 /* Element.swift in Sources */,
799396C91D48C02300086753 /* SMB2IOCtl.swift in Sources */,
@@ -457,6 +534,7 @@
799396C31D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CC1D48C02300086753 /* SMB2Query.swift in Sources */,
7924B1AD1D89F7D800589DB7 /* SMBClient.swift in Sources */,
79BD638D1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */,
799396AB1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
7924B1A61D89DAE000589DB7 /* Parser.swift in Sources */,
799396B11D48C02300086753 /* FileProvider.swift in Sources */,
@@ -472,6 +550,7 @@
799396D61D48C02300086753 /* SMB2Tree.swift in Sources */,
7924B1A41D89DAE000589DB7 /* Options.swift in Sources */,
792572431DF23BDA006A1526 /* LocalHelper.swift in Sources */,
79F5745D1DFDB10B00179ABF /* FileObject.swift in Sources */,
7924B1B11D89F7DF00589DB7 /* FPSStreamTask.swift in Sources */,
7924B19B1D89DAE000589DB7 /* Element.swift in Sources */,
799396CA1D48C02300086753 /* SMB2IOCtl.swift in Sources */,
@@ -492,6 +571,7 @@
799396C41D48C02300086753 /* SMB2FileHandle.swift in Sources */,
799396CD1D48C02300086753 /* SMB2Query.swift in Sources */,
7924B1AE1D89F7D900589DB7 /* SMBClient.swift in Sources */,
79BD638E1E2CC2300035128C /* ExtendedLocalFileProvider.swift in Sources */,
799396AC1D48C02300086753 /* DropboxFileProvider.swift in Sources */,
7924B1A71D89DAE000589DB7 /* Parser.swift in Sources */,
799396B21D48C02300086753 /* FileProvider.swift in Sources */,
@@ -505,7 +585,7 @@
799396601D48B7BF00086753 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.7.0;
BUNDLE_VERSION_STRING = 0.8.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -528,14 +608,14 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
ONLY_ACTIVE_ARCH = YES;
SWIFT_VERSION = 3.0.1;
SWIFT_VERSION = 3.0;
};
name = Debug;
};
799396611D48B7BF00086753 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_VERSION_STRING = 0.7.0;
BUNDLE_VERSION_STRING = 0.8.3;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_EMPTY_BODY = YES;
@@ -556,7 +636,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
SWIFT_VERSION = 3.0.1;
SWIFT_VERSION = 3.0;
};
name = Release;
};
@@ -565,7 +645,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.8.0;
BUNDLE_VERSION_STRING = 0.8.2;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -618,7 +698,7 @@
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
APPLICATION_EXTENSION_API_ONLY = YES;
BUNDLE_VERSION_STRING = 0.8.0;
BUNDLE_VERSION_STRING = 0.8.2;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
@@ -686,6 +766,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
FRAMEWORK_VERSION = A;
@@ -741,6 +822,7 @@
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
DYLIB_INSTALL_NAME_BASE = "@rpath";
ENABLE_BITCODE = NO;
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
FRAMEWORK_VERSION = A;
+48 -8
View File
@@ -43,21 +43,26 @@ Legacy version is available in swift-2 branch
### Cocoapods / Carthage / Swift Package Manager
FileProvider supports both CocoaPods.
Add this line to your pods file:
```ruby
pod "FileProvider"
```
Or add this to cartfile:
Or add this to Cartfile:
```
github "amosavian/FileProvider"
```
### Git
Or to use in Swift Package Manager add this line in `Dependencies`:
```swift
.Package(url: "https://github.com/amosavian/FileProvider.git", majorVersion: 0, minorVersion: 8)
```
### Manually
To have latest updates with ease, use this command on terminal to get a clone:
```bash
@@ -75,11 +80,11 @@ if you have a git based project, use this command in your projects directory to
```bash
git submodule add https://github.com/amosavian/FileProvider
```
Then you can do either:
### Manually
**First way:** Copy Source folder to your project and Voila!
* Copy Source folder to your project and Voila!
**Second way:** Drop FileProvider.xcodeproj to you Xcode workspace and add the framework to your Embeded Binaries in target.
* Drop FileProvider.xcodeproj to you Xcode workspace and add the framework to your Embeded Binaries in target.
## Usage
@@ -112,7 +117,7 @@ let webdavProvider = WebDAVFileProvider(baseURL: URL(string: "http://www.example
* In case you want to connect non-secure servers for WebDAV (http) in iOS 9+ / macOS 10.11+ you should disable App Transport Security (ATS) according to [this guide.](https://gist.github.com/mlynch/284699d676fe9ed0abfa)
* For Dropbox, user is clientID and password is Token which both must be retrieved via [OAuth2 API of Dropbox](https://www.dropbox.com/developers/reference/oauth-guide). There are libraries like [p2/OAuth2](https://github.com/p2/OAuth2) or [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) which can facilate the procedure to retrieve token.
* For Dropbox, user is clientID and password is Token which both must be retrieved via [OAuth2 API of Dropbox](https://www.dropbox.com/developers/reference/oauth-guide). There are libraries like [p2/OAuth2](https://github.com/p2/OAuth2) or [OAuthSwift](https://github.com/OAuthSwift/OAuthSwift) which can facilate the procedure to retrieve token. The latter is easier to use and prefered.
For interaction with UI, set delegate variable of `FileProvider` object
@@ -323,6 +328,41 @@ documentsProvider.unregisterNotifcation(path: provider.currentPath)
* **Please note** in LocalFileProvider it will also monitor changes in subfolders. This behaviour can varies according to file system specification.
### Thumbnail and meta-information
Providers which conform `ExtendedFileProvider` are able to generate thumbnail or provide file meta-information for images, media and pdf files.
Local and Dropbox providers support this functionality.
##### Thumbnails
To check either file thumbnail is supported or not and fetch thumbnail, use (and modify) these example code:
```swift
let path = "/newImage.jpg"
let thumbSize = CGSize(width: 64, height: 64)
if documentsProvider.thumbnailOfFileSupported(path: path {
documentsProvider..thumbnailOfFile(path: file.path, dimension: thumbSize, completionHandler: { (image, error) in
DispatchQueue.main.async {
self.previewImage.image = image
}
}
}
```
##### Meta-informations
To get meta-information like image/video taken date, dimension, etc., use (and modify) these example code:
```swift
if documentsProvider..propertiesOfFile(path: file.path, completionHandler: { (propertiesDictionary, keys, error) in
for key in keys {
print("\(key): \(propertiesDictionary[key])")
}
}
```
* **Bonus:** You can modify/extend Local provider generator by setting `LocalFileInformationGenerator` static variables and methods
## Contribute
We would love for you to contribute to **FileProvider**, check the `LICENSE` file for more info.
+60 -29
View File
@@ -118,11 +118,11 @@ extension DropboxFileProvider: FileProviderOperations {
return self.writeContents(path: filePath, contents: data ?? Data(), completionHandler: completionHandler)
}
public func moveItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public func moveItem(path: String, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return doOperation(.move(source: path, destination: toPath), completionHandler: completionHandler)
}
public func copyItem(path: String, to toPath: String, overwrite: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public func copyItem(path: String, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return doOperation(.copy(source: path, destination: toPath), completionHandler: completionHandler)
}
@@ -174,17 +174,12 @@ extension DropboxFileProvider: FileProviderOperations {
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.copy(source: localFile.absoluteString, destination: toPath)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
guard let data = try? Data(contentsOf: localFile) else {
let error = throwError(localFile.absoluteString, code: URLError.fileDoesNotExist as FoundationErrorEnum)
completionHandler?(error)
return nil
}
return upload_simple(toPath, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
return upload_simple(toPath, localFile: localFile, overwrite: overwrite, operation: opType, completionHandler: completionHandler)
}
public func copyItem(path: String, toLocalURL destURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
@@ -196,13 +191,14 @@ extension DropboxFileProvider: FileProviderOperations {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)! as NSString]
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let requestDictionary = ["path": path]
let requestJson = dictionaryToJSON(requestDictionary as [String : AnyObject]) ?? ""
request.setValue(requestJson, forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.downloadTask(with: request, completionHandler: { (cacheURL, response, error) in
guard let cacheURL = cacheURL, let httpResponse = response as? HTTPURLResponse , httpResponse.statusCode < 300 else {
let code = FileProviderHTTPErrorCode(rawValue: (response as? HTTPURLResponse)?.statusCode ?? -1)
let dbError: FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: nil) : nil
let errorData : Data? = nil //Data(contentsOf:cacheURL) // TODO: Figure out how to get error response data for the error description
let dbError : FileProviderDropboxError? = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: errorData ?? Data(), encoding: .utf8)) : nil
completionHandler?(dbError ?? error)
return
}
@@ -220,10 +216,6 @@ extension DropboxFileProvider: FileProviderOperations {
}
extension DropboxFileProvider: FileProviderReadWrite {
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
}
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
let url = URL(string: "https://content.dropboxapi.com/2/files/download")!
@@ -250,13 +242,13 @@ extension DropboxFileProvider: FileProviderReadWrite {
return RemoteOperationHandle(operationType: opType, tasks: [task])
}
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
}
// FIXME: remove 150MB restriction
return upload_simple(path, data: data, overwrite: true, operation: opType, completionHandler: completionHandler)
return upload_simple(path, data: data, overwrite: overwrite, operation: opType, completionHandler: completionHandler)
}
public func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void)) {
@@ -348,32 +340,43 @@ extension DropboxFileProvider: ExtendedFileProvider {
switch (path as NSString).pathExtension.lowercased() {
case "jpg", "jpeg", "gif", "bmp", "png", "tif", "tiff":
return true
/*case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
return true
case "ppt", "pps", "ppsx", "ppsm", "pptx", "pptm":
return true
case "rtf":
return true*/
return true
default:
return false
}
}
public func propertiesOfFileSupported(path: String) -> Bool {
return false
let fileExt = (path as NSString).pathExtension.lowercased()
switch fileExt {
case "jpg", "jpeg", "bmp", "gif", "png", "tif", "tiff":
return true
/*case "mp3", "aac", "m4a":
return true*/
case "mp4", "mpg", "3gp", "mov", "avi":
return true
default:
return false
}
}
public func thumbnailOfFile(path: String, dimension: CGSize, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) {
/// Default value for dimension is 64x64, according to Dropbox documentation
public func thumbnailOfFile(path: String, dimension: CGSize?, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) {
let url: URL
switch (path as NSString).pathExtension.lowercased() {
case "jpg", "jpeg", "gif", "bmp", "png", "tif", "tiff":
url = URL(string: "https://content.dropboxapi.com/2/files/get_thumbnail")!
/*case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
case "doc", "docx", "docm", "xls", "xlsx", "xlsm":
fallthrough
case "ppt", "pps", "ppsx", "ppsm", "pptx", "pptm":
fallthrough
case "rtf":
url = NSURL(string: "https://content.dropboxapi.com/2/files/get_preview")!*/
url = URL(string: "https://content.dropboxapi.com/2/files/get_preview")!
default:
return
}
@@ -381,7 +384,9 @@ extension DropboxFileProvider: ExtendedFileProvider {
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
var requestDictionary = ["path": path as NSString]
requestDictionary["format"] = "jpeg" as NSString
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
if let dimension = dimension {
requestDictionary["size"] = "w\(Int(dimension.width))h\(Int(dimension.height))" as NSString
}
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
let task = self.session.dataTask(with: request, completionHandler: { (data, response, error) in
var image: ImageClass? = nil
@@ -391,7 +396,13 @@ extension DropboxFileProvider: ExtendedFileProvider {
}
}
if let data = data {
image = ImageClass(data: data)
if DropboxFileProvider.dataIsPDF(data) {
image = DropboxFileProvider.convertToImage(pdfData: data)
} else if let contentType = (response as? HTTPURLResponse)?.allHeaderFields["Content-Type"] as? String, contentType.contains("text/html") {
// TODO: Implement converting html returned type of get_preview to image
} else {
image = ImageClass(data: data)
}
}
completionHandler(image, error)
})
@@ -399,7 +410,27 @@ extension DropboxFileProvider: ExtendedFileProvider {
}
public func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String : Any], _ keys: [String], _ error: Error?) -> Void)) {
NotImplemented()
let url = URL(string: "https://api.dropboxapi.com/2/files/get_metadata")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let requestDictionary = ["path": correctPath(path)! as NSString, "include_media_info": NSNumber(value: true)]
request.httpBody = dictionaryToJSON(requestDictionary)?.data(using: .utf8)
let task = session.dataTask(with: request, completionHandler: { (data, response, error) in
var dbError: FileProviderDropboxError?
var dic = [String: Any]()
var keys = [String]()
if let response = response as? HTTPURLResponse {
let code = FileProviderHTTPErrorCode(rawValue: response.statusCode)
dbError = code != nil ? FileProviderDropboxError(code: code!, path: path, errorDescription: String(data: data ?? Data(), encoding: .utf8)) : nil
if let data = data, let jsonStr = String(data: data, encoding: .utf8), let json = jsonToDictionary(jsonStr), let properties = json["media_info"] as? [String: Any] {
(dic, keys) = self.mapMediaInfo(properties)
}
}
completionHandler(dic, keys, dbError ?? error)
})
task.resume()
}
}
+61 -7
View File
@@ -99,19 +99,17 @@ internal extension DropboxFileProvider {
func upload_simple(_ targetPath: String, data: Data, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
assert(data.count < 150*1024*1024, "Maximum size of allowed size to upload is 150MB")
var requestDictionary = [String: AnyObject]()
var requestDictionary = [String: Any]()
let url: URL
url = URL(string: "https://content.dropboxapi.com/2/files/upload")!
requestDictionary["path"] = correctPath(targetPath) as NSString?
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ssz"
requestDictionary["client_modified"] = dateFormatter.string(from: modifiedDate) as NSString
requestDictionary["client_modified"] = string(from:modifiedDate)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(dictionaryToJSON(requestDictionary), forHTTPHeaderField: "Dropbox-API-Arg")
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
request.httpBody = data
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
var responseError: FileProviderDropboxError?
@@ -120,7 +118,32 @@ internal extension DropboxFileProvider {
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
})
})
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(operationType: operation, tasks: [task])
}
func upload_simple(_ targetPath: String, localFile: URL, modifiedDate: Date = Date(), overwrite: Bool, operation: FileOperationType, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
var requestDictionary = [String: Any]()
let url: URL
url = URL(string: "https://content.dropboxapi.com/2/files/upload")!
requestDictionary["path"] = correctPath(targetPath) as NSString?
requestDictionary["mode"] = (overwrite ? "overwrite" : "add") as NSString
requestDictionary["client_modified"] = string(from:modifiedDate)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("Bearer \(credential?.password ?? "")", forHTTPHeaderField: "Authorization")
request.setValue("application/octet-stream", forHTTPHeaderField: "Content-Type")
request.setValue(dictionaryToJSON(requestDictionary as [String : AnyObject]), forHTTPHeaderField: "Dropbox-API-Arg")
let task = session.uploadTask(with: request, fromFile: localFile, completionHandler: { (data, response, error) in
var responseError: FileProviderDropboxError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
responseError = FileProviderDropboxError(code: rCode, path: targetPath, errorDescription: String(data: data ?? Data(), encoding: .utf8))
}
completionHandler?(responseError ?? error)
self.delegateNotify(.create(path: targetPath), error: responseError ?? error)
})
task.taskDescription = operation.json
task.resume()
return RemoteOperationHandle(operationType: operation, tasks: [task])
@@ -180,13 +203,44 @@ internal extension DropboxFileProvider {
fileObject.size = (json["size"] as? NSNumber)?.int64Value ?? -1
fileObject.serverTime = resolve(dateString: json["server_modified"] as? String ?? "")
fileObject.modifiedDate = resolve(dateString: json["client_modified"] as? String ?? "")
fileObject.fileType = (json[".tag"] as? String) == "folder" ? .directory : .regular
fileObject.type = (json[".tag"] as? String) == "folder" ? .directory : .regular
fileObject.isReadOnly = (json["sharing_info"]?["read_only"] as? NSNumber)?.boolValue ?? false
fileObject.id = json["id"] as? String
fileObject.rev = json["id"] as? String
return fileObject
}
static let dateFormatter = DateFormatter()
static let decimalFormatter = NumberFormatter()
func mapMediaInfo(_ json: [String: Any]) -> (dictionary: [String: Any], keys: [String]) {
var dic = [String: Any]()
var keys = [String]()
if let dimensions = json["dimensions"] as? [String: Any], let height = dimensions["height"] as? UInt64, let width = dimensions["width"] as? UInt64 {
keys.append("Dimensions")
dic["Dimensions"] = "\(width)x\(height)"
}
if let location = json["location"] as? [String: Any], let latitude = location["latitude"] as? Double, let longitude = location["longitude"] as? Double {
DropboxFileProvider.decimalFormatter.numberStyle = .decimal
DropboxFileProvider.decimalFormatter.maximumFractionDigits = 5
keys.append("Location")
let latStr = DropboxFileProvider.decimalFormatter.string(from: NSNumber(value: latitude))
let longStr = DropboxFileProvider.decimalFormatter.string(from: NSNumber(value: longitude))
dic["Location"] = "\(latStr), \(longStr)"
}
if let timeTakenStr = json["time_taken"] as? String, let timeTaken = self.resolve(dateString: timeTakenStr) {
keys.append("Date taken")
DropboxFileProvider.dateFormatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
dic["Date taken"] = DropboxFileProvider.dateFormatter.string(from: timeTaken)
}
if let duration = json["duration"] as? UInt64 {
keys.append("Duration")
dic["Duration"] = DropboxFileProvider.formatshort(interval: TimeInterval(duration))
}
return (dic, keys)
}
func delegateNotify(_ operation: FileOperationType, error: Error?) {
DispatchQueue.main.async(execute: {
if error == nil {
+444
View File
@@ -0,0 +1,444 @@
//
// ExtendedLocalFileProvider.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2017 Mousavian. Distributed under MIT license.
//
import Foundation
import ImageIO
import CoreGraphics
import AVFoundation
import MediaPlayer
#if os(iOS) || os(tvOS)
import UIKit
#elseif os(OSX)
import Cocoa
#endif
extension LocalFileProvider: ExtendedFileProvider {
public func thumbnailOfFileSupported(path: String) -> Bool {
switch (path as NSString).pathExtension.lowercased() {
case LocalFileInformationGenerator.imageThumbnailExtensions:
return true
case LocalFileInformationGenerator.audioThumbnailExtensions:
return true
case LocalFileInformationGenerator.videoThumbnailExtensions:
return true
case LocalFileInformationGenerator.pdfThumbnailExtensions:
return true
case LocalFileInformationGenerator.officeThumbnailExtensions:
return true
case LocalFileInformationGenerator.customThumbnailExtensions:
return true
default:
return false
}
}
public func propertiesOfFileSupported(path: String) -> Bool {
let fileExt = (path as NSString).pathExtension.lowercased()
switch fileExt {
case LocalFileInformationGenerator.imagePropertiesExtensions:
return LocalFileInformationGenerator.imageProperties != nil
case LocalFileInformationGenerator.audioPropertiesExtensions:
return LocalFileInformationGenerator.audioProperties != nil
case LocalFileInformationGenerator.videoPropertiesExtensions:
return LocalFileInformationGenerator.videoProperties != nil
case LocalFileInformationGenerator.pdfPropertiesExtensions:
return LocalFileInformationGenerator.pdfProperties != nil
case LocalFileInformationGenerator.archivePropertiesExtensions:
return LocalFileInformationGenerator.archiveProperties != nil
case LocalFileInformationGenerator.officePropertiesExtensions:
return LocalFileInformationGenerator.officeProperties != nil
case LocalFileInformationGenerator.customPropertiesExtensions:
return LocalFileInformationGenerator.customProperties != nil
default:
return false
}
}
public func thumbnailOfFile(path: String, dimension: CGSize? = nil, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) {
(dispatch_queue).async {
var thumbnailImage: ImageClass? = nil
// Check cache
let fileURL = self.absoluteURL(path)
// Create Thumbnail and cache
switch fileURL.pathExtension.lowercased() {
case LocalFileInformationGenerator.videoThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.videoThumbnail(fileURL)
case LocalFileInformationGenerator.audioThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.audioThumbnail(fileURL)
case LocalFileInformationGenerator.imageThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.imageThumbnail(fileURL)
case LocalFileInformationGenerator.pdfThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.pdfThumbnail(fileURL)
case LocalFileInformationGenerator.officeThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.officeThumbnail(fileURL)
case LocalFileInformationGenerator.customThumbnailExtensions:
thumbnailImage = LocalFileInformationGenerator.customThumbnail(fileURL)
default:
completionHandler(nil, nil)
return
}
if let image = thumbnailImage {
let scaledImage = dimension != nil ? LocalFileProvider.scaleDown(image: image, toSize: dimension!) : image
completionHandler(scaledImage, nil)
}
}
}
public func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String: Any], _ keys: [String], _ error: Error?) -> Void)) {
(dispatch_queue).async {
let fileExt = (path as NSString).pathExtension.lowercased()
var getter: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))?
switch fileExt {
case LocalFileInformationGenerator.imagePropertiesExtensions:
getter = LocalFileInformationGenerator.imageProperties
case LocalFileInformationGenerator.audioPropertiesExtensions:
getter = LocalFileInformationGenerator.audioProperties
case LocalFileInformationGenerator.videoPropertiesExtensions:
getter = LocalFileInformationGenerator.videoProperties
case LocalFileInformationGenerator.pdfPropertiesExtensions:
getter = LocalFileInformationGenerator.pdfProperties
case LocalFileInformationGenerator.archivePropertiesExtensions:
getter = LocalFileInformationGenerator.archiveProperties
case LocalFileInformationGenerator.officePropertiesExtensions:
getter = LocalFileInformationGenerator.officeProperties
case LocalFileInformationGenerator.customPropertiesExtensions:
getter = LocalFileInformationGenerator.customProperties
default:
break
}
var dic = [String: Any]()
var keys = [String]()
if let getterMethod = getter {
(dic, keys) = getterMethod(self.absoluteURL(path))
}
completionHandler(dic, keys, nil)
}
}
}
public struct LocalFileInformationGenerator {
static public var imageThumbnailExtensions: [String] = ["jpg", "jpeg", "gif", "bmp", "png", "tif", "tiff", "ico"]
static public var audioThumbnailExtensions: [String] = ["mp3", "aac", "m4a"]
static public var videoThumbnailExtensions: [String] = ["mov", "mp4", "m4v", "mpg", "mpeg"]
static public var pdfThumbnailExtensions: [String] = ["pdf"]
static public var officeThumbnailExtensions: [String] = []
static public var customThumbnailExtensions: [String] = []
static public var imagePropertiesExtensions: [String] = ["jpg", "jpeg", "bmp", "gif", "png", "tif", "tiff"]
static public var audioPropertiesExtensions: [String] = ["mp3", "aac", "m4a", "caf"]
static public var videoPropertiesExtensions: [String] = ["mp4", "mpg", "3gp", "mov", "avi"]
static public var pdfPropertiesExtensions: [String] = ["pdf"]
static public var archivePropertiesExtensions: [String] = []
static public var officePropertiesExtensions: [String] = []
static public var customPropertiesExtensions: [String] = []
static public var imageThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
return ImageClass(contentsOfFile: fileURL.path)
}
static public var audioThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
let playerItem = AVPlayerItem(url: fileURL)
let metadataList = playerItem.asset.commonMetadata
for item in metadataList {
if item.commonKey == AVMetadataCommonKeyArtwork {
if let data = item.dataValue {
return ImageClass(data: data)
}
}
}
return nil
}
static public var videoThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
let asset = AVAsset(url: fileURL)
let assetImgGenerate = AVAssetImageGenerator(asset: asset)
assetImgGenerate.appliesPreferredTrackTransform = true
let time = CMTimeMake(asset.duration.value / 3, asset.duration.timescale)
if let cgImage = try? assetImgGenerate.copyCGImage(at: time, actualTime: nil) {
#if os(OSX)
return ImageClass(cgImage: cgImage, size: NSSize.zero)
#else
return ImageClass(cgImage: cgImage)
#endif
}
return nil
}
static public var pdfThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
guard let data = try? Data(contentsOf: fileURL) else { return nil }
return LocalFileProvider.convertToImage(pdfData: data)
}
static public var officeThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
return nil
}
static public var customThumbnail: (_ fileURL: URL) -> ImageClass? = { fileURL in
return nil
}
static public var imageProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = { fileURL in
func simplify(_ top:Int64, _ bottom:Int64) -> (newTop:Int, newBottom:Int) {
var x = top
var y = bottom
while (y != 0) {
let buffer = y
y = x % y
x = buffer
}
let hcfVal = x
let newTopVal = top/hcfVal
let newBottomVal = bottom/hcfVal
return(Int(newTopVal), Int(newBottomVal))
}
var dic = [String: Any]()
var keys = [String]()
guard let cgDataRef = CGImageSourceCreateWithURL(fileURL as CFURL, nil), let cfImageDict = CGImageSourceCopyPropertiesAtIndex(cgDataRef, 0, nil) else {
return (dic, keys)
}
let imageDict = cfImageDict as NSDictionary
let tiffDict = imageDict[kCGImagePropertyTIFFDictionary as String] as? [String : AnyObject] ?? [:]
let exifDict = imageDict[kCGImagePropertyExifDictionary as String] as? [String : AnyObject] ?? [:]
if let pixelWidth: AnyObject = imageDict.object(forKey: kCGImagePropertyPixelWidth) as? NSNumber, let pixelHeight: AnyObject = imageDict.object(forKey: kCGImagePropertyPixelHeight) as? NSNumber {
keys.append("Dimensions")
dic["Dimensions"] = "\(pixelWidth)x\(pixelHeight)"
}
if let dpi = imageDict[kCGImagePropertyDPIWidth as String] {
keys.append("DPI")
dic["DPI"] = dpi
}
if let devicemake = tiffDict[kCGImagePropertyTIFFMake as String] {
keys.append("Device make")
dic["Device make"] = devicemake
}
if let devicemodel = tiffDict[kCGImagePropertyTIFFModel as String] {
keys.append("Device model")
dic["Device model"] = devicemodel
}
if let lensmodel = exifDict[kCGImagePropertyExifLensModel as String] {
keys.append("Lens model")
dic["Lens model"] = lensmodel
}
if let artist = tiffDict[kCGImagePropertyTIFFArtist as String] as? String , !artist.isEmpty {
keys.append("Artist")
dic["Artist"] = artist
}
if let cr = tiffDict[kCGImagePropertyTIFFCopyright as String] as? String , !cr.isEmpty {
keys.append("Copyright")
dic["Copyright"] = cr
}
if let date = tiffDict[kCGImagePropertyTIFFDateTime as String] as? String , !date.isEmpty {
keys.append("Date taken")
dic["Date taken"] = date
}
if let latitude = tiffDict[kCGImagePropertyGPSLatitude as String]?.doubleValue, let longitude = tiffDict[kCGImagePropertyGPSLongitude as String]?.doubleValue {
keys.append("Location")
dic["Location"] = "\(latitude), \(longitude)"
}
if let colorspace = imageDict[kCGImagePropertyColorModel as String] {
keys.append("Color space")
dic["Color space"] = colorspace
}
if let focallen = exifDict[kCGImagePropertyExifFocalLength as String] {
keys.append("Focal length")
dic["Focal length"] = focallen
}
if let fnum = exifDict[kCGImagePropertyExifFNumber as String] {
keys.append("F number")
dic["F number"] = fnum
}
if let expprog = exifDict[kCGImagePropertyExifExposureProgram as String] {
keys.append("Exposure program")
dic["Exposure program"] = expprog
}
if let exp = exifDict[kCGImagePropertyExifExposureTime as String]?.doubleValue {
let expfrac = simplify(Int64(exp * 10_000_000_000_000), 10_000_000_000_000)
keys.append("Exposure time")
dic["Exposure time"] = "\(expfrac.newTop)/\(expfrac.newBottom)"
}
if let iso = exifDict[kCGImagePropertyExifISOSpeedRatings as String] as? NSArray , iso.count > 0 {
keys.append("ISO speed")
dic["ISO speed"] = iso[0]
}
return (dic, keys)
}
static var audioProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = { fileURL in
func makeDescription(_ key: String?) -> String? {
guard let key = key else {
return nil
}
guard let regex = try? NSRegularExpression(pattern: "([a-z])([A-Z])" , options: NSRegularExpression.Options()) else {
return nil
}
let newKey = regex.stringByReplacingMatches(in: key, options: NSRegularExpression.MatchingOptions(), range: NSMakeRange(0, (key as NSString).length) , withTemplate: "$1 $2")
return newKey.capitalized
}
var dic = [String: Any]()
var keys = [String]()
if FileManager.default.fileExists(atPath: fileURL.path) {
let playerItem = AVPlayerItem(url: fileURL)
let metadataList = playerItem.asset.commonMetadata
for item in metadataList {
if let description = makeDescription(item.commonKey) {
if let value = item.stringValue {
keys.append(description)
dic[description] = value
}
}
}
if let ap = try? AVAudioPlayer(contentsOf: fileURL) {
keys.append("Duration")
dic["Duration"] = LocalFileProvider.formatshort(interval: ap.duration)
if let bitRate = ap.settings[AVSampleRateKey] as? Int {
keys.append("Bitrate")
dic["Bitrate"] = bitRate
}
}
}
return (dic, keys)
}
static public var videoProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = { fileURL in
var dic = [String: Any]()
var keys = [String]()
if let audioprops = LocalFileInformationGenerator.audioProperties?(fileURL) {
dic = audioprops.prop
keys = audioprops.keys
dic.removeValue(forKey: "Duration")
if let index = keys.index(of: "Duration") {
keys.remove(at: index)
}
}
let asset = AVURLAsset(url: fileURL, options: nil)
let videoTracks = asset.tracks(withMediaType: AVMediaTypeVideo)
if videoTracks.count > 0 {
var bitrate: Float = 0
let width = Int(videoTracks[0].naturalSize.width)
let height = Int(videoTracks[0].naturalSize.height)
keys.append("Dimensions")
dic["Dimensions"] = "\(width)x\(height)"
var duration: Int64 = 0
for track in videoTracks {
duration += track.timeRange.duration.timescale > 0 ? track.timeRange.duration.value / Int64(track.timeRange.duration.timescale) : 0
bitrate += track.estimatedDataRate
}
keys.append("Duration")
dic["Duration"] = LocalFileProvider.formatshort(interval: TimeInterval(duration))
keys.append("Video Bitrate")
dic["Video Bitrate"] = "\(Int(ceil(bitrate / 1000))) kbps"
}
let audioTracks = asset.tracks(withMediaType: AVMediaTypeAudio)
// dic["Audio channels"] = audioTracks.count
var bitrate: Float = 0
for track in audioTracks {
bitrate += track.estimatedDataRate
}
keys.append("Audio Bitrate")
dic["Audio Bitrate"] = "\(Int(ceil(bitrate / 1000))) kbps"
return (dic, keys)
}
static public var pdfProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = { fileURL in
func getKey(_ key: String, from dict: CGPDFDictionaryRef) -> String? {
var cfValue: CGPDFStringRef? = nil
if (CGPDFDictionaryGetString(dict, key, &cfValue)), let value = CGPDFStringCopyTextString(cfValue!) {
return value as String
}
return nil
}
func convertDate(_ date: String) -> Date? {
var dateStr = date
if dateStr.hasPrefix("D:") {
dateStr = date.substring(from: date.characters.index(date.startIndex, offsetBy: 2))
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMddHHmmssTZD"
if let result = dateFormatter.date(from: dateStr) {
return result
}
dateFormatter.dateFormat = "yyyyMMddHHmmss"
if let result = dateFormatter.date(from: dateStr) {
return result
}
return nil
}
var dic = [String: Any]()
var keys = [String]()
if let data = try? Data(contentsOf: fileURL), let provider = CGDataProvider(data: data as CFData), let reference = CGPDFDocument(provider), let dict = reference.info {
if let title = getKey("Title", from: dict), !title.isEmpty {
keys.append("Title")
dic["Title"] = title
}
if let author = getKey("Author", from: dict), !author.isEmpty {
keys.append("Author")
dic["Author"] = author
}
if let subject = getKey("Subject", from: dict), !subject.isEmpty {
keys.append("Subject")
dic["Subject"] = subject
}
var majorVersion: Int32 = 0
var minorVersion: Int32 = 0
reference.getVersion(majorVersion: &majorVersion, minorVersion: &minorVersion)
if majorVersion > 0 {
keys.append("Version")
dic["Version"] = String(majorVersion) + "." + String(minorVersion)
}
if reference.numberOfPages > 0 {
keys.append("Pages")
dic["Pages"] = reference.numberOfPages
}
if reference.numberOfPages > 0, let pageRef = reference.page(at: 1) {
let size = pageRef.getBoxRect(CGPDFBox.mediaBox).size
keys.append("Resolution")
dic["Resolution"] = "\(Int(size.width))x\(Int(size.height))"
}
if let creator = getKey("Creator", from: dict), !creator.isEmpty {
keys.append("Content creator")
dic["Content creator"] = creator
}
if let creationDateString = getKey("CreationDate", from: dict), let creationDate = convertDate(creationDateString) {
keys.append("Creation date")
dic["Creation date"] = creationDate
}
if let modifiedDateString = getKey("ModDate", from: dict), let modDate = convertDate(modifiedDateString) {
keys.append("Modified date")
dic["Modified date"] = modDate
}
keys.append("Security")
dic["Security"] = reference.isEncrypted ? "Present" : "None"
keys.append("Allows printing")
dic["Allows printing"] = reference.allowsPrinting ? "Yes" : "No"
keys.append("Allows copying")
dic["Allows copying"] = reference.allowsCopying ? "Yes" : "No"
}
return (dic, keys)
}
static public var archiveProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = nil
static public var officeProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = nil
static public var customProperties: ((_ fileURL: URL) -> (prop: [String: Any], keys: [String]))? = nil
}
func ~=<T : Equatable>(array: [T], value: T) -> Bool {
return array.contains(value)
}
+8 -22
View File
@@ -401,8 +401,8 @@ open class FPSStreamTask: URLSessionTask, StreamDelegate {
if #available(iOS 9.0, OSX 10.11, *) {
_underlyingTask!.startSecureConnection()
} else {
inputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
outputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
inputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: .socketSecurityLevelKey)
outputStream!.setProperty(StreamSocketSecurityLevel.negotiatedSSL.rawValue, forKey: .socketSecurityLevelKey)
}
}
@@ -414,8 +414,8 @@ open class FPSStreamTask: URLSessionTask, StreamDelegate {
if #available(iOS 9.0, OSX 10.11, *) {
_underlyingTask!.stopSecureConnection()
} else {
inputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
outputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: Stream.PropertyKey.socketSecurityLevelKey)
inputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: .socketSecurityLevelKey)
outputStream!.setProperty(StreamSocketSecurityLevel.none.rawValue, forKey: .socketSecurityLevelKey)
}
}
@@ -503,21 +503,7 @@ public protocol FPSStreamDelegate : URLSessionTaskDelegate {
@objc optional func urlSession(_ session: URLSession, streamTask: FPSStreamTask, didBecome inputStream: InputStream, outputStream: OutputStream)
}
private let ports = ["http": 80,
"https": 443,
"smb": 445,
"ftp": 21,
"sftp": 22,
"sftp": 2121,
"telnet": 23,
"pop": 110,
"smtp": 25,
"imap": 143]
private let securePorts = ["https": 443,
"smb": 445,
"sftp": 22,
"sftp": 2121,
"telnet": 992,
"pop": 995,
"smtp": 465,
"imap": 993]
private let ports: [String: Int] = ["http": 80, "https": 443, "smb": 445,"ftp": 21,"ftps": 22, "sftp": 2121,
"telnet": 23, "pop": 110, "smtp": 25, "imap": 143]
private let securePorts: [String: Int] = ["https": 443, "smb": 445, "ftps": 22, "sftp": 2121,
"telnet": 992, "pop": 995, "smtp": 465, "imap": 993]
+261
View File
@@ -0,0 +1,261 @@
//
// FileProvider.swift
// FileProvider
//
// Created by Amir Abbas Mousavian.
// Copyright © 2016 Mousavian. Distributed under MIT license.
//
import Foundation
/// Containts path and attributes of a file or resource.
open class FileObject {
open internal(set) var allValues: [String: Any]
internal init(allValues: [String: Any]) {
self.allValues = allValues
}
internal init(absoluteURL: URL? = nil, name: String, path: String) {
self.allValues = [String: Any]()
self.absoluteURL = absoluteURL
self.name = name
self.path = path
}
/// url to access the resource, not supported by Dropbox provider
open internal(set) var absoluteURL: URL? {
get {
return allValues["NSURLAbsoluteURLKey"] as? URL
}
set {
allValues["NSURLAbsoluteURLKey"] = 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
}
set {
allValues[URLResourceKey.nameKey.rawValue] = newValue
}
}
/// Relative path of file object
open internal(set) var path: String {
get {
return allValues[URLResourceKey.pathKey.rawValue] as! String
}
set {
allValues[URLResourceKey.pathKey.rawValue] = 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
}
set {
allValues[URLResourceKey.fileSizeKey.rawValue] = 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
}
set {
allValues[URLResourceKey.creationDateKey.rawValue] = 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
}
set {
allValues[URLResourceKey.contentModificationDateKey.rawValue] = 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
}
set {
allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue
}
}
@available(*, deprecated, message: "Use FileObject.type property instead.")
open var fileType: URLFileResourceType? {
return self.type
}
/// File is hidden either because begining with dot or filesystem flags
/// 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
}
set {
allValues[URLResourceKey.isHiddenKey.rawValue] = newValue
}
}
/// File can not be written
open internal(set) var isReadOnly: Bool {
get {
return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true)
}
set {
allValues[URLResourceKey.isWritableKey.rawValue] = !newValue
}
}
/// File is a Directory
open var isDirectory: Bool {
return self.type == .directory
}
/// File is a normal file
open var isRegularFile: Bool {
return self.type == .regular
}
/// File is a Symbolic link
open var isSymLink: Bool {
return self.type == .symbolicLink
}
}
/// Sorting FileObject array by given criteria, not thread-safe
public struct FileObjectSorting {
/// Determines sort kind by which item of File object
public enum SortType {
/// Sorting by default Finder (case-insensitive) behavior
case name
/// Sorting by case-sensitive form of file name
case nameCaseSensitive
/// Sorting by case-in sensitive form of file name
case nameCaseInsensitive
/// Sorting by file type
case `extension`
/// Sorting by file modified date
case modifiedDate
/// Sorting by file creation date
case creationDate
/// Sorting by file modified date
case size
/// all sort types
static var allItems: [SortType] {
return [.name, .nameCaseSensitive, .nameCaseInsensitive, .extension,
.modifiedDate,.creationDate, .size]
}
}
public let sortType: SortType
/// puts A before Z, default is true
public let ascending: Bool
/// puts directories on top, regardless of other attributes, default is false
public let isDirectoriesFirst: Bool
public static let nameAscending = FileObjectSorting(type: .name, ascending: true)
public static let nameDesceding = FileObjectSorting(type: .name, ascending: false)
public static let sizeAscending = FileObjectSorting(type: .size, ascending: true)
public static let sizeDesceding = FileObjectSorting(type: .size, ascending: false)
public static let extensionAscending = FileObjectSorting(type: .extension, ascending: true)
public static let extensionDesceding = FileObjectSorting(type: .extension, ascending: false)
public static let modifiedAscending = FileObjectSorting(type: .modifiedDate, ascending: true)
public static let modifiedDesceding = FileObjectSorting(type: .modifiedDate, ascending: false)
public static let createdAscending = FileObjectSorting(type: .creationDate, ascending: true)
public static let createdDesceding = FileObjectSorting(type: .creationDate, ascending: false)
public init (type: SortType, ascending: Bool = true, isDirectoriesFirst: Bool = false) {
self.sortType = type
self.ascending = ascending
self.isDirectoriesFirst = isDirectoriesFirst
}
/// Sorts array of FileObjects by criterias set in properties
public func sort(_ files: [FileObject]) -> [FileObject] {
return files.sorted {
if isDirectoriesFirst {
if ($0.isDirectory) && !($1.isDirectory) {
return true
}
if !($0.isDirectory) && ($1.isDirectory) {
return false
}
}
switch sortType {
case .name:
return ($0.name).localizedStandardCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
case .nameCaseSensitive:
return ($0.name).localizedCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
case .nameCaseInsensitive:
return ($0.name).localizedCaseInsensitiveCompare($1.name) == (ascending ? .orderedAscending : .orderedDescending)
case .extension:
let kind1 = $0.isDirectory ? "folder" : ($0.path as NSString).pathExtension
let kind2 = $1.isDirectory ? "folder" : ($1.path as NSString).pathExtension
return kind1.localizedCaseInsensitiveCompare(kind2) == (ascending ? .orderedAscending : .orderedDescending)
case .modifiedDate:
let fileMod1 = $0.modifiedDate ?? Date.distantPast
let fileMod2 = $1.modifiedDate ?? Date.distantPast
return ascending ? fileMod1 < fileMod2 : fileMod1 > fileMod2
case .creationDate:
let fileCreation1 = $0.creationDate ?? Date.distantPast
let fileCreation2 = $1.creationDate ?? Date.distantPast
return ascending ? fileCreation1 < fileCreation2 : fileCreation1 > fileCreation2
case .size:
return ascending ? $0.size < $1.size : $0.size > $1.size
}
}
}
}
extension URLFileResourceType {
public init(fileTypeValue: FileAttributeType) {
switch fileTypeValue {
case FileAttributeType.typeCharacterSpecial: self = .characterSpecial
case FileAttributeType.typeDirectory: self = .directory
case FileAttributeType.typeBlockSpecial: self = .blockSpecial
case FileAttributeType.typeRegular: self = .regular
case FileAttributeType.typeSymbolicLink: self = .symbolicLink
case FileAttributeType.typeSocket: self = .socket
case FileAttributeType.typeUnknown: self = .unknown
default: self = .unknown
}
}
}
internal extension URL {
var uw_scheme: String {
return self.scheme ?? ""
}
}
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
guard let data = jsonString.data(using: .utf8) else {
return nil
}
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
return dic
}
return nil
}
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
return String(data: data, encoding: .utf8)
}
return nil
}
+3
View File
@@ -5,6 +5,9 @@
// Created by Amir Abbas Mousavian on 5/6/95.
//
//
#import <TargetConditionals.h>
#if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR
#import <UIKit/UIKit.h>
//! Project version number for FileProvider iOS.
+188 -153
View File
@@ -98,29 +98,80 @@ public protocol FileProviderOperations: FileProviderBasic {
@discardableResult
func create(file: String, at: String, contents data: Data?, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func moveItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func moveItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func copyItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func copyItem(path: String, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func removeItem(path: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func copyItem(localFile: URL, to: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func copyItem(path: String, toLocalURL: URL, completionHandler: SimpleCompletionHandler) -> OperationHandle?
}
extension FileProviderOperations {
@discardableResult
public func moveItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.moveItem(path: path, to: to, overwrite: false, completionHandler: completionHandler)
}
@discardableResult
public func copyItem(localFile: URL, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.copyItem(localFile: localFile, to: to, overwrite: false, completionHandler: completionHandler)
}
@discardableResult
public func copyItem(path: String, to: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.copyItem(path: path, to: to, overwrite: false, completionHandler: completionHandler)
}
}
public protocol FileProviderReadWrite: FileProviderBasic {
@discardableResult
func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
@discardableResult
func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?
@discardableResult
func writeContents(path: String, contents: Data, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func writeContents(path: String, contents: Data, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
@discardableResult
func writeContents(path: String, contents: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle?
func searchFiles(path: String, recursive: Bool, query: String, foundItemHandler: ((FileObject) -> Void)?, completionHandler: @escaping ((_ files: [FileObject], _ error: Error?) -> Void))
}
extension FileProviderReadWrite {
@discardableResult
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle?{
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
}
@discardableResult
public func writeContents(path: String, contents: Data, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.writeContents(path: path, contents: contents, atomically: false, overwrite: false, completionHandler: completionHandler)
}
@discardableResult
public func writeContents(path: String, contents: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.writeContents(path: path, contents: contents, atomically: atomically, overwrite: false, completionHandler: completionHandler)
}
@discardableResult
public func writeContents(path: String, contents: Data, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
return self.writeContents(path: path, contents: contents, atomically: false, overwrite: overwrite, completionHandler: completionHandler)
}
}
public protocol FileProviderMonitor: FileProviderBasic {
func registerNotifcation(path: String, eventHandler: @escaping (() -> Void))
func unregisterNotifcation(path: String)
@@ -168,7 +219,7 @@ extension FileProviderBasic {
guard let path = path else { return nil }
var p = path.hasPrefix("/") ? path : "/" + path
if p.hasSuffix("/") {
p.remove(at: p.endIndex)
p.remove(at: p.index(before:p.endIndex))
}
return p
}
@@ -246,15 +297,148 @@ extension FileProviderBasic {
}
return nil
}
public func string(from date:Date) -> String {
let fm = DateFormatter()
fm.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'"
fm.timeZone = TimeZone(identifier:"UTC")
fm.locale = Locale(identifier:"en_US_POSIX")
return fm.string(from:date)
}
}
public protocol ExtendedFileProvider: FileProvider {
public protocol ExtendedFileProvider: FileProviderBasic {
func thumbnailOfFileSupported(path: String) -> Bool
func propertiesOfFileSupported(path: String) -> Bool
func thumbnailOfFile(path: String, dimension: CGSize, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void))
func thumbnailOfFile(path: String, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void))
func thumbnailOfFile(path: String, dimension: CGSize?, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void))
func propertiesOfFile(path: String, completionHandler: @escaping ((_ propertiesDictionary: [String: Any], _ keys: [String], _ error: Error?) -> Void))
}
extension ExtendedFileProvider {
public func thumbnailOfFile(path: String, completionHandler: @escaping ((_ image: ImageClass?, _ error: Error?) -> Void)) {
self.thumbnailOfFile(path: path, dimension: nil, completionHandler: completionHandler)
}
internal static func formatshort(interval: TimeInterval) -> String {
var result = "0:00"
if interval < TimeInterval(Int32.max) {
result = ""
var time = DateComponents()
time.hour = Int(interval / 3600)
time.minute = Int((interval.truncatingRemainder(dividingBy: 3600)) / 60)
time.second = Int(interval.truncatingRemainder(dividingBy: 60))
let formatter = NumberFormatter()
formatter.paddingCharacter = "0"
formatter.minimumIntegerDigits = 2
formatter.maximumFractionDigits = 0
let formatterFirst = NumberFormatter()
formatterFirst.maximumFractionDigits = 0
if time.hour! > 0 {
result = "\(formatterFirst.string(from: NSNumber(value: time.hour!))!):\(formatter.string(from: NSNumber(value: time.minute!))!):\(formatter.string(from: NSNumber(value: time.second!))!)"
} else {
result = "\(formatterFirst.string(from: NSNumber(value: time.minute!))!):\(formatter.string(from: NSNumber(value: time.second!))!)"
}
}
result = result.trimmingCharacters(in: CharacterSet(charactersIn: ": "))
return result
}
internal static func dataIsPDF(_ data: Data) -> Bool {
return data.count > 4 && data.scanString(length: 4, encoding: .ascii) == "%PDF"
}
internal static func convertToImage(pdfData: Data?) -> ImageClass? {
guard let pdfData = pdfData else { return nil }
let cfPDFData: CFData = pdfData as CFData
if let provider = CGDataProvider(data: cfPDFData), let reference = CGPDFDocument(provider), let pageRef = reference.page(at: 1) {
let frame = pageRef.getBoxRect(CGPDFBox.mediaBox)
var size = frame.size
let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height)
#if os(OSX)
let ppp = Int(NSScreen.main()?.backingScaleFactor ?? 1) // fetch device is retina or not
size.width *= CGFloat(ppp)
size.height *= CGFloat(ppp)
let rep = NSBitmapImageRep(bitmapDataPlanes: nil, pixelsWide: Int(size.width), pixelsHigh: Int(size.height),
bitsPerSample: 8, samplesPerPixel: 4, hasAlpha: true, isPlanar: false, colorSpaceName: NSCalibratedRGBColorSpace,
bytesPerRow: 0, bitsPerPixel: 0)
guard let context = NSGraphicsContext(bitmapImageRep: rep!) else {
return nil
}
NSGraphicsContext.saveGraphicsState()
NSGraphicsContext.setCurrent(context)
let transform = pageRef.getDrawingTransform(CGPDFBox.mediaBox, rect: rect, rotate: 0, preserveAspectRatio: true)
context.cgContext.concatenate(transform)
context.cgContext.translateBy(x: 0, y: size.height)
context.cgContext.scaleBy(x: CGFloat(ppp), y: CGFloat(-ppp))
context.cgContext.drawPDFPage(pageRef)
let resultingImage = NSImage(size: size)
resultingImage.addRepresentation(rep!)
return resultingImage
#else
let ppp = Int(UIScreen.main.scale) // fetch device is retina or not
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
size.width *= CGFloat(ppp)
size.height *= CGFloat(ppp)
UIGraphicsBeginImageContext(size)
context.saveGState()
let transform = pageRef.getDrawingTransform(CGPDFBox.mediaBox, rect: rect, rotate: 0, preserveAspectRatio: true)
context.concatenate(transform)
context.translateBy(x: 0, y: size.height)
context.scaleBy(x: CGFloat(ppp), y: CGFloat(-ppp))
context.drawPDFPage(pageRef)
context.restoreGState()
let resultingImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return resultingImage
#endif
}
return nil
}
internal static func scaleDown(image: ImageClass, toSize maxSize: CGSize) -> ImageClass {
let height, width: CGFloat
if image.size.width > image.size.height {
width = maxSize.width
height = (image.size.height / image.size.width) * width
} else {
height = maxSize.height
width = (image.size.width / image.size.height) * height
}
let newSize = CGSize(width: width, height: height)
#if os(OSX)
var imageRect = NSRect(origin: CGPoint.zero, size: image.size)
let imageRef = image.cgImage(forProposedRect: &imageRect, context: nil, hints: nil)
// Create NSImage from the CGImage using the new size
return NSImage(cgImage: imageRef!, size: newSize)
#else
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
image.draw(in: CGRect(origin: CGPoint.zero, size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext() ?? image
UIGraphicsEndImageContext()
return newImage
#endif
}
}
public enum FileOperationType: CustomStringConvertible {
case create (path: String)
case copy (source: String, destination: String)
@@ -300,116 +484,6 @@ public enum FileOperationType: CustomStringConvertible {
}
}
open class FileObject {
open internal(set) var allValues: [String: Any]
internal init(allValues: [String: Any]) {
self.allValues = allValues
}
internal init(absoluteURL: URL? = nil, name: String, path: String) {
self.allValues = [String: Any]()
self.absoluteURL = absoluteURL
self.name = name
self.path = path
}
open internal(set) var absoluteURL: URL? {
get {
return allValues["NSURLAbsoluteURLKey"] as? URL
}
set {
allValues["NSURLAbsoluteURLKey"] = newValue
}
}
open internal(set) var name: String {
get {
return allValues[URLResourceKey.nameKey.rawValue] as! String
}
set {
allValues[URLResourceKey.nameKey.rawValue] = newValue
}
}
open internal(set) var path: String {
get {
return allValues[URLResourceKey.pathKey.rawValue] as! String
}
set {
allValues[URLResourceKey.pathKey.rawValue] = newValue
}
}
open internal(set) var size: Int64 {
get {
return allValues[URLResourceKey.fileSizeKey.rawValue] as? Int64 ?? -1
}
set {
allValues[URLResourceKey.fileSizeKey.rawValue] = Int(exactly: newValue) ?? Int.max
}
}
open internal(set) var creationDate: Date? {
get {
return allValues[URLResourceKey.creationDateKey.rawValue] as? Date
}
set {
allValues[URLResourceKey.creationDateKey.rawValue] = newValue
}
}
open internal(set) var modifiedDate: Date? {
get {
return allValues[URLResourceKey.contentModificationDateKey.rawValue] as? Date
}
set {
allValues[URLResourceKey.contentModificationDateKey.rawValue] = newValue
}
}
open internal(set) var fileType: URLFileResourceType? {
get {
guard let typeString = allValues[URLResourceKey.fileResourceTypeKey.rawValue] as? String else {
return nil
}
return URLFileResourceType(rawValue: typeString)
}
set {
allValues[URLResourceKey.fileResourceTypeKey.rawValue] = newValue
}
}
open internal(set) var isHidden: Bool {
get {
return allValues[URLResourceKey.isHiddenKey.rawValue] as? Bool ?? false
}
set {
allValues[URLResourceKey.isHiddenKey.rawValue] = newValue
}
}
open internal(set) var isReadOnly: Bool {
get {
return !(allValues[URLResourceKey.isWritableKey.rawValue] as? Bool ?? true)
}
set {
allValues[URLResourceKey.isWritableKey.rawValue] = !newValue
}
}
open var isDirectory: Bool {
return self.fileType == .directory
}
open var isRegularFile: Bool {
return self.fileType == .regular
}
open var isSymLink: Bool {
return self.fileType == .symbolicLink
}
}
public protocol OperationHandle {
var operationType: FileOperationType { get }
@@ -443,13 +517,6 @@ public protocol FileOperationDelegate: class {
func fileProvider(_ fileProvider: FileProviderOperations, shouldProceedAfterError error: Error, operation: FileOperationType) -> Bool
}
// THESE ARE METHODS TO PROVIDE COMPATIBILITY WITH SWIFT 2.3 SIMOULTANIOUSLY!
internal extension URL {
var uw_scheme: String {
return self.scheme ?? ""
}
}
internal class Weak<T: AnyObject> {
weak var value : T?
init (_ value: T) {
@@ -457,21 +524,6 @@ internal class Weak<T: AnyObject> {
}
}
extension URLFileResourceType {
public init(fileTypeValue: FileAttributeType) {
switch fileTypeValue {
case FileAttributeType.typeCharacterSpecial: self = .characterSpecial
case FileAttributeType.typeDirectory: self = .directory
case FileAttributeType.typeBlockSpecial: self = .blockSpecial
case FileAttributeType.typeRegular: self = .regular
case FileAttributeType.typeSymbolicLink: self = .symbolicLink
case FileAttributeType.typeSocket: self = .socket
case FileAttributeType.typeUnknown: self = .unknown
default: self = .unknown
}
}
}
public protocol FoundationErrorEnum {
init? (rawValue: Int)
var rawValue: Int { get }
@@ -479,20 +531,3 @@ public protocol FoundationErrorEnum {
extension URLError.Code: FoundationErrorEnum {}
extension CocoaError.Code: FoundationErrorEnum {}
internal func jsonToDictionary(_ jsonString: String) -> [String: AnyObject]? {
guard let data = jsonString.data(using: .utf8) else {
return nil
}
if let dic = try? JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions()) as? [String: AnyObject] {
return dic
}
return nil
}
internal func dictionaryToJSON(_ dictionary: [String: AnyObject]) -> String? {
if let data = try? JSONSerialization.data(withJSONObject: dictionary, options: JSONSerialization.WritingOptions()) {
return String(data: data, encoding: .utf8)
}
return nil
}
+13 -2
View File
@@ -182,7 +182,8 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
}
@discardableResult
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
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.async {
do {
@@ -232,6 +233,9 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
@discardableResult
open func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
if length < 0 {
return self.contents(path: path, completionHandler: completionHandler)
}
let opType = FileOperationType.fetch(path: path)
dispatch_queue.async {
let aPath = self.absoluteURL(path).path
@@ -255,8 +259,15 @@ open class LocalFileProvider: FileProvider, FileProviderMonitor {
}
@discardableResult
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
var options: Data.WritingOptions = []
if atomically {
options.insert(.atomic)
}
if overwrite {
options.insert(.withoutOverwriting)
}
operation_queue.async {
try? data.write(to: self.absoluteURL(path), options: atomically ? [.atomic] : [])
DispatchQueue.main.async(execute: {
+1 -1
View File
@@ -56,7 +56,7 @@ public final class LocalFileObject: FileObject {
internal class LocalFolderMonitor {
fileprivate let source: DispatchSourceFileSystemObject
fileprivate let descriptor: CInt
fileprivate let qq: DispatchQueue = DispatchQueue.global(qos: DispatchQoS.QoSClass.default)
fileprivate let qq: DispatchQueue = DispatchQueue.global(qos: .default)
fileprivate var state: Bool = false
fileprivate var monitoredTime: TimeInterval = Date().timeIntervalSinceReferenceDate
var url: URL
+5 -5
View File
@@ -201,11 +201,11 @@ public enum FileProviderHTTPErrorCode: Int {
case notExtended = 510
case networkAuthenticationRequired = 511
fileprivate static let status1xx = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
fileprivate static let status2xx = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
fileprivate static let status3xx = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
fileprivate static let status4xx = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
fileprivate static let status5xx = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
fileprivate static let status1xx: [Int: String] = [100: "Continue", 101: "Switching Protocols", 102: "Processing"]
fileprivate static let status2xx: [Int: String] = [200: "OK", 201: "Created", 202: "Accepted", 203: "Non-Authoritative Information", 204: "No Content", 205: "Reset Content", 206: "Partial Content", 207: "Multi-Status", 208: "Already Reported", 226: "IM Used"]
fileprivate static let status3xx: [Int: String] = [300: "Multiple Choices", 301: "Moved Permanently", 302: "Found", 303: "See Other", 304: "Not Modified", 305: "Use Proxy", 306: "Switch Proxy", 307: "Temporary Redirect", 308: "Permanent Redirect"]
fileprivate static let status4xx: [Int: String] = [400: "Bad Request", 401: "Unauthorized/Expired Session", 402: "Payment Required", 403: "Forbidden", 404: "Not Found", 405: "Method Not Allowed", 406: "Not Acceptable", 407: "Proxy Authentication Required", 408: "Request Timeout", 409: "Conflict", 410: "Gone", 411: "Length Required", 412: "Precondition Failed", 413: "Payload Too Large", 414: "URI Too Long", 415: "Unsupported Media Type", 416: "Range Not Satisfiable", 417: "Expectation Failed", 421: "Misdirected Request", 422: "Unprocessable Entity", 423: "Locked", 424: "Failed Dependency", 425: "Unordered Collection", 426: "Upgrade Required", 428: "Precondition Required", 429: "Too Many Requests", 431: "Request Header Fields Too Large", 451: "Unavailable For Legal Reasons"]
fileprivate static let status5xx: [Int: String] = [500: "Internal Server Error", 501: "Not Implemented", 502: "Bad Gateway", 503: "Service Unavailable", 504: "Gateway Timeout", 505: "HTTP Version Not Supported", 506: "Variant Also Negotiates", 507: "Insufficient Storage", 508: "Loop Detected", 509: "Bandwidth Limit Exceeded", 510: "Not Extended", 511: "Network Authentication Required"]
public var description: String {
switch self.rawValue {
+2 -2
View File
@@ -68,7 +68,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
return nil
}
open func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func copyItem(localFile: URL, to toPath: String, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
NotImplemented()
return nil
}
@@ -88,7 +88,7 @@ class SMBFileProvider: FileProvider, FileProviderMonitor {
return nil
}
open func writeContents(path: String, contents data: Data, atomically: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
open func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
NotImplemented()
return nil
}
+29
View File
@@ -66,3 +66,32 @@ struct SMBTime {
return Date(timeIntervalSince1970: Double(self.time) / 10000000 - 11644473600)
}
}
extension Data {
init<T>(value: T) {
var value = value
self = Data(buffer: UnsafeBufferPointer(start: &value, count: 1))
}
func scanValue<T>() -> T? {
guard MemoryLayout<T>.size <= self.count else { return nil }
return self.withUnsafeBytes { $0.pointee }
}
func scanValue<T>(start: Int) -> T? {
let length = MemoryLayout<T>.size
guard self.count >= start + length else { return nil }
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
}
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
guard self.count >= start + length else { return nil }
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
}
static func mapMemory<T, U>(from: T) -> U? {
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
let data = Data(value: from)
return data.scanValue()
}
}
+2 -3
View File
@@ -227,9 +227,8 @@ extension SMB2 {
}
init(name: UUID, data: Data) {
var uuid = uuid_t(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
(name as NSUUID).getBytes(&uuid.0)
var nameData = Data(bytes: &uuid.0, count: 16)
let uuid = name.uuid
var nameData = Data(value: uuid)
self.header = CreateContext.Header(next: 0, nameOffset: 32, nameLength: UInt16(nameData.count), reserved: 0, dataOffset: UInt16(nameData.count), dataLength: UInt32(data.count))
self.buffer = data
}
-29
View File
@@ -8,35 +8,6 @@
import Foundation
extension Data {
init<T>(value: T) {
var value = value
self = Data(buffer: UnsafeBufferPointer(start: &value, count: MemoryLayout.size(ofValue: value)))
}
func scanValue<T>() -> T? {
guard MemoryLayout<T>.size <= self.count else { return nil }
return self.withUnsafeBytes { $0.pointee }
}
func scanValue<T>(start: Int) -> T? {
let length = MemoryLayout<T>.size
guard self.count >= start + length else { return nil }
return self.subdata(in: start..<start+length).withUnsafeBytes { $0.pointee }
}
func scanString(start: Int = 0, length: Int, encoding: String.Encoding) -> String? {
guard self.count >= start + length else { return nil }
return String(data: self.subdata(in: start..<start+length), encoding: encoding)
}
static func mapMemory<T, U>(from: T) -> U? {
guard MemoryLayout<T>.size >= MemoryLayout<U>.size else { return nil }
let data = Data(value: from)
return data.scanValue()
}
}
protocol FileProviderSMBHeader {
var protocolID: UInt32 { get }
static var protocolConst: UInt32 { get }
+7 -8
View File
@@ -280,7 +280,8 @@ extension WebDAVFileProvider: FileProviderOperations {
}
@discardableResult
public func copyItem(localFile: URL, to toPath: String, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public 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)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -332,11 +333,6 @@ extension WebDAVFileProvider: FileProviderOperations {
}
extension WebDAVFileProvider: FileProviderReadWrite {
@discardableResult
public func contents(path: String, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
return self.contents(path: path, offset: 0, length: -1, completionHandler: completionHandler)
}
@discardableResult
public func contents(path: String, offset: Int64, length: Int, completionHandler: @escaping ((_ contents: Data?, _ error: Error?) -> Void)) -> OperationHandle? {
let opType = FileOperationType.fetch(path: path)
@@ -360,7 +356,7 @@ extension WebDAVFileProvider: FileProviderReadWrite {
}
@discardableResult
public func writeContents(path: String, contents data: Data, atomically: Bool = false, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
public func writeContents(path: String, contents data: Data, atomically: Bool, overwrite: Bool, completionHandler: SimpleCompletionHandler) -> OperationHandle? {
let opType = FileOperationType.modify(path: path)
guard fileOperationDelegate?.fileProvider(self, shouldDoOperation: opType) ?? true == true else {
return nil
@@ -369,6 +365,9 @@ extension WebDAVFileProvider: FileProviderReadWrite {
let url = atomically ? absoluteURL(path).appendingPathExtension("tmp") : absoluteURL(path)
var request = URLRequest(url: url)
request.httpMethod = "PUT"
if !overwrite {
request.setValue("F", forHTTPHeaderField: "Overwrite")
}
let task = session.uploadTask(with: request, from: data, completionHandler: { (data, response, error) in
var responseError: FileProviderWebDavError?
if let code = (response as? HTTPURLResponse)?.statusCode , code >= 300, let rCode = FileProviderHTTPErrorCode(rawValue: code) {
@@ -540,7 +539,7 @@ internal extension WebDAVFileProvider {
fileObject.creationDate = self.resolve(dateString: davResponse.prop["creationdate"] ?? "")
fileObject.modifiedDate = self.resolve(dateString: davResponse.prop["getlastmodified"] ?? "")
fileObject.contentType = davResponse.prop["getcontenttype"] ?? "octet/stream"
fileObject.fileType = fileObject.contentType == "httpd/unix-directory" ? .directory : .regular
fileObject.type = fileObject.contentType == "httpd/unix-directory" ? .directory : .regular
fileObject.entryTag = davResponse.prop["getetag"]
return fileObject
}