├── .gitignore
├── .swift-version
├── .swiftformat
├── .travis.yml
├── CHANGELOG.md
├── Demo
├── FilestackDemo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── FilestackDemo.xcscheme
└── FilestackDemo
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── AppIcon.png
│ │ ├── AppIcon_iPadApp_76.png
│ │ ├── AppIcon_iPadApp_76@2x.png
│ │ ├── AppIcon_iPadProApp_83.5@2x.png
│ │ ├── AppIcon_iPadSettings_29.png
│ │ ├── AppIcon_iPadSettings_29@2x.png
│ │ ├── AppIcon_iPadSpotlight_40.png
│ │ ├── AppIcon_iPadSpotlight_40@2x.png
│ │ ├── AppIcon_iPhoneApp_60@2x.png
│ │ ├── AppIcon_iPhoneApp_60@3x.png
│ │ ├── AppIcon_iPhoneSettings_29.png
│ │ ├── AppIcon_iPhoneSettings_29@2x.png
│ │ ├── AppIcon_iPhoneSettings_29@3x.png
│ │ ├── AppIcon_iPhoneSpotlight_40@2x.png
│ │ ├── AppIcon_iPhoneSpotlight_40@3x.png
│ │ └── Contents.json
│ ├── Contents.json
│ └── icon-custom-source.imageset
│ │ ├── Contents.json
│ │ ├── icon-unsplash.png
│ │ └── icon-unsplash@2x.png
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Custom Source Providers
│ ├── Cells
│ │ └── CustomCell.swift
│ └── View Controllers
│ │ └── MyCustomSourceProvider.swift
│ ├── Demo Content
│ ├── demo1.jpg
│ ├── demo2.jpg
│ ├── demo3.jpg
│ ├── demo4.jpg
│ └── demo5.jpg
│ ├── Extensions
│ └── UIViewController+PresentAlert.swift
│ ├── Info.plist
│ └── ViewController.swift
├── Filestack.podspec
├── Filestack.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ ├── WorkspaceSettings.xcsettings
│ │ └── swiftpm
│ │ └── Package.resolved
└── xcshareddata
│ └── xcschemes
│ └── Filestack.xcscheme
├── Filestack.xcworkspace
├── contents.xcworkspacedata
└── xcshareddata
│ ├── IDEWorkspaceChecks.plist
│ ├── WorkspaceSettings.xcsettings
│ └── swiftpm
│ └── Package.resolved
├── LICENSE
├── Package.resolved
├── Package.swift
├── README.md
├── Sources
└── Filestack
│ ├── Filestack.h
│ ├── Info.plist
│ ├── Internal
│ ├── Bundle.swift
│ ├── Constants.swift
│ ├── Extensions
│ │ ├── AVAsset+Export.swift
│ │ ├── Array+SafeIndex.swift
│ │ ├── Data+JSON.swift
│ │ ├── ImageEdition
│ │ │ ├── CGPoint+Distance.swift
│ │ │ ├── CGRect+Scale.swift
│ │ │ ├── UIImage+CIImageTransformations.swift
│ │ │ ├── UIImage+Rect.swift
│ │ │ └── UIImage+Sanitize.swift
│ │ ├── ImageURLExportPreset+asImagePickerControllerImageURLExportPreset.swift
│ │ ├── Math+Clamp.swift
│ │ ├── String+UTI.swift
│ │ ├── UICollectionView+Reusable.swift
│ │ ├── UIColor+Bundle.swift
│ │ ├── UIColor+Predefined.swift
│ │ ├── UIImage+Bundle.swift
│ │ ├── UIImage+Export.swift
│ │ ├── UIImage+HEIC.swift
│ │ ├── UIImage+Resized.swift
│ │ ├── UIImage+Write.swift
│ │ ├── UIView+Constraints.swift
│ │ ├── URL+Copy.swift
│ │ ├── URL+IsDirectory.swift
│ │ ├── URL+Move.swift
│ │ └── URLSession+FilestackDefault.swift
│ ├── Extractors
│ │ ├── URLExtractor.swift
│ │ └── UploadableExtractor.swift
│ ├── Formatters
│ │ └── DurationNumberFormatter.swift
│ ├── Operations
│ │ └── AssetURLExtractorOperation.swift
│ ├── Protocols
│ │ └── CloudRequest.swift
│ ├── Requests
│ │ ├── FolderListRequest.swift
│ │ ├── LogoutRequest.swift
│ │ ├── PrefetchRequest.swift
│ │ └── StoreRequest.swift
│ ├── Services
│ │ └── CloudService.swift
│ └── TrackingProgress.swift
│ ├── Public
│ ├── CompletionHandlers.swift
│ ├── Enums
│ │ ├── ClientError.swift
│ │ ├── CloudProvider.swift
│ │ ├── ImageURLExportPreset.swift
│ │ ├── PhotosPickerFilter.swift
│ │ └── PickerBehavior.swift
│ ├── Extensions
│ │ ├── Client+Deprecated.swift
│ │ └── Client+ObjC.swift
│ ├── Models
│ │ ├── Client.swift
│ │ └── Config.swift
│ └── Responses
│ │ ├── CloudResponse.swift
│ │ ├── FolderListResponse.swift
│ │ ├── LogoutResponse.swift
│ │ ├── PrefetchResponse.swift
│ │ └── StoreResponse.swift
│ ├── Resources
│ ├── Colors.xcassets
│ │ ├── Contents.json
│ │ └── SelectionCellBorderColor.colorset
│ │ │ └── Contents.json
│ ├── Icons.xcassets
│ │ ├── Contents.json
│ │ ├── clear-pattern.imageset
│ │ │ ├── Contents.json
│ │ │ └── clear-pattern.png
│ │ ├── file.imageset
│ │ │ ├── Contents.json
│ │ │ ├── file.png
│ │ │ └── file@2x.png
│ │ ├── icon-box.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-box.png
│ │ │ └── icon-box@2x.png
│ │ ├── icon-camera.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-camera.png
│ │ │ └── icon-camera@2x.png
│ │ ├── icon-circle.imageset
│ │ │ ├── Contents.json
│ │ │ └── circle_icon.png
│ │ ├── icon-clouddrive.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-clouddrive.png
│ │ │ └── icon-clouddrive@2x.png
│ │ ├── icon-crop.imageset
│ │ │ ├── Contents.json
│ │ │ └── crop_icon.png
│ │ ├── icon-customsource.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-customsource.png
│ │ │ └── icon-customsource@2x.png
│ │ ├── icon-documents.imageset
│ │ │ ├── Contents.json
│ │ │ ├── documents.png
│ │ │ └── documents@2x.png
│ │ ├── icon-dropbox.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-dropbox.png
│ │ │ └── icon-dropbox@2x.png
│ │ ├── icon-edit.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-pencil.png
│ │ ├── icon-facebook.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-facebook.png
│ │ │ └── icon-facebook@2x.png
│ │ ├── icon-file-image.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-image.png
│ │ ├── icon-file-pdf.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-file-pdf.png
│ │ ├── icon-file-unknown.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-file-unknown.png
│ │ ├── icon-file-video.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-video-camera.png
│ │ ├── icon-github.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-github.png
│ │ │ └── icon-github@2x.png
│ │ ├── icon-gmail.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-gmail.png
│ │ │ └── icon-gmail@2x.png
│ │ ├── icon-googledrive.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-googledrive.png
│ │ │ └── icon-googledrive@2x.png
│ │ ├── icon-grid.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-grid.png
│ │ │ ├── icon-grid@2x.png
│ │ │ └── icon-grid@3x.png
│ │ ├── icon-image.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon-image.png
│ │ ├── icon-instagram.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-instagram.png
│ │ │ └── icon-instagram@2x.png
│ │ ├── icon-list.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-list.png
│ │ │ ├── icon-list@2x.png
│ │ │ └── icon-list@3x.png
│ │ ├── icon-logout.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-logout.png
│ │ │ ├── icon-logout@2x.png
│ │ │ └── icon-logout@3x.png
│ │ ├── icon-onedrive.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-skydrive.png
│ │ │ └── icon-skydrive@2x.png
│ │ ├── icon-photolibrary.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-albums.png
│ │ │ └── icon-albums@2x.png
│ │ ├── icon-picasa.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-picasa.png
│ │ │ └── icon-picasa@2x.png
│ │ ├── icon-redo.imageset
│ │ │ ├── Contents.json
│ │ │ └── redo_icon.png
│ │ ├── icon-rotate.imageset
│ │ │ ├── Contents.json
│ │ │ └── rotate_icon.png
│ │ ├── icon-selected.imageset
│ │ │ ├── Contents.json
│ │ │ ├── photoIcon-1.png
│ │ │ ├── photoIcon-2.png
│ │ │ └── photoIcon.png
│ │ ├── icon-tick.imageset
│ │ │ ├── Contents.json
│ │ │ └── checkmark-solid.png
│ │ ├── icon-undo.imageset
│ │ │ ├── Contents.json
│ │ │ └── undo_icon.png
│ │ ├── icon-unsplash.imageset
│ │ │ ├── Contents.json
│ │ │ ├── icon-unsplash.png
│ │ │ └── icon-unsplash@2x.png
│ │ ├── icon-upload.imageset
│ │ │ ├── Contents.json
│ │ │ └── icon_upload.png
│ │ └── placeholder.imageset
│ │ │ ├── Contents.json
│ │ │ └── placeholder.png
│ ├── PhotoPicker.storyboard
│ └── Picker.storyboard
│ ├── UI
│ ├── Internal
│ │ ├── Collection View Cells
│ │ │ ├── ActivityIndicatorCollectionViewCell.swift
│ │ │ └── CloudItemCollectionViewCell.swift
│ │ ├── Controllers
│ │ │ ├── CloudSourceCollectionViewController.swift
│ │ │ ├── CloudSourceTabBarController.swift
│ │ │ ├── CloudSourceTableViewController.swift
│ │ │ ├── CustomPickerUploadController.swift
│ │ │ ├── DocumentPickerUploadController.swift
│ │ │ ├── ImagePickerUploadController.swift
│ │ │ ├── MonitorViewController.swift
│ │ │ ├── SourceTableViewController.swift
│ │ │ └── URLPickerUploadController.swift
│ │ ├── Enums
│ │ │ └── CloudSourceViewType.swift
│ │ ├── Extensions
│ │ │ ├── Scene+Defaults.swift
│ │ │ ├── Storyboard+Scenes.swift
│ │ │ ├── UIImage+Squared.swift
│ │ │ └── UserDefaults+State.swift
│ │ ├── FlowLayouts
│ │ │ └── CollectionViewFlowLayout.swift
│ │ ├── Models
│ │ │ └── CloudItem.swift
│ │ ├── PhotoEditor
│ │ │ ├── EditionController
│ │ │ │ ├── EditorToolbar.swift
│ │ │ │ ├── Enums
│ │ │ │ │ └── ImageEditorCommand.swift
│ │ │ │ ├── Handlers
│ │ │ │ │ ├── CircleGesturesHandler.swift
│ │ │ │ │ └── CropGesturesHandler.swift
│ │ │ │ ├── ImageEditor.swift
│ │ │ │ ├── Layers
│ │ │ │ │ ├── CircleLayer.swift
│ │ │ │ │ └── CropLayer.swift
│ │ │ │ ├── ViewController
│ │ │ │ │ ├── EditorViewController+EditDataSource.swift
│ │ │ │ │ ├── EditorViewController+ToolbarDelegate.swift
│ │ │ │ │ ├── EditorViewController+ViewSetup.swift
│ │ │ │ │ └── EditorViewController.swift
│ │ │ │ └── Views
│ │ │ │ │ └── ImageEditorView.swift
│ │ │ └── SelectionList
│ │ │ │ ├── SelectableElement.swift
│ │ │ │ ├── SelectionCell.swift
│ │ │ │ ├── SelectionListViewController+FlowLayout.swift
│ │ │ │ ├── SelectionListViewController+UICollectionView.swift
│ │ │ │ ├── SelectionListViewController.swift
│ │ │ │ └── Uploadable.swift
│ │ ├── PhotoPicker
│ │ │ ├── AlbumList
│ │ │ │ ├── AlbumCell.swift
│ │ │ │ └── AlbumListViewController.swift
│ │ │ ├── AssetCollection
│ │ │ │ ├── AssetCell.swift
│ │ │ │ └── AssetCollectionViewController.swift
│ │ │ ├── PhotoAlbumRepository.swift
│ │ │ ├── PhotoPickerController.swift
│ │ │ └── PhotosExtensions.swift
│ │ ├── Protocols
│ │ │ ├── CellDescriptibleSource.swift
│ │ │ ├── CloudSourceDataSource.swift
│ │ │ ├── CloudSourceDataSourceConsumer.swift
│ │ │ └── Scene.swift
│ │ ├── Scenes
│ │ │ ├── CloudSourceBarTabScene.swift
│ │ │ └── PickerNavigationScene.swift
│ │ └── Table View Cells
│ │ │ ├── ActivityIndicatorTableViewCell.swift
│ │ │ └── CloudItemTableViewCell.swift
│ └── Public
│ │ ├── Controllers
│ │ └── PickerNavigationController.swift
│ │ ├── Enums
│ │ └── LocalProvider.swift
│ │ ├── Models
│ │ ├── CloudSource.swift
│ │ ├── LocalSource.swift
│ │ └── Stylizer.swift
│ │ └── Protocols
│ │ ├── SourceProvider.swift
│ │ ├── SourceProviderDelegate.swift
│ │ └── StylizerDelegate.swift
│ └── VERSION
├── VERSION
└── bin
├── apply-swiftformat.sh
├── deploy-docs.sh
├── generate-and-deploy-docs.sh
├── generate-docs.sh
├── set-buildnumber.sh
└── set-version.sh
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | #
3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
4 |
5 | ## Build generated
6 | build/
7 | DerivedData
8 |
9 | ## Various settings
10 | *.pbxuser
11 | !default.pbxuser
12 | *.mode1v3
13 | !default.mode1v3
14 | *.mode2v3
15 | !default.mode2v3
16 | *.perspectivev3
17 | !default.perspectivev3
18 | xcuserdata
19 | .swiftpm/xcode
20 |
21 | ## Other
22 | *.xccheckout
23 | *.moved-aside
24 | *.xcuserstate
25 | *.xcscmblueprint
26 | .idea
27 | compile_commands.json
28 |
29 | ## Obj-C/Swift specific
30 | *.hmap
31 | *.ipa
32 |
33 | # Swift Package Manager
34 | #
35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
36 | /.build
37 | /Packages
38 |
39 | # CocoaPods
40 | #
41 | # We recommend against adding the Pods directory to your .gitignore. However
42 | # you should judge for yourself, the pros and cons are mentioned at:
43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
44 | #
45 | Pods/
46 |
47 | # Carthage
48 | #
49 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
50 | # Carthage/Checkouts
51 |
52 | Carthage/
53 | # Carthage/Build
54 |
55 | # fastlane
56 | #
57 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
58 | # screenshots whenever they are needed.
59 | # For more information about the recommended setup visit:
60 | # https://github.com/fastlane/fastlane/blob/master/docs/Gitignore.md
61 |
62 | fastlane/report.xml
63 | fastlane/screenshots
64 |
65 | .DS_Store
66 |
67 | # Jazzy Docs
68 | docs/
69 |
70 |
--------------------------------------------------------------------------------
/.swift-version:
--------------------------------------------------------------------------------
1 | 4.2
--------------------------------------------------------------------------------
/.swiftformat:
--------------------------------------------------------------------------------
1 | # file options
2 |
3 | --exclude Carthage
4 |
5 | # format options
6 |
7 | --allman false
8 | --binarygrouping 4,8
9 | --commas always
10 | --comments indent
11 | --decimalgrouping 3,6
12 | --elseposition same-line
13 | --empty void
14 | --exponentcase lowercase
15 | --exponentgrouping disabled
16 | --fractiongrouping disabled
17 | --header ignore
18 | --hexgrouping 4,8
19 | --hexliteralcase uppercase
20 | --ifdef indent
21 | --indent 4
22 | --indentcase false
23 | --importgrouping testable-bottom
24 | --linebreaks lf
25 | --octalgrouping 4,8
26 | --operatorfunc spaced
27 | --patternlet hoist
28 | --ranges spaced
29 | --self remove
30 | --semicolons inline
31 | --stripunusedargs always
32 | --trimwhitespace always
33 | --wraparguments preserve
34 | --wrapcollections preserve
35 |
36 | # rules
37 |
38 | --enable isEmpty
39 | --enable modifierOrder
40 | --enable spaceInsideComments
41 | --enable linebreakAtEndOfFile
42 | --enable typeSugar
43 | --disable redundantSelf
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: objective-c
2 | osx_image: xcode9
3 | branches:
4 | only:
5 | - ios-sdk
6 | env:
7 | global:
8 | - LC_CTYPE=en_US.UTF-8
9 | - LANG=en_US.UTF-8
10 | - IOS_FRAMEWORK_SCHEME="Filestack"
11 | - NSUnbufferedIO=YES
12 | matrix:
13 | - DESTINATION="OS=11.0,name=iPhone 8 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME"
14 | - DESTINATION="OS=10.3.1,name=iPhone 7 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME"
15 | - DESTINATION="OS=9.3,name=iPhone 6" SCHEME="$IOS_FRAMEWORK_SCHEME"
16 | - DESTINATION="OS=9.0,name=iPhone 5" SCHEME="$IOS_FRAMEWORK_SCHEME"
17 | before_install:
18 | - brew update
19 | - brew outdated carthage || brew upgrade carthage
20 | - brew outdated xctool || brew upgrade xctool
21 | - carthage bootstrap --no-build --use-submodules --verbose
22 | script:
23 | - set -o pipefail
24 | - xcodebuild -version
25 | - xcodebuild -showsdks
26 | - carthage version
27 |
28 | # Build Framework in Debug and Run Tests
29 | - xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES clean build build-for-testing | xcpretty;
30 | - travis_retry xcodebuild -scheme "$SCHEME" -destination "$DESTINATION" -configuration Debug test-without-building | xcpretty;
31 |
32 | after_success:
33 | - sleep 5
34 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo.xcodeproj/xcshareddata/xcschemes/FilestackDemo.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
54 |
60 |
61 |
62 |
63 |
69 |
71 |
77 |
78 |
79 |
80 |
82 |
83 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadApp_76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadApp_76.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadApp_76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadApp_76@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadProApp_83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadProApp_83.5@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSettings_29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSettings_29.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSettings_29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSettings_29@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSpotlight_40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSpotlight_40.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSpotlight_40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPadSpotlight_40@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneApp_60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneApp_60@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneApp_60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneApp_60@3x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSettings_29@3x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSpotlight_40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSpotlight_40@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSpotlight_40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/AppIcon_iPhoneSpotlight_40@3x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "size" : "20x20",
6 | "scale" : "2x"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "size" : "20x20",
11 | "scale" : "3x"
12 | },
13 | {
14 | "size" : "29x29",
15 | "idiom" : "iphone",
16 | "filename" : "AppIcon_iPhoneSettings_29.png",
17 | "scale" : "1x"
18 | },
19 | {
20 | "size" : "29x29",
21 | "idiom" : "iphone",
22 | "filename" : "AppIcon_iPhoneSettings_29@2x.png",
23 | "scale" : "2x"
24 | },
25 | {
26 | "size" : "29x29",
27 | "idiom" : "iphone",
28 | "filename" : "AppIcon_iPhoneSettings_29@3x.png",
29 | "scale" : "3x"
30 | },
31 | {
32 | "size" : "40x40",
33 | "idiom" : "iphone",
34 | "filename" : "AppIcon_iPhoneSpotlight_40@2x.png",
35 | "scale" : "2x"
36 | },
37 | {
38 | "size" : "40x40",
39 | "idiom" : "iphone",
40 | "filename" : "AppIcon_iPhoneSpotlight_40@3x.png",
41 | "scale" : "3x"
42 | },
43 | {
44 | "size" : "60x60",
45 | "idiom" : "iphone",
46 | "filename" : "AppIcon_iPhoneApp_60@2x.png",
47 | "scale" : "2x"
48 | },
49 | {
50 | "size" : "60x60",
51 | "idiom" : "iphone",
52 | "filename" : "AppIcon_iPhoneApp_60@3x.png",
53 | "scale" : "3x"
54 | },
55 | {
56 | "idiom" : "ipad",
57 | "size" : "20x20",
58 | "scale" : "1x"
59 | },
60 | {
61 | "idiom" : "ipad",
62 | "size" : "20x20",
63 | "scale" : "2x"
64 | },
65 | {
66 | "size" : "29x29",
67 | "idiom" : "ipad",
68 | "filename" : "AppIcon_iPadSettings_29.png",
69 | "scale" : "1x"
70 | },
71 | {
72 | "size" : "29x29",
73 | "idiom" : "ipad",
74 | "filename" : "AppIcon_iPadSettings_29@2x.png",
75 | "scale" : "2x"
76 | },
77 | {
78 | "size" : "40x40",
79 | "idiom" : "ipad",
80 | "filename" : "AppIcon_iPadSpotlight_40.png",
81 | "scale" : "1x"
82 | },
83 | {
84 | "size" : "40x40",
85 | "idiom" : "ipad",
86 | "filename" : "AppIcon_iPadSpotlight_40@2x.png",
87 | "scale" : "2x"
88 | },
89 | {
90 | "size" : "76x76",
91 | "idiom" : "ipad",
92 | "filename" : "AppIcon_iPadApp_76.png",
93 | "scale" : "1x"
94 | },
95 | {
96 | "size" : "76x76",
97 | "idiom" : "ipad",
98 | "filename" : "AppIcon_iPadApp_76@2x.png",
99 | "scale" : "2x"
100 | },
101 | {
102 | "size" : "83.5x83.5",
103 | "idiom" : "ipad",
104 | "filename" : "AppIcon_iPadProApp_83.5@2x.png",
105 | "scale" : "2x"
106 | },
107 | {
108 | "size" : "1024x1024",
109 | "idiom" : "ios-marketing",
110 | "filename" : "AppIcon.png",
111 | "scale" : "1x"
112 | }
113 | ],
114 | "info" : {
115 | "version" : 1,
116 | "author" : "xcode"
117 | }
118 | }
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/icon-custom-source.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon-unsplash.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "icon-unsplash@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | },
22 | "properties" : {
23 | "template-rendering-intent" : "template"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/icon-custom-source.imageset/icon-unsplash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/icon-custom-source.imageset/icon-unsplash.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Assets.xcassets/icon-custom-source.imageset/icon-unsplash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Assets.xcassets/icon-custom-source.imageset/icon-unsplash@2x.png
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Custom Source Providers/Cells/CustomCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomCell.swift
3 | // CustomCell
4 | //
5 | // Created by Ruben Nine on 5/8/21.
6 | // Copyright © 2021 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CustomCell: UICollectionViewCell {
12 | let imageView = UIImageView()
13 |
14 | override init(frame: CGRect) {
15 | super.init(frame: frame)
16 | setupView()
17 | }
18 |
19 | required init?(coder: NSCoder) {
20 | fatalError("init(coder:) has not been implemented")
21 | }
22 | }
23 |
24 | extension CustomCell {
25 | func setupView() {
26 | selectedBackgroundView = UIView()
27 | selectedBackgroundView?.backgroundColor = UIColor.systemOrange.withAlphaComponent(0.33)
28 | selectedBackgroundView?.layer.cornerRadius = 9
29 | selectedBackgroundView?.clipsToBounds = true
30 | selectedBackgroundView?.layer.borderColor = UIColor.white.cgColor
31 | selectedBackgroundView?.layer.borderWidth = 2
32 |
33 | imageView.translatesAutoresizingMaskIntoConstraints = false
34 | imageView.layer.cornerRadius = 9
35 | imageView.contentMode = .scaleAspectFit
36 | imageView.clipsToBounds = true
37 |
38 | let backgroundView = UIView()
39 | backgroundView.addSubview(imageView)
40 |
41 | imageView.topAnchor.constraint(equalTo: backgroundView.topAnchor).isActive = true
42 | imageView.bottomAnchor.constraint(equalTo: backgroundView.bottomAnchor).isActive = true
43 | imageView.leadingAnchor.constraint(equalTo: backgroundView.leadingAnchor).isActive = true
44 | imageView.trailingAnchor.constraint(equalTo: backgroundView.trailingAnchor).isActive = true
45 |
46 | self.backgroundView = backgroundView
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Demo Content/demo1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Demo Content/demo1.jpg
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Demo Content/demo2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Demo Content/demo2.jpg
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Demo Content/demo3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Demo Content/demo3.jpg
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Demo Content/demo4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Demo Content/demo4.jpg
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Demo Content/demo5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Demo/FilestackDemo/Demo Content/demo5.jpg
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Extensions/UIViewController+PresentAlert.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIViewController+PresentAlert.swift
3 | // FilestackDemo
4 | //
5 | // Created by Ruben Nine on 16/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIViewController {
12 | func presentAlert(titled title: String, message: String) {
13 | let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
14 | alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
15 |
16 | self.present(alert, animated: false, completion: nil)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Demo/FilestackDemo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleURLTypes
20 |
21 |
22 | CFBundleURLName
23 | com.filestack.FilestackDemo
24 | CFBundleURLSchemes
25 |
26 | filestackdemo
27 |
28 |
29 |
30 | CFBundleVersion
31 | 1
32 | LSRequiresIPhoneOS
33 |
34 | NSCameraUsageDescription
35 | The demo needs access to the camera.
36 | NSMicrophoneUsageDescription
37 | The demo needs access to the microphone for video recording.
38 | NSPhotoLibraryUsageDescription
39 | This demo needs access to the photo library in order to be able to pick pictures from it.
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIMainStoryboardFile
43 | Main
44 | UIRequiredDeviceCapabilities
45 |
46 | armv7
47 |
48 | UISupportedInterfaceOrientations
49 |
50 | UIInterfaceOrientationPortrait
51 | UIInterfaceOrientationLandscapeLeft
52 | UIInterfaceOrientationLandscapeRight
53 |
54 | UISupportedInterfaceOrientations~ipad
55 |
56 | UIInterfaceOrientationPortrait
57 | UIInterfaceOrientationPortraitUpsideDown
58 | UIInterfaceOrientationLandscapeLeft
59 | UIInterfaceOrientationLandscapeRight
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/Filestack.podspec:
--------------------------------------------------------------------------------
1 | Pod::Spec.new do |spec|
2 | spec.name = 'Filestack'
3 | spec.version = File.read('./VERSION')
4 | spec.license = { :type => 'Apache License, Version 2.0"', :file => "LICENSE" }
5 | spec.homepage = 'https://github.com/filestack/filestack-ios'
6 | spec.authors = { 'Filestack' => 'ios@filestack.com' }
7 | spec.summary = 'Official iOS SDK for Filestack.'
8 | spec.source = { :git => 'https://github.com/filestack/filestack-ios.git', :tag => spec.version }
9 |
10 | spec.ios.deployment_target = '14.0'
11 |
12 | spec.source_files = 'Sources/Filestack/**/*.{h,swift}'
13 | spec.resources = ["Sources/Filestack/Resources/*.{storyboard,xcassets}"]
14 | spec.public_header_files = 'Sources/**/*.h'
15 |
16 | spec.swift_versions = [4.2, 5.2]
17 |
18 | spec.dependency 'FilestackSDK', '~> 2.8.0'
19 | spec.dependency 'ZIPFoundation', '0.9.19'
20 | end
21 |
--------------------------------------------------------------------------------
/Filestack.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Filestack.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Filestack.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/Filestack.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "FilestackSDK",
6 | "repositoryURL": "https://github.com/filestack/filestack-swift.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "5ba0ac9ca2fb53ccf18335b1654b8cc600785b66",
10 | "version": "2.8.0"
11 | }
12 | },
13 | {
14 | "package": "OHHTTPStubs",
15 | "repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "e92b5a5746ef16add2a1424f1fc19529d9a75cde",
19 | "version": "9.0.0"
20 | }
21 | },
22 | {
23 | "package": "ZIPFoundation",
24 | "repositoryURL": "https://github.com/weichsel/ZIPFoundation",
25 | "state": {
26 | "branch": null,
27 | "revision": "cf10bbff6ac3b873e97b36b9784c79866a051a8e",
28 | "version": "0.9.12"
29 | }
30 | }
31 | ]
32 | },
33 | "version": 1
34 | }
35 |
--------------------------------------------------------------------------------
/Filestack.xcodeproj/xcshareddata/xcschemes/Filestack.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
37 |
38 |
39 |
40 |
41 |
42 |
52 |
53 |
59 |
60 |
61 |
62 |
68 |
69 |
75 |
76 |
77 |
78 |
80 |
81 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/Filestack.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Filestack.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Filestack.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Filestack.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "FilestackSDK",
6 | "repositoryURL": "https://github.com/filestack/filestack-swift.git",
7 | "state": {
8 | "branch": null,
9 | "revision": "5ba0ac9ca2fb53ccf18335b1654b8cc600785b66",
10 | "version": "2.8.0"
11 | }
12 | },
13 | {
14 | "package": "OHHTTPStubs",
15 | "repositoryURL": "https://github.com/AliSoftware/OHHTTPStubs.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "12f19662426d0434d6c330c6974d53e2eb10ecd9",
19 | "version": "9.1.0"
20 | }
21 | },
22 | {
23 | "package": "ZIPFoundation",
24 | "repositoryURL": "https://github.com/weichsel/ZIPFoundation",
25 | "state": {
26 | "branch": null,
27 | "revision": "cf10bbff6ac3b873e97b36b9784c79866a051a8e",
28 | "version": "0.9.12"
29 | }
30 | }
31 | ]
32 | },
33 | "version": 1
34 | }
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015–2019 Filestack (https://www.filestack.com/)
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in
11 | all copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "object": {
3 | "pins": [
4 | {
5 | "package": "FilestackSDK",
6 | "repositoryURL": "https://github.com/filestack/filestack-swift",
7 | "state": {
8 | "branch": null,
9 | "revision": "5ba0ac9ca2fb53ccf18335b1654b8cc600785b66",
10 | "version": "2.8.0"
11 | }
12 | },
13 | {
14 | "package": "ZIPFoundation",
15 | "repositoryURL": "https://github.com/weichsel/ZIPFoundation.git",
16 | "state": {
17 | "branch": null,
18 | "revision": "cf10bbff6ac3b873e97b36b9784c79866a051a8e",
19 | "version": "0.9.17"
20 | }
21 | }
22 | ]
23 | },
24 | "version": 1
25 | }
26 |
--------------------------------------------------------------------------------
/Package.swift:
--------------------------------------------------------------------------------
1 | // swift-tools-version:5.3
2 | // The swift-tools-version declares the minimum version of Swift required to build this package.
3 |
4 | import PackageDescription
5 |
6 | let package = Package(
7 | name: "Filestack",
8 | platforms: [.iOS(.v11)],
9 | products: [
10 | // Products define the executables and libraries a package produces, and make them visible to other packages.
11 | .library(
12 | name: "Filestack",
13 | targets: ["Filestack"]
14 | ),
15 | ],
16 | dependencies: [
17 | .package(name: "FilestackSDK", url: "https://github.com/filestack/filestack-swift", .upToNextMajor(from: Version(2, 7, 0))),
18 | .package(url: "https://github.com/weichsel/ZIPFoundation.git", .upToNextMajor(from: Version(0, 9, 0)))
19 | ],
20 | targets: [
21 | .target(
22 | name: "Filestack",
23 | dependencies: ["FilestackSDK", "ZIPFoundation"],
24 | exclude: ["Filestack.h", "Info.plist"],
25 | resources: [
26 | .copy("VERSION")
27 | ]
28 | )
29 | ]
30 | )
31 |
--------------------------------------------------------------------------------
/Sources/Filestack/Filestack.h:
--------------------------------------------------------------------------------
1 | //
2 | // Filestack.h
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/18/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | #import
10 |
11 | //! Project version number for Filestack.
12 | FOUNDATION_EXPORT double FilestackVersionNumber;
13 |
14 | //! Project version string for Filestack.
15 | FOUNDATION_EXPORT const unsigned char FilestackVersionString[];
16 |
17 | // In this header, you should import all the public headers of your framework using statements like #import
18 |
19 |
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | $(CURRENT_PROJECT_VERSION)
21 | NSPrincipalClass
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Bundle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Bundle.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 13/10/2020.
6 | // Copyright © 2020 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private class BundleFinder {}
12 |
13 | /// Returns the bundle that is associated to this module (supports SPM.)
14 | let bundle: Bundle = {
15 | #if SWIFT_PACKAGE
16 | return Bundle.module
17 | #else
18 | return Bundle(for: BundleFinder.self)
19 | #endif
20 | }()
21 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/24/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct Constants {
12 | static let cloudURL = URL(string: "https://cloud.filestackapi.com")!
13 | static let validHTTPResponseCodes = Array(200 ..< 300)
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/AVAsset+Export.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AVAsset+Export.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/11/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import AVKit
10 |
11 | extension AVAsset {
12 | func videoExportSession(using preset: String) -> AVAssetExportSession? {
13 | let export = AVAssetExportSession(asset: self, presetName: preferredVideoPreset(using: preset))
14 | let tempDirURL = FileManager.default.temporaryDirectory
15 |
16 | export?.outputURL = tempDirURL.appendingPathComponent(UUID().uuidString).appendingPathExtension("mov")
17 | export?.outputFileType = .mov
18 |
19 | return export
20 | }
21 |
22 | private func preferredVideoPreset(using preset: String) -> String {
23 | let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: self)
24 |
25 | if compatiblePresets.contains(preset) {
26 | return preset
27 | }
28 |
29 | return AVAssetExportPresetPassthrough
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/Array+SafeIndex.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array+SafeIndex.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/13/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Array {
12 | // Originally written by Erica Sadun, and Mike Ash
13 | // Source: http://ericasadun.com/2015/06/01/swift-safe-array-indexing-my-favorite-thing-of-the-new-week/
14 | subscript(safe index: UInt) -> Element? {
15 | return Int(index) < count ? self[Int(index)] : nil
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/Data+JSON.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Data+JSON.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/8/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Data {
12 | func parseJSON() -> [String: Any]? {
13 | return (try? JSONSerialization.jsonObject(with: self)) as? [String: Any]
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/ImageEdition/CGPoint+Distance.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGPoint+Distance.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 09/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension CGPoint {
12 | enum Metric {
13 | case euclidean
14 | case manhattan
15 | case maximum
16 | }
17 |
18 | func distance(to point: CGPoint, metric: Metric = .euclidean) -> CGFloat {
19 | switch metric {
20 | case .euclidean: return euclideanDistance(to: point)
21 | case .manhattan: return manhattanDistance(to: point)
22 | case .maximum: return maximumDistance(to: point)
23 | }
24 | }
25 |
26 | func euclideanDistance(to point: CGPoint) -> CGFloat {
27 | return sqrt(pow(x - point.x, 2) + pow(y - point.y, 2))
28 | }
29 |
30 | func manhattanDistance(to point: CGPoint) -> CGFloat {
31 | return abs(x - point.x) + abs(y - point.y)
32 | }
33 |
34 | func maximumDistance(to point: CGPoint) -> CGFloat {
35 | return max(abs(x - point.x), abs(y - point.y))
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/ImageEdition/CGRect+Scale.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CGRect+Scale.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 02/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension CGRect {
12 | func scaled(by scale: CGFloat) -> CGRect {
13 | return applying(CGAffineTransform(scaleX: scale, y: scale))
14 | }
15 | }
16 |
17 | extension CGPoint {
18 | func movedBy(x: CGFloat = 0, y: CGFloat = 0) -> CGPoint {
19 | return applying(CGAffineTransform(translationX: x, y: y))
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/ImageEdition/UIImage+Rect.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Rect.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 02/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | var cgRect: CGRect {
13 | return CGRect(origin: .zero, size: size)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/ImageEdition/UIImage+Sanitize.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEditor+Sanitize.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 28/06/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | // MARK: Sanitize
12 |
13 | extension UIImage {
14 | var sanitized: UIImage? {
15 | guard
16 | let imageRef = cgImage,
17 | let colorSpace = imageRef.colorSpace,
18 | let context = CGContext(data: nil,
19 | width: imageRef.width,
20 | height: imageRef.height,
21 | bitsPerComponent: imageRef.bitsPerComponent,
22 | bytesPerRow: imageRef.bytesPerRow,
23 | space: colorSpace,
24 | bitmapInfo: imageRef.bitmapInfo.rawValue) else { return nil }
25 | context.draw(imageRef, in: CGRect(x: 0, y: 0, width: CGFloat(imageRef.width), height: CGFloat(imageRef.height)))
26 | guard let sanitizedRef = context.makeImage() else { return nil }
27 | return UIImage(cgImage: sanitizedRef, scale: scale, orientation: .up)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/ImageURLExportPreset+asImagePickerControllerImageURLExportPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageURLExportPreset+asImagePickerControllerImageURLExportPreset.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension ImageURLExportPreset {
12 | var asImagePickerControllerImageURLExportPreset: UIImagePickerController.ImageURLExportPreset {
13 | switch self {
14 | case .compatible:
15 | return .compatible
16 | case .current:
17 | return .current
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/Math+Clamp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Math+Clamp.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 10/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | func clamp(_ element: T, min minimum: T, max maximum: T) -> T where T: Comparable {
12 | return min(maximum, max(element, minimum))
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/String+UTI.swift:
--------------------------------------------------------------------------------
1 | //
2 | // String+UTI.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/1/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import MobileCoreServices.UTType
11 |
12 | extension String {
13 | var UTI: CFString? {
14 | var ext = (self as NSString).pathExtension
15 |
16 | if ext.isEmpty {
17 | ext = "txt"
18 | }
19 |
20 | guard let utiRef = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, ext as CFString, nil) else { return nil }
21 |
22 | let uti = utiRef.takeUnretainedValue()
23 | utiRef.release()
24 |
25 | return uti
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UICollectionView+Reusable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionView+Reusable.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 26/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | private extension UICollectionViewCell {
12 | class var reuseIdentifier: String {
13 | return String(describing: self)
14 | }
15 | }
16 |
17 | extension UICollectionView {
18 | func register(_ cell: Cell.Type) {
19 | register(cell, forCellWithReuseIdentifier: cell.reuseIdentifier)
20 | }
21 |
22 | func reuse(_ cell: Cell.Type, for indexPath: IndexPath) -> Cell? {
23 | let reusable = dequeueReusableCell(withReuseIdentifier: cell.reuseIdentifier, for: indexPath)
24 | return reusable as? Cell
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIColor+Bundle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+Bundle.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 17/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor {
12 | static func fromFilestackBundle(_ name: String) -> UIColor? {
13 | return UIColor(named: name, in: bundle, compatibleWith: nil)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIColor+Predefined.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIColor+predefined.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 10/08/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIColor {
12 | static var appleBlue: UIColor {
13 | return UIColor(red: 0, green: 122 / 255, blue: 1, alpha: 1)
14 | }
15 |
16 | static var appleTableSeparator: UIColor {
17 | return UIColor(red: 224 / 255, green: 224 / 255, blue: 224 / 255, alpha: 1)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIImage+Bundle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Bundle.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 30/07/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | static func fromFilestackBundle(_ name: String) -> UIImage {
13 | return UIImage(named: name, in: bundle, compatibleWith: nil) ?? UIImage()
14 | }
15 |
16 | static func templatedFilestackImage(_ name: String) -> UIImage {
17 | return fromFilestackBundle(name).withRenderingMode(.alwaysTemplate)
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIImage+Export.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Export.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/11/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | func exportHEICImage(to destinationURL: URL, quality: Float) -> Bool {
13 | guard let imageData = heicRepresentation(quality: quality) else { return false }
14 |
15 | return export(data: imageData, to: destinationURL)
16 | }
17 |
18 | func exportJPGImage(to destinationURL: URL, quality: Float) -> Bool {
19 | guard let imageData = jpegData(compressionQuality: CGFloat(quality)) else { return false }
20 |
21 | return export(data: imageData, to: destinationURL)
22 | }
23 |
24 | // MARK: - Private Functions
25 |
26 | private func export(data: Data, to destinationURL: URL) -> Bool {
27 | do {
28 | try data.write(to: destinationURL)
29 | return true
30 | } catch {
31 | return false
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIImage+HEIC.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+HEIC.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/20/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import UIKit
11 |
12 | extension UIImage {
13 | func heicRepresentation(quality: Float) -> Data? {
14 | var imageData: Data?
15 | let destinationData = NSMutableData()
16 |
17 | guard
18 | let cgImage = cgImage,
19 | let destination = CGImageDestinationCreateWithData(destinationData, AVFileType.heic as CFString, 1, nil)
20 | else {
21 | return nil
22 | }
23 |
24 | let options: [CFString: Any] = [
25 | kCGImageDestinationLossyCompressionQuality: quality,
26 | kCGImagePropertyOrientation: cgImageOrientation.rawValue
27 | ]
28 |
29 | CGImageDestinationAddImage(destination, cgImage, options as CFDictionary)
30 | CGImageDestinationFinalize(destination)
31 |
32 | imageData = destinationData as Data
33 |
34 | return imageData
35 | }
36 | }
37 |
38 | extension UIImage {
39 | var cgImageOrientation: CGImagePropertyOrientation { .init(imageOrientation) }
40 | }
41 |
42 | extension CGImagePropertyOrientation {
43 | init(_ uiOrientation: UIImage.Orientation) {
44 | switch uiOrientation {
45 | case .up: self = .up
46 | case .upMirrored: self = .upMirrored
47 | case .down: self = .down
48 | case .downMirrored: self = .downMirrored
49 | case .left: self = .left
50 | case .leftMirrored: self = .leftMirrored
51 | case .right: self = .right
52 | case .rightMirrored: self = .rightMirrored
53 | @unknown default:
54 | fatalError()
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIImage+Resized.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Resized.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/11/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | func resized(for size: CGSize) -> UIImage? {
13 | let renderer = UIGraphicsImageRenderer(size: size)
14 |
15 | return renderer.image { _ in
16 | draw(in: CGRect(origin: .zero, size: size))
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIImage+Write.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+write.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 31/07/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | func with(written text: String, atPoint point: CGPoint) -> UIImage {
13 | let textSize = min(size.height, size.width) / 20
14 | let textColor = UIColor.white
15 | let textFont = UIFont(name: "Helvetica Bold", size: textSize)!
16 |
17 | UIGraphicsBeginImageContextWithOptions(size, false, UIScreen.main.scale)
18 |
19 | let textFontAttributes: [NSAttributedString.Key: Any] = [.font: textFont, .foregroundColor: textColor]
20 | draw(in: CGRect(origin: CGPoint.zero, size: size))
21 |
22 | let rect = CGRect(origin: point, size: size)
23 | text.draw(in: rect, withAttributes: textFontAttributes)
24 |
25 | let newImage = UIGraphicsGetImageFromCurrentImageContext()
26 | UIGraphicsEndImageContext()
27 |
28 | return newImage ?? self
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/UIView+Constraints.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIView+Constraints.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 05/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIView {
12 | func fill(with subview: UIView,
13 | connectingEdges: [NSLayoutConstraint.Attribute] = [.top, .bottom, .left, .right],
14 | inset: CGFloat = 0,
15 | withSafeAreaRespecting useSafeArea: Bool = false) {
16 | subview.translatesAutoresizingMaskIntoConstraints = false
17 |
18 | if !subviews.contains(subview) {
19 | addSubview(subview)
20 | }
21 |
22 | connect(edges: connectingEdges, of: subview, inset: inset, withSafeAreaRespecting: useSafeArea)
23 | }
24 |
25 | func connect(edges: [NSLayoutConstraint.Attribute],
26 | of subview: UIView,
27 | inset: CGFloat = 0,
28 | withSafeAreaRespecting useSafeArea: Bool = false) {
29 | guard subviews.contains(subview) else { return }
30 |
31 | let primaryItem = useSafeArea ? safeAreaLayoutGuide : self
32 |
33 | for edge in edges {
34 | let reversedEdges: [NSLayoutConstraint.Attribute] = [.top, .left, .topMargin, .leftMargin]
35 | let offset = reversedEdges.contains(edge) ? -inset : inset
36 |
37 | NSLayoutConstraint(item: primaryItem, attribute: edge, relatedBy: .equal,
38 | toItem: subview, attribute: edge, multiplier: 1, constant: offset).isActive = true
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/URL+Copy.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+Copy.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/15/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 | func copy(to destinationURL: URL) -> Bool {
13 | let fm = FileManager.default
14 |
15 | do {
16 | try fm.copyItem(at: self, to: destinationURL)
17 | return true
18 | } catch {
19 | return false
20 | }
21 | }
22 |
23 | func copyIntoTemporaryLocation() -> URL? {
24 | let fm = FileManager.default
25 |
26 | let destinationURL = fm.temporaryDirectory
27 | .appendingPathComponent(UUID().uuidString)
28 | .appendingPathExtension(pathExtension)
29 |
30 | if copy(to: destinationURL) {
31 | return destinationURL
32 | } else {
33 | return nil
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/URL+IsDirectory.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+IsDirectory.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 17/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 | var isDirectory: Bool {
13 | var isDirectory: ObjCBool = false
14 | FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory)
15 |
16 | return isDirectory.boolValue
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/URL+Move.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URL+Move.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 15/10/2020.
6 | // Copyright © 2020 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension URL {
12 | func move(to destinationURL: URL) -> Bool {
13 | let fm = FileManager.default
14 |
15 | do {
16 | try fm.moveItem(at: self, to: destinationURL)
17 | return true
18 | } catch {
19 | return false
20 | }
21 | }
22 |
23 | func moveIntoTemporaryLocation() -> URL? {
24 | let fm = FileManager.default
25 |
26 | let destinationURL = fm.temporaryDirectory
27 | .appendingPathComponent(UUID().uuidString)
28 | .appendingPathExtension(pathExtension)
29 |
30 | if move(to: destinationURL) {
31 | return destinationURL
32 | } else {
33 | return nil
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extensions/URLSession+FilestackDefault.swift:
--------------------------------------------------------------------------------
1 | //
2 | // URLSession+FilestackDefault.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/24/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | #if SWIFT_PACKAGE
12 | #else
13 | private class BundleFinder {}
14 | #endif
15 |
16 | extension URLSession {
17 | static var filestackDefault: URLSession {
18 | let configuration = URLSessionConfiguration.default
19 |
20 | configuration.isDiscretionary = false
21 | configuration.shouldUseExtendedBackgroundIdleMode = true
22 | configuration.httpMaximumConnectionsPerHost = 20
23 | configuration.httpShouldUsePipelining = true
24 | configuration.httpAdditionalHeaders = customHTTPHeaders
25 |
26 | return URLSession(configuration: configuration)
27 | }
28 |
29 | func jsonRequest(_ url: URL, payload: [String: Any], method: String = "POST") -> URLRequest {
30 | var request = URLRequest(url: url)
31 |
32 | request.setValue("application/json", forHTTPHeaderField: "Accept")
33 | request.setValue("application/json", forHTTPHeaderField: "Content-Type")
34 | request.httpMethod = method
35 | request.httpBody = try? JSONSerialization.data(withJSONObject: payload, options: [])
36 |
37 | return request
38 | }
39 | }
40 |
41 | // MARK: - Private Functions
42 |
43 | private extension URLSession {
44 | static var customHTTPHeaders: [String: String] {
45 | var defaultHeaders: [String: String] = [:]
46 |
47 | defaultHeaders["User-Agent"] = "filestack-ios \(shortVersionString)"
48 | defaultHeaders["Filestack-Source"] = "Swift-\(shortVersionString)"
49 |
50 | return defaultHeaders
51 | }
52 |
53 | static var shortVersionString: String {
54 | #if SWIFT_PACKAGE
55 | if let url = Bundle.module.url(forResource: "VERSION", withExtension: nil),
56 | let data = try? Data(contentsOf: url),
57 | let version = String(data: data, encoding: .utf8)?.trimmingCharacters(in: CharacterSet.alphanumerics.inverted)
58 | {
59 | return version
60 | }
61 | #else
62 | if let info = Bundle(for: BundleFinder.self).infoDictionary,
63 | let version = info["CFBundleShortVersionString"] as? String {
64 | return version
65 | }
66 | #endif
67 |
68 | return "0.0.0"
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Extractors/UploadableExtractor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UploadableExtractor.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 02/08/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Photos
11 |
12 | class UploadableExtractor {
13 | // MARK: - Private Properties
14 |
15 | private lazy var imageManager = PHCachingImageManager.default()
16 |
17 | private lazy var videoRequestOptions: PHVideoRequestOptions = {
18 | let options = PHVideoRequestOptions()
19 |
20 | options.version = PHVideoRequestOptionsVersion.current
21 | options.deliveryMode = PHVideoRequestOptionsDeliveryMode.fastFormat
22 |
23 | return options
24 | }()
25 | }
26 |
27 | // MARK: - Internal Functions
28 |
29 | extension UploadableExtractor {
30 | func fetchUploadable(using asset: PHAsset, completion: @escaping (Uploadable?, PHImageRequestID) -> Void) -> PHImageRequestID? {
31 | switch asset.mediaType {
32 | case .image: return fetchImage(for: asset, completion: completion)
33 | case .video: return fetchVideo(for: asset, completion: completion)
34 | case .unknown, .audio: fallthrough
35 | @unknown default: return nil
36 | }
37 | }
38 |
39 | func cancelFetch(using requestID: PHImageRequestID) {
40 | PHImageManager.default().cancelImageRequest(requestID)
41 | }
42 | }
43 |
44 | // MARK: - Private Functions
45 |
46 | private extension UploadableExtractor {
47 | func fetchImage(for asset: PHAsset, completion: @escaping (Uploadable?, PHImageRequestID) -> Void) -> PHImageRequestID {
48 | return asset.fetchImage(forSize: PHImageManagerMaximumSize) { image, requestID in
49 | completion(image, requestID)
50 | }
51 | }
52 |
53 | func fetchVideo(for asset: PHAsset, completion: @escaping (Uploadable?, PHImageRequestID) -> Void) -> PHImageRequestID {
54 | var requestID: PHImageRequestID!
55 |
56 | requestID = imageManager.requestAVAsset(forVideo: asset, options: videoRequestOptions) { avAsset, _, _ in
57 | completion(avAsset, requestID)
58 | }
59 |
60 | return requestID
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Formatters/DurationNumberFormatter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DurationNumberFormatter.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 02/08/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class DurationFormatter: NumberFormatter, @unchecked Sendable {
12 | func string(from seconds: Double) -> String {
13 | let hours = Int(seconds / 3600)
14 | let minutes = Int(seconds.truncatingRemainder(dividingBy: 3600) / 60)
15 | let seconds = Int(seconds.truncatingRemainder(dividingBy: 60))
16 | if hours > 0 {
17 | return String(format: "%i:%02i:%02i", hours, minutes, seconds)
18 | } else {
19 | return String(format: "%i:%02i", minutes, seconds)
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Operations/AssetURLExtractorOperation.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssetURLExtractorOperation.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 08/07/2020.
6 | // Copyright © 2020 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Photos
11 | import UIKit
12 |
13 | class AssetURLExtractorOperation: BaseOperation<[URL]>, ProgressReporting, @unchecked Sendable {
14 | // MARK: - Internal Properties
15 |
16 | let assets: [PHAsset]
17 |
18 | // MARK: - Private Properties
19 |
20 | private let config: Config
21 |
22 | private(set) lazy var progress: Progress = {
23 | let progress = Progress(totalUnitCount: Int64(assets.count))
24 |
25 | progress.localizedDescription = "Processing \(assets.count) file(s)…"
26 |
27 | return progress
28 | }()
29 |
30 | private var imageRequestIDs: [PHImageRequestID] = []
31 | private var assetExportSessions: [AVAssetExportSession] = []
32 |
33 | private lazy var uploadableExtractor = UploadableExtractor()
34 |
35 | private lazy var urlExtractor: URLExtractor = {
36 | URLExtractor(imageExportPreset: config.imageURLExportPreset,
37 | videoExportPreset: config.videoExportPreset,
38 | cameraImageQuality: config.imageExportQuality)
39 | }()
40 |
41 | // MARK: - Lifecycle
42 |
43 | init(assets: [PHAsset], config: Config) {
44 | self.assets = assets
45 | self.config = config
46 | }
47 |
48 | // MARK: - Overrides
49 |
50 | override func main() {
51 | extract()
52 | }
53 |
54 | override func cancel() {
55 | super.cancel()
56 |
57 | for requestID in imageRequestIDs {
58 | PHImageManager.default().cancelImageRequest(requestID)
59 | }
60 |
61 | imageRequestIDs.removeAll()
62 |
63 | for exportSession in assetExportSessions {
64 | exportSession.cancelExport()
65 | }
66 |
67 | assetExportSessions.removeAll()
68 | }
69 | }
70 |
71 | // MARK: - Private Functions
72 |
73 | private extension AssetURLExtractorOperation {
74 | func extract() {
75 | var urls: [URL] = []
76 | var completed: Int = 0
77 |
78 | let markProgress: (URL?, PHImageRequestID) -> () = { url, id in
79 | completed += 1
80 |
81 | self.imageRequestIDs.removeAll { $0 == id }
82 | self.progress.completedUnitCount = Int64(completed)
83 |
84 | if let url = url {
85 | urls.append(url)
86 | }
87 |
88 | if self.imageRequestIDs.count == 0, !self.isCancelled {
89 | self.finish(with: .success(urls))
90 | }
91 | }
92 |
93 | for asset in assets {
94 | let requestID: PHImageRequestID? = uploadableExtractor.fetchUploadable(using: asset) { (uploadable, id) in
95 | guard !self.isCancelled else { return }
96 |
97 | switch uploadable {
98 | case let image as UIImage:
99 | if let url = self.urlExtractor.fetchURL(image: image) {
100 | markProgress(url, id)
101 | } else {
102 | markProgress(nil, id)
103 | }
104 | case let video as AVAsset:
105 | let exportSession = self.urlExtractor.fetchVideoURL(of: video) { url in
106 | markProgress(url, id)
107 | }
108 |
109 | if let exportSession = exportSession {
110 | self.assetExportSessions.append(exportSession)
111 | }
112 | default:
113 | break
114 | }
115 | }
116 |
117 | if let requestID = requestID {
118 | imageRequestIDs.append(requestID)
119 | }
120 | }
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Protocols/CloudRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudRequest.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/25/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | typealias CloudRequestCompletion = (_ appRedirectURL: URL?, _ response: CloudResponse) -> Void
13 |
14 | protocol CloudRequest {
15 | var token: String? { get }
16 | var provider: CloudProvider { get }
17 | var apiKey: String { get }
18 | var security: Security? { get }
19 |
20 | @discardableResult
21 | func perform(cloudService: CloudService, completionBlock: @escaping CloudRequestCompletion) -> URLSessionDataTask
22 |
23 | func getResults(from json: [String: Any]) -> [String: Any]?
24 | }
25 |
26 | extension CloudRequest {
27 | func getResults(from json: [String: Any]) -> [String: Any]? {
28 | return json[provider.description] as? [String: Any]
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Requests/FolderListRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FolderListRequest.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/25/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | final class FolderListRequest: CloudRequest, Cancellable {
13 | // MARK: - Properties
14 |
15 | let authCallbackURL: URL
16 | let apiKey: String
17 | let security: Security?
18 | let pageToken: String?
19 | let provider: CloudProvider
20 | let path: String
21 |
22 | private(set) var token: String?
23 | private weak var dataTask: URLSessionDataTask?
24 |
25 | // MARK: - Lifecyle Functions
26 |
27 | init(authCallbackURL: URL,
28 | apiKey: String,
29 | security: Security? = nil,
30 | token: String? = nil,
31 | pageToken: String? = nil,
32 | provider: CloudProvider,
33 | path: String) {
34 | self.authCallbackURL = authCallbackURL
35 | self.apiKey = apiKey
36 | self.security = security
37 | self.token = token
38 | self.pageToken = pageToken
39 | self.provider = provider
40 | self.path = path
41 | }
42 |
43 | // MARK: - Cancellable Protocol Implementation
44 |
45 | @discardableResult func cancel() -> Bool {
46 | guard let dataTask = dataTask else { return false }
47 | dataTask.cancel()
48 |
49 | return true
50 | }
51 |
52 | // MARK: - Internal Functions
53 |
54 | func perform(cloudService: CloudService, completionBlock: @escaping CloudRequestCompletion) -> URLSessionDataTask {
55 | let request = cloudService.folderListRequest(provider: provider,
56 | path: path,
57 | authCallbackURL: authCallbackURL,
58 | apiKey: apiKey,
59 | security: security,
60 | token: token,
61 | pageToken: pageToken)
62 |
63 | let task = URLSession.filestackDefault.dataTask(with: request) { (data, response, error) in
64 | // Parse JSON, or return early with error if unable to parse.
65 | guard let data = data, let json = data.parseJSON() else {
66 | let response = FolderListResponse(error: error)
67 |
68 | DispatchQueue.main.async { completionBlock(nil, response) }
69 |
70 | return
71 | }
72 |
73 | // Store any token we receive so we can use it next time.
74 | self.token = json["token"] as? String
75 |
76 | if let authURL = self.getAuthURL(from: json) {
77 | // Auth is required — redirect to authentication URL
78 | let response = FolderListResponse(authURL: authURL)
79 |
80 | DispatchQueue.main.async { completionBlock(self.authCallbackURL, response) }
81 | } else if let results = self.getResults(from: json) {
82 | // Results received — return response with contents, and, optionally next token
83 | let contents = results["contents"] as? [[String: Any]]
84 | let nextToken: String? = self.token(from: results["next"] as? String)
85 | let response = FolderListResponse(contents: contents, nextToken: nextToken, error: error)
86 |
87 | DispatchQueue.main.async { completionBlock(nil, response) }
88 | } else {
89 | let response = FolderListResponse(contents: nil, nextToken: nil, error: error)
90 |
91 | DispatchQueue.main.async { completionBlock(nil, response) }
92 | }
93 | }
94 |
95 | dataTask = task
96 |
97 | task.resume()
98 |
99 | return task
100 | }
101 |
102 | private func token(from string: String?) -> String? {
103 | guard let string = string, !string.isEmpty else { return nil }
104 | return string
105 | }
106 |
107 | // MARK: - Private Functions
108 |
109 | func getAuthURL(from json: [String: Any]) -> URL? {
110 | guard let providerJSON = json[provider.description] as? [String: Any] else { return nil }
111 | guard let authJSON = providerJSON["auth"] as? [String: Any] else { return nil }
112 | guard let redirectURLString = authJSON["redirect_url"] as? String else { return nil }
113 |
114 | return URL(string: redirectURLString)
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Requests/LogoutRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LogoutRequest.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/21/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | final class LogoutRequest {
12 | // MARK: - Properties
13 |
14 | let provider: CloudProvider
15 | let apiKey: String
16 | let token: String
17 |
18 | // MARK: - Lifecyle Functions
19 |
20 | init(provider: CloudProvider, apiKey: String, token: String) {
21 | self.provider = provider
22 | self.apiKey = apiKey
23 | self.token = token
24 | }
25 |
26 | // MARK: - Internal Functions
27 |
28 | func perform(cloudService: CloudService, completionBlock: @escaping LogoutCompletionHandler) {
29 | let request = cloudService.logoutRequest(provider: provider, apiKey: apiKey, token: token)
30 |
31 | let task = URLSession.filestackDefault.dataTask(with: request) { (data, response, error) in
32 | let response = LogoutResponse(error: error)
33 |
34 | completionBlock(response)
35 | }
36 |
37 | task.resume()
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Requests/PrefetchRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrefetchRequest.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/8/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | final class PrefetchRequest {
12 | // MARK: - Properties
13 |
14 | let apiKey: String
15 |
16 | // MARK: - Lifecyle Functions
17 |
18 | init(apiKey: String) {
19 | self.apiKey = apiKey
20 | }
21 |
22 | // MARK: - Internal Functions
23 |
24 | func perform(cloudService: CloudService, completionBlock: @escaping PrefetchCompletionHandler) {
25 | let request = cloudService.prefetchRequest(apiKey: apiKey)
26 |
27 | let task = URLSession.filestackDefault.dataTask(with: request) { (data, response, error) in
28 | // Parse JSON, or return early with error if unable to parse.
29 | guard let data = data, let json = data.parseJSON() else {
30 | let response = PrefetchResponse(error: error)
31 |
32 | DispatchQueue.main.async { completionBlock(response) }
33 |
34 | return
35 | }
36 |
37 | // Results received — return response with contents
38 | let response = PrefetchResponse(contents: json, error: error)
39 |
40 | DispatchQueue.main.async { completionBlock(response) }
41 | }
42 |
43 | task.resume()
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Requests/StoreRequest.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoreRequest.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/27/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | final class StoreRequest: CloudRequest, Cancellable, Monitorizable {
13 | // MARK: - Properties
14 |
15 | let apiKey: String
16 | let security: Security?
17 | let provider: CloudProvider
18 | let path: String
19 | let storeOptions: StorageOptions
20 |
21 | private(set) var token: String?
22 | private weak var dataTask: URLSessionDataTask?
23 |
24 | let progress: Progress = {
25 | let progress = Progress(totalUnitCount: 0)
26 |
27 | progress.localizedDescription = "Storing file in storage location…"
28 | progress.localizedAdditionalDescription = ""
29 |
30 | return progress
31 | }()
32 |
33 | // MARK: - Lifecyle Functions
34 |
35 | init(apiKey: String,
36 | security: Security? = nil,
37 | token: String? = nil,
38 | provider: CloudProvider,
39 | path: String,
40 | storeOptions: StorageOptions) {
41 | self.apiKey = apiKey
42 | self.security = security
43 | self.token = token
44 | self.provider = provider
45 | self.path = path
46 | self.storeOptions = storeOptions
47 | }
48 |
49 | // MARK: - Cancellable Protocol Implementation
50 |
51 | @discardableResult func cancel() -> Bool {
52 | guard let dataTask = dataTask else { return false }
53 | dataTask.cancel()
54 |
55 | return true
56 | }
57 |
58 | // MARK: - Internal Functions
59 |
60 | func perform(cloudService: CloudService, completionBlock: @escaping CloudRequestCompletion) -> URLSessionDataTask {
61 | let request = cloudService.storeRequest(provider: provider,
62 | path: path,
63 | apiKey: apiKey,
64 | security: security,
65 | token: token,
66 | storeOptions: storeOptions)
67 |
68 | let task = URLSession.filestackDefault.dataTask(with: request) { (data, response, error) in
69 | // Parse JSON, or return early with error if unable to parse.
70 | guard let data = data, let json = data.parseJSON() else {
71 | let response = StoreResponse(error: error)
72 |
73 | DispatchQueue.main.async { completionBlock(nil, response) }
74 |
75 | return
76 | }
77 |
78 | // Store any token we receive so we can use it next time.
79 | self.token = json["token"] as? String
80 |
81 | if let results = self.getResults(from: json) {
82 | // Results received — return response with contents
83 | let response = StoreResponse(contents: results, error: error)
84 |
85 | DispatchQueue.main.async { completionBlock(nil, response) }
86 | }
87 | }
88 |
89 | dataTask = task
90 |
91 | task.resume()
92 |
93 | return task
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/Services/CloudService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudService.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/24/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | class CloudService {
13 | let session = URLSession.filestackDefault
14 | let baseURL = Constants.cloudURL
15 |
16 | func folderListRequest(provider: CloudProvider,
17 | path: String,
18 | authCallbackURL: URL,
19 | apiKey: String,
20 | security: Security? = nil,
21 | token: String? = nil,
22 | pageToken: String? = nil) -> URLRequest {
23 | let url = baseURL.appendingPathComponent("folder/list")
24 |
25 | var params: [String: Any] = [
26 | "apikey": apiKey,
27 | "appurl": authCallbackURL.absoluteString,
28 | "flow": "mobile",
29 | ]
30 |
31 | if let token = token {
32 | params["token"] = token
33 | }
34 |
35 | if let pageToken = pageToken {
36 | params["clouds"] = [
37 | provider.description: [
38 | "path": path,
39 | "next": pageToken,
40 | ],
41 | ]
42 | } else {
43 | params["clouds"] = [
44 | provider.description: [
45 | "path": path,
46 | ],
47 | ]
48 | }
49 |
50 | if let security = security {
51 | params["policy"] = security.encodedPolicy
52 | params["signature"] = security.signature
53 | }
54 |
55 | return session.jsonRequest(url, payload: params)
56 | }
57 |
58 | func storeRequest(provider: CloudProvider,
59 | path: String,
60 | apiKey: String,
61 | security: Security? = nil,
62 | token: String? = nil,
63 | storeOptions: StorageOptions) -> URLRequest {
64 | let url = baseURL.appendingPathComponent("store/")
65 |
66 | var params: [String: Any] = [
67 | "apikey": apiKey,
68 | "flow": "mobile",
69 | "clouds": [
70 | provider.description: [
71 | "path": path,
72 | "store": storeOptions.asDictionary(),
73 | ],
74 | ]
75 | ]
76 |
77 | if let token = token {
78 | params["token"] = token
79 | }
80 |
81 | if let security = security {
82 | params["policy"] = security.encodedPolicy
83 | params["signature"] = security.signature
84 | }
85 |
86 | return session.jsonRequest(url, payload: params)
87 | }
88 |
89 | func prefetchRequest(apiKey: String) -> URLRequest {
90 | let url = baseURL.appendingPathComponent("prefetch")
91 | let params: [String: Any] = ["apikey": apiKey]
92 |
93 | return session.jsonRequest(url, payload: params)
94 | }
95 |
96 | func logoutRequest(provider: CloudProvider, apiKey: String, token: String) -> URLRequest {
97 | let url = baseURL.appendingPathComponent("auth/logout")
98 |
99 | let params: [String: Any] = [
100 | "apikey": apiKey,
101 | "token": token,
102 | "flow": "mobile",
103 | "clouds": [
104 | provider.description: [:],
105 | ],
106 | ]
107 |
108 | return session.jsonRequest(url, payload: params)
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Sources/Filestack/Internal/TrackingProgress.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TrackingProgress.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 07/07/2020.
6 | // Copyright © 2020 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class TrackingProgress: Progress, @unchecked Sendable {
12 | // MARK: - Private Properties
13 |
14 | private let lockQueue = DispatchQueue(label: "com.filestack.FilestackSDK.tracking-progress-lock-queue")
15 |
16 | private var observers: [NSKeyValueObservation] = []
17 |
18 | private var _tracked: Progress? {
19 | didSet {
20 | removeObservers()
21 |
22 | guard let progress = _tracked else { return }
23 |
24 | kind = progress.kind
25 | fileOperationKind = progress.fileOperationKind
26 | fileTotalCount = progress.fileTotalCount
27 | fileCompletedCount = progress.fileCompletedCount
28 | fileURL = progress.fileURL
29 | totalUnitCount = progress.totalUnitCount
30 | completedUnitCount = progress.completedUnitCount
31 |
32 | setupObservers()
33 | }
34 | }
35 |
36 | // MARK: - Lifecycle
37 |
38 | required init(tracked progress: Progress? = nil) {
39 | super.init(parent: nil, userInfo: nil)
40 |
41 | if let tracked = tracked {
42 | update(tracked: tracked)
43 | }
44 | }
45 | }
46 |
47 | // MARK: - Computed Properties
48 |
49 | private extension TrackingProgress {
50 | var tracked: Progress? {
51 | get { lockQueue.sync { _tracked } }
52 | set { lockQueue.sync { _tracked = newValue } }
53 | }
54 | }
55 |
56 | // MARK: - Internal Functions
57 |
58 | extension TrackingProgress {
59 | func update(tracked progress: Progress?, delay: Double = 0) {
60 | let delay: Double = tracked == nil ? 0 : delay // ignore `delay` argument if there's currently no tracked progress.
61 |
62 | DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
63 | guard !self.isCancelled else { return }
64 | self.tracked = progress
65 | }
66 | }
67 |
68 | override func cancel() {
69 | super.cancel()
70 |
71 | tracked = nil
72 | }
73 | }
74 |
75 | // MARK: - Progress Overrides
76 |
77 | extension TrackingProgress {
78 | override var localizedDescription: String! {
79 | get { tracked?.localizedDescription ?? super.localizedDescription }
80 | set { /* */ }
81 | }
82 |
83 | override var localizedAdditionalDescription: String! {
84 | get { tracked?.localizedAdditionalDescription ?? super.localizedAdditionalDescription }
85 | set { /* */ }
86 | }
87 | }
88 |
89 | // MARK: - Private Functions
90 |
91 | private extension TrackingProgress {
92 | func setupObservers() {
93 | guard let tracked = _tracked else { return }
94 |
95 | observers.append(tracked.observe(\.totalUnitCount, options: [.new]) { (progress, change) in
96 | self.totalUnitCount = progress.totalUnitCount
97 | })
98 |
99 | observers.append(tracked.observe(\.completedUnitCount, options: [.new]) { (progress, change) in
100 | self.completedUnitCount = progress.completedUnitCount
101 | })
102 | }
103 |
104 | func removeObservers() {
105 | observers.removeAll()
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/CompletionHandlers.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CompletionHandlers.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// :nodoc:
12 | public typealias FolderListCompletionHandler = (_ response: FolderListResponse) -> Void
13 |
14 | /// :nodoc:
15 | public typealias StoreCompletionHandler = (_ response: StoreResponse) -> Void
16 |
17 | /// :nodoc:
18 | public typealias LogoutCompletionHandler = (_ response: LogoutResponse) -> Void
19 |
20 | /// :nodoc:
21 | public typealias PrefetchCompletionHandler = (_ response: PrefetchResponse) -> Void
22 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Enums/ClientError.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Errors.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 16/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// A specific kind of `Error` that may be returned by the `Client`.
12 | public enum ClientError: Error {
13 | /// Authentication failed.
14 | case authenticationFailed
15 | }
16 |
17 | extension ClientError: LocalizedError {
18 | public var errorDescription: String? {
19 | switch self {
20 | case .authenticationFailed:
21 | return NSLocalizedString("Unable to authenticate.", comment: "")
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Enums/CloudProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudProvider.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 10/26/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Represents a cloud provider.
12 | @objc(FSCloudProvider) public enum CloudProvider: UInt, CustomStringConvertible {
13 | /// Facebook
14 | case facebook
15 |
16 | /// Instagram
17 | case instagram
18 |
19 | /// Google Drive
20 | case googleDrive
21 |
22 | /// Dropbox
23 | case dropbox
24 |
25 | /// Box
26 | case box
27 |
28 | /// GitHub
29 | case gitHub
30 |
31 | /// Gmail
32 | case gmail
33 |
34 | /// Google Photos
35 | case googlePhotos
36 |
37 | /// OneDrive
38 | case oneDrive
39 |
40 | /// Amazon Drive
41 | case amazonDrive
42 |
43 | /// Unsplash
44 | case unsplash
45 |
46 | /// Custom Source
47 | case customSource
48 | }
49 |
50 | extension CloudProvider {
51 | /// :nodoc:
52 | public var searchBased: Bool {
53 | switch self {
54 | case .unsplash:
55 | return true
56 | default:
57 | return false
58 | }
59 | }
60 |
61 | /// :nodoc:
62 | var viewType: CloudSourceViewType? {
63 | switch self {
64 | case .unsplash:
65 | return .grid
66 | default:
67 | return nil
68 | }
69 | }
70 | }
71 |
72 | extension CloudProvider {
73 | /// :nodoc:
74 | public var description: String {
75 | switch self {
76 | case .facebook:
77 | return "facebook"
78 | case .instagram:
79 | return "instagram"
80 | case .googleDrive:
81 | return "googledrive"
82 | case .dropbox:
83 | return "dropbox"
84 | case .box:
85 | return "box"
86 | case .gitHub:
87 | return "github"
88 | case .gmail:
89 | return "gmail"
90 | case .googlePhotos:
91 | return "picasa"
92 | case .oneDrive:
93 | return "onedrive"
94 | case .amazonDrive:
95 | return "clouddrive"
96 | case .unsplash:
97 | return "unsplash"
98 | case .customSource:
99 | return "customsource"
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Enums/ImageURLExportPreset.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageURLExportPreset.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/22/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Represents an image URL export preset.
12 | @objc(FSImageURLExportPreset) public enum ImageURLExportPreset: Int {
13 | /// A preset for converting HEIF formatted images to JPEG.
14 | case compatible
15 |
16 | /// A preset for passing image data as-is to the client.
17 | case current
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Enums/PhotosPickerFilter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotosPickerFilter.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 15/10/2020.
6 | // Copyright © 2020 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import PhotosUI
11 |
12 | /// Represents a cloud provider.
13 | @objc(FSPhotosPickerFilter) public enum PhotosPickerFilter: UInt {
14 | /// The filter for images.
15 | case images
16 |
17 | /// The filter for live photos.
18 | case livePhotos
19 |
20 | /// The filter for videos.
21 | case videos
22 | }
23 |
24 | @available(iOS 14.0, *)
25 | extension PhotosPickerFilter {
26 | var asPHFilter: PHPickerFilter {
27 | switch self {
28 | case .images:
29 | return .images
30 | case .livePhotos:
31 | return .livePhotos
32 | case .videos:
33 | return .videos
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Enums/PickerBehavior.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PickerBehavior.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/2/21.
6 | // Copyright © 2021 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import FilestackSDK
11 |
12 | /// Represents the picker's pick behavior.
13 | public enum PickerBehavior: Equatable {
14 | /// After finishing picking, local files are uploaded and cloud files are stored at the store destination.
15 | case uploadAndStore(uploadOptions: UploadOptions)
16 |
17 | /// After finishing picking, only cloud files are stored at the store destination.
18 | case storeOnly
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Responses/CloudResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudResponse.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// :nodoc:
12 | @objc(FSCloudResponse) public protocol CloudResponse {
13 | @objc var error: Error? { get }
14 | @objc var authURL: URL? { get }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Responses/FolderListResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FolderListResponse.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// This class represents a response obtained from a folder list request.
12 | @objc(FSFolderListResponse) public class FolderListResponse: NSObject, CloudResponse {
13 | // MARK: - Properties
14 |
15 | /// The contents payload as an array of dictionaries, where each dictionary represents an entry in the cloud.
16 | @objc public let contents: [[String: Any]]?
17 |
18 | /// A next token used for pagination purposes. Optional.
19 | @objc public let nextToken: String?
20 |
21 | /// A redirect URL to a cloud provider's OAuth page. Typically this is only required internally.
22 | @objc public let authURL: URL?
23 |
24 | /// An error response. Optional.
25 | @objc public let error: Error?
26 |
27 | // MARK: - Lifecyle Functions
28 |
29 | init(contents: [[String: Any]]? = nil,
30 | nextToken: String? = nil,
31 | authURL: URL? = nil,
32 | error: Error? = nil) {
33 | self.contents = contents
34 | self.nextToken = nextToken
35 | self.authURL = authURL
36 | self.error = error
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Responses/LogoutResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LogoutResponse.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// This class represents a response obtained from a logout request.
12 | @objc(FSLogoutResponse) public class LogoutResponse: NSObject {
13 | // MARK: - Properties
14 |
15 | /// An error response. Optional.
16 | @objc public let error: Error?
17 |
18 | // MARK: - Lifecyle Functions
19 |
20 | init(error: Error? = nil) {
21 | self.error = error
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Responses/PrefetchResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PrefetchResponse.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// :nodoc:
12 | @objc(FSPrefetchResponse) public class PrefetchResponse: NSObject {
13 | // MARK: - Properties
14 |
15 | @objc public let contents: [String: Any]?
16 | @objc public let error: Error?
17 |
18 | // MARK: - Lifecyle Functions
19 |
20 | init(contents: [String: Any]? = nil, error: Error? = nil) {
21 | self.contents = contents
22 | self.error = error
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Filestack/Public/Responses/StoreResponse.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StoreResponse.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// This class represents a response obtained from a store request.
12 | @objc(FSStoreResponse) public class StoreResponse: NSObject, CloudResponse {
13 | // MARK: - Properties
14 |
15 | /// The contents payload as a dictionary containing details about the operation response.
16 | @objc public let contents: [String: Any]?
17 |
18 | /// A redirect URL to a cloud provider's OAuth page. Typically this is only required internally.
19 | @objc public let authURL: URL? = nil
20 |
21 | /// An error response. Optional.
22 | @objc public let error: Error?
23 |
24 | // MARK: - Lifecyle Functions
25 |
26 | init(contents: [String: Any]? = nil, error: Error? = nil) {
27 | self.contents = contents
28 | self.error = error
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Colors.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | }
6 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Colors.xcassets/SelectionCellBorderColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "version" : 1,
4 | "author" : "xcode"
5 | },
6 | "colors" : [
7 | {
8 | "idiom" : "universal",
9 | "color" : {
10 | "color-space" : "srgb",
11 | "components" : {
12 | "red" : "0.792",
13 | "alpha" : "1.000",
14 | "blue" : "0.847",
15 | "green" : "0.808"
16 | }
17 | }
18 | },
19 | {
20 | "idiom" : "universal",
21 | "appearances" : [
22 | {
23 | "appearance" : "luminosity",
24 | "value" : "dark"
25 | }
26 | ],
27 | "color" : {
28 | "color-space" : "srgb",
29 | "components" : {
30 | "red" : "0.400",
31 | "alpha" : "1.000",
32 | "blue" : "0.455",
33 | "green" : "0.416"
34 | }
35 | }
36 | }
37 | ]
38 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/clear-pattern.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "clear-pattern.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/clear-pattern.imageset/clear-pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/clear-pattern.imageset/clear-pattern.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/file.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "file.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "file@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/file.imageset/file.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/file.imageset/file.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/file.imageset/file@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/file.imageset/file@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-box.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-box.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-box@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-box.imageset/icon-box.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-box.imageset/icon-box.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-box.imageset/icon-box@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-box.imageset/icon-box@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-camera.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-camera.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-camera@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-camera.imageset/icon-camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-camera.imageset/icon-camera.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-camera.imageset/icon-camera@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-camera.imageset/icon-camera@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-circle.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "circle_icon.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-circle.imageset/circle_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-circle.imageset/circle_icon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-clouddrive.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-clouddrive.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-clouddrive@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-clouddrive.imageset/icon-clouddrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-clouddrive.imageset/icon-clouddrive.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-clouddrive.imageset/icon-clouddrive@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-clouddrive.imageset/icon-clouddrive@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-crop.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "crop_icon.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-crop.imageset/crop_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-crop.imageset/crop_icon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-customsource.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-customsource.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-customsource@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-customsource.imageset/icon-customsource.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-customsource.imageset/icon-customsource.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-customsource.imageset/icon-customsource@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-customsource.imageset/icon-customsource@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-documents.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "documents.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "documents@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-documents.imageset/documents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-documents.imageset/documents.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-documents.imageset/documents@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-documents.imageset/documents@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-dropbox.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-dropbox.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-dropbox@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-dropbox.imageset/icon-dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-dropbox.imageset/icon-dropbox.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-dropbox.imageset/icon-dropbox@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-dropbox.imageset/icon-dropbox@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-edit.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-pencil.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-edit.imageset/icon-pencil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-edit.imageset/icon-pencil.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-facebook.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-facebook.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-facebook@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-facebook.imageset/icon-facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-facebook.imageset/icon-facebook.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-facebook.imageset/icon-facebook@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-facebook.imageset/icon-facebook@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-image.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-image.imageset/icon-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-file-image.imageset/icon-image.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-pdf.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-file-pdf.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-pdf.imageset/icon-file-pdf.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-file-pdf.imageset/icon-file-pdf.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-unknown.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-file-unknown.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-unknown.imageset/icon-file-unknown.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-file-unknown.imageset/icon-file-unknown.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-video.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-video-camera.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-file-video.imageset/icon-video-camera.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-file-video.imageset/icon-video-camera.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-github.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-github.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-github@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-github.imageset/icon-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-github.imageset/icon-github.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-github.imageset/icon-github@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-github.imageset/icon-github@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-gmail.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-gmail.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-gmail@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-gmail.imageset/icon-gmail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-gmail.imageset/icon-gmail.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-gmail.imageset/icon-gmail@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-gmail.imageset/icon-gmail@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-googledrive.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-googledrive.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-googledrive@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-googledrive.imageset/icon-googledrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-googledrive.imageset/icon-googledrive.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-googledrive.imageset/icon-googledrive@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-googledrive.imageset/icon-googledrive@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-grid.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-grid@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "icon-grid@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-grid.imageset/icon-grid@3x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-image.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon-image.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-image.imageset/icon-image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-image.imageset/icon-image.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-instagram.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-instagram.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-instagram@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-instagram.imageset/icon-instagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-instagram.imageset/icon-instagram.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-instagram.imageset/icon-instagram@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-instagram.imageset/icon-instagram@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-list.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-list@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "icon-list@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-list.imageset/icon-list@3x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-logout.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-logout@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "icon-logout@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-logout.imageset/icon-logout@3x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-onedrive.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-skydrive.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-skydrive@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-onedrive.imageset/icon-skydrive.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-onedrive.imageset/icon-skydrive.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-onedrive.imageset/icon-skydrive@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-onedrive.imageset/icon-skydrive@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-photolibrary.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-albums.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-albums@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-photolibrary.imageset/icon-albums.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-photolibrary.imageset/icon-albums.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-photolibrary.imageset/icon-albums@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-photolibrary.imageset/icon-albums@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-picasa.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "icon-picasa.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "icon-picasa@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "version" : 1,
20 | "author" : "xcode"
21 | }
22 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-picasa.imageset/icon-picasa.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-picasa.imageset/icon-picasa.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-picasa.imageset/icon-picasa@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-picasa.imageset/icon-picasa@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-redo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "redo_icon.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-redo.imageset/redo_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-redo.imageset/redo_icon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-rotate.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "rotate_icon.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-rotate.imageset/rotate_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-rotate.imageset/rotate_icon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "photoIcon.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "photoIcon-1.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "photoIcon-2.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon-1.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon-2.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-selected.imageset/photoIcon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-tick.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "checkmark-solid.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-tick.imageset/checkmark-solid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-tick.imageset/checkmark-solid.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-undo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "undo_icon.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-undo.imageset/undo_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-undo.imageset/undo_icon.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-unsplash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "icon-unsplash.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "filename" : "icon-unsplash@2x.png",
10 | "idiom" : "universal",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "scale" : "3x"
16 | }
17 | ],
18 | "info" : {
19 | "author" : "xcode",
20 | "version" : 1
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-unsplash.imageset/icon-unsplash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-unsplash.imageset/icon-unsplash.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-unsplash.imageset/icon-unsplash@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-unsplash.imageset/icon-unsplash@2x.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-upload.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "scale" : "1x"
6 | },
7 | {
8 | "idiom" : "universal",
9 | "scale" : "2x"
10 | },
11 | {
12 | "idiom" : "universal",
13 | "filename" : "icon_upload.png",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/icon-upload.imageset/icon_upload.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/icon-upload.imageset/icon_upload.png
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/placeholder.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "placeholder.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "version" : 1,
19 | "author" : "xcode"
20 | }
21 | }
--------------------------------------------------------------------------------
/Sources/Filestack/Resources/Icons.xcassets/placeholder.imageset/placeholder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filestack/filestack-ios/e1d9ce15e3e0bc904d5323f9670388df0d36a4a0/Sources/Filestack/Resources/Icons.xcassets/placeholder.imageset/placeholder.png
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Collection View Cells/ActivityIndicatorCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivityIndicatorCollectionViewCell.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ActivityIndicatorCollectionViewCell: UICollectionViewCell {
12 | @IBOutlet var activityIndicator: UIActivityIndicatorView!
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Collection View Cells/CloudItemCollectionViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudItemCollectionViewCell.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CloudItemCollectionViewCell: UICollectionViewCell {
12 | @IBOutlet var imageView: UIImageView!
13 | @IBOutlet var label: UILabel!
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Controllers/CustomPickerUploadController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomPickerUploadController.swift
3 | // CustomPickerUploadController
4 | //
5 | // Created by Ruben Nine on 26/7/21.
6 | // Copyright © 2021 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import UIKit
11 |
12 | class CustomPickerUploadController: URLPickerUploadController {
13 | let navigationVC: UINavigationController
14 | let provider: SourceProvider
15 |
16 | init(uploader: (Uploader & DeferredAdd)?,
17 | viewController: UIViewController,
18 | provider: SourceProvider,
19 | config: Config,
20 | context: Any? = nil,
21 | completionBlock: (([URL]) -> Void)? = nil) {
22 |
23 | self.navigationVC = UINavigationController(rootViewController: provider)
24 | self.provider = provider
25 |
26 | super.init(uploader: uploader, viewController: viewController, presentedViewController: navigationVC, config: config, completionBlock: completionBlock)
27 |
28 | provider.sourceProviderDelegate = self
29 | }
30 | }
31 |
32 | extension CustomPickerUploadController: SourceProviderDelegate {
33 | func sourceProviderPicked(urls: [URL]) {
34 | upload(urls: urls)
35 | }
36 |
37 | func sourceProviderCancelled() {
38 | cancel()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Controllers/DocumentPickerUploadController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DocumentPickerUploadController.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 12/1/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import UIKit
11 | import UniformTypeIdentifiers
12 |
13 | class DocumentPickerUploadController: URLPickerUploadController {
14 | let picker: UIDocumentPickerViewController
15 |
16 | init(uploader: (Uploader & DeferredAdd)?,
17 | viewController: UIViewController,
18 | config: Config,
19 | completionBlock: (([URL]) -> Void)? = nil) {
20 | let allowedContentTypes = config.documentPickerAllowedUTIs.compactMap { UTIString in
21 | if let contentType = UTType(UTIString) {
22 | return contentType
23 | } else {
24 | return nil
25 | }
26 | }
27 | self.picker = UIDocumentPickerViewController(forOpeningContentTypes: allowedContentTypes.isEmpty ? [.item] : allowedContentTypes, asCopy: true)
28 | super.init(uploader: uploader,
29 | viewController: viewController,
30 | presentedViewController: picker,
31 | config: config,
32 | completionBlock: completionBlock)
33 |
34 | self.picker.delegate = self
35 | }
36 | }
37 |
38 | extension DocumentPickerUploadController: UIDocumentPickerDelegate {
39 | // called if the user dismisses the document picker without selecting a document (using the Cancel button)
40 | func documentPickerWasCancelled(_: UIDocumentPickerViewController) {
41 | cancel()
42 | }
43 |
44 | // Required
45 | func documentPicker(_: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
46 | completionBlock?(urls)
47 |
48 | if uploader != nil {
49 | upload(urls: urls)
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Enums/CloudSourceViewType.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudSourceViewType.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | enum CloudSourceViewType: Int {
12 | case list = 0
13 | case grid = 1
14 | }
15 |
16 | extension CloudSourceViewType {
17 | var iconName: String {
18 | switch self {
19 | case .list:
20 | return "icon-list"
21 | case .grid:
22 | return "icon-grid"
23 | }
24 | }
25 |
26 | func toggle() -> CloudSourceViewType {
27 | switch self {
28 | case .list:
29 | return .grid
30 | case .grid:
31 | return .list
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Extensions/Scene+Defaults.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Scene+Defaults.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/13/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension Scene {
12 | var identifier: String {
13 | return String(describing: ViewController.self)
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Extensions/Storyboard+Scenes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Storyboard+ instantiateViewController.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/13/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIStoryboard {
12 | func instantiateViewController(for scene: S) -> S.ViewController {
13 | guard let viewController = instantiateViewController(withIdentifier: scene.identifier) as? S.ViewController else {
14 | fatalError("expected view controller with identifier '\(scene.identifier)' to be of type '\(String(describing: S.ViewController.self))'")
15 | }
16 |
17 | scene.configureViewController(viewController)
18 |
19 | return viewController
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Extensions/UIImage+Squared.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage+Squared.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/14/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension UIImage {
12 | var isPortrait: Bool { return size.height > size.width }
13 | var isLandscape: Bool { return size.width > size.height }
14 | var breadth: CGFloat { return min(size.width, size.height) }
15 | var breadthSize: CGSize { return CGSize(width: breadth, height: breadth) }
16 | var breadthRect: CGRect { return CGRect(origin: .zero, size: breadthSize) }
17 |
18 | var squared: UIImage? {
19 | if size.width == size.height {
20 | // Already square. Return self
21 | return self
22 | }
23 |
24 | UIGraphicsBeginImageContextWithOptions(breadthSize, false, scale)
25 | defer { UIGraphicsEndImageContext() }
26 |
27 | let cropX = isLandscape ? floor((size.width - size.height) / 2) : 0
28 | let cropY = isPortrait ? floor((size.height - size.width) / 2) : 0
29 | let cropRect = CGRect(origin: CGPoint(x: cropX, y: cropY), size: breadthSize)
30 |
31 | guard let cgImage = cgImage?.cropping(to: cropRect) else { return nil }
32 |
33 | UIImage(cgImage: cgImage).draw(in: breadthRect)
34 |
35 | return UIGraphicsGetImageFromCurrentImageContext()
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Extensions/UserDefaults+State.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UserDefaults+State.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | private extension String {
12 | static let cloudSourceViewType = "FSCloudSourceViewType"
13 | }
14 |
15 | extension UserDefaults {
16 | func cloudSourceViewType() -> CloudSourceViewType? {
17 | return CloudSourceViewType(rawValue: integer(forKey: .cloudSourceViewType))
18 | }
19 |
20 | func set(cloudSourceViewType: CloudSourceViewType) {
21 | setValue(cloudSourceViewType.rawValue, forKeyPath: .cloudSourceViewType)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/FlowLayouts/CollectionViewFlowLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionViewFlowLayout.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 17/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CollectionViewFlowLayout: UICollectionViewFlowLayout {
12 | /// The default implementation of this method returns false.
13 | /// Subclasses can override it and return an appropriate value
14 | /// based on whether changes in the bounds of the collection
15 | /// view require changes to the layout of cells and supplementary views.
16 | /// If the bounds of the collection view change and this method returns true,
17 | /// the collection view invalidates the layout by calling the invalidateLayout(with:) method.
18 | override func shouldInvalidateLayout(forBoundsChange newBounds: CGRect) -> Bool {
19 | return (self.collectionView?.bounds ?? newBounds) == newBounds
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Models/CloudItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudItem.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/10/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | struct CloudItem {
12 | let isFolder: Bool
13 | let name: String
14 | let path: String
15 | let thumbnailURL: URL
16 |
17 | init?(dictionary: [String: Any]) {
18 | guard let isFolder = dictionary["folder"] as? Bool,
19 | let name = dictionary["name"] as? String,
20 | let path = dictionary["path"] as? String,
21 | let thumbnailURLString = dictionary["thumbnail"] as? String,
22 | let thumbnailURL = URL(string: thumbnailURLString) else {
23 | return nil
24 | }
25 |
26 | self.isFolder = isFolder
27 | self.name = name
28 |
29 | if isFolder {
30 | // Ensure items representing folders contain a trailing slash.
31 | // Sometimes, results from some providers (e.g. using GitHub) do not include it.
32 | self.path = path.last == "/" ? path : "\(path)/"
33 | } else {
34 | self.path = path
35 | }
36 |
37 | self.thumbnailURL = thumbnailURL
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/Enums/ImageEditorCommand.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEditorCommand.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/4/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | enum ImageEditorCommand {
12 | case rotate(clockwise: Bool)
13 | case crop(insets: UIEdgeInsets)
14 | case circled(center: CGPoint, radius: CGFloat)
15 | case undo
16 | case redo
17 | case reset
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/ImageEditor.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEditor.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/4/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit.UIImage
10 |
11 | class ImageEditor {
12 | private var editedImages: [UIImage] = []
13 | private var undoneImages: [UIImage] = []
14 |
15 | public let originalImage: UIImage
16 |
17 | public var editedImage: UIImage {
18 | return editedImages.last ?? originalImage
19 | }
20 |
21 | init?(image: UIImage) {
22 | guard let ciImageBackedCopy = image.ciImageBackedCopy() else { return nil }
23 |
24 | originalImage = ciImageBackedCopy
25 | }
26 | }
27 |
28 | // MARK: - Image Transform Commands
29 |
30 | extension ImageEditor {
31 | func rotate(clockwise: Bool) {
32 | if let rotatedImage = editedImage.rotated(clockwise: clockwise) {
33 | editedImages.append(rotatedImage)
34 | undoneImages.removeAll()
35 | }
36 | }
37 |
38 | func crop(insets: UIEdgeInsets) {
39 | if let croppedImage = editedImage.cropped(by: insets) {
40 | editedImages.append(croppedImage)
41 | undoneImages.removeAll()
42 | }
43 | }
44 |
45 | func cropCircled(center: CGPoint, radius: CGFloat) {
46 | if let cropCircledImage = editedImage.circled(center: center, radius: radius) {
47 | editedImages.append(cropCircledImage)
48 | undoneImages.removeAll()
49 | }
50 | }
51 | }
52 |
53 | // MARK: - Image Undo, Redo & Reset Commands
54 |
55 | extension ImageEditor {
56 | func undo() {
57 | if canUndo() {
58 | undoneImages.append(editedImages.removeLast())
59 | }
60 | }
61 |
62 | func redo() {
63 | if canRedo() {
64 | editedImages.append(undoneImages.removeLast())
65 | }
66 | }
67 |
68 | func canUndo() -> Bool {
69 | return !editedImages.isEmpty
70 | }
71 |
72 | func canRedo() -> Bool {
73 | return !undoneImages.isEmpty
74 | }
75 |
76 | func reset() {
77 | editedImages.removeAll()
78 | undoneImages.removeAll()
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/Layers/CircleLayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CropLayer.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 12/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CircleLayer: CALayer {
12 | var imageFrame = CGRect.zero {
13 | didSet {
14 | updateSublayers()
15 | }
16 | }
17 |
18 | var circleRadius: CGFloat = 0 {
19 | didSet {
20 | updateSublayers()
21 | }
22 | }
23 |
24 | var circleCenter = CGPoint.zero {
25 | didSet {
26 | updateSublayers()
27 | }
28 | }
29 |
30 | override init() {
31 | super.init()
32 | addSublayer(outsideLayer)
33 | }
34 |
35 | required init?(coder _: NSCoder) {
36 | fatalError("init(coder:) has not been implemented")
37 | }
38 |
39 | private lazy var outsideLayer: CAShapeLayer = {
40 | let layer = CAShapeLayer()
41 | layer.path = outsidePath
42 | layer.fillRule = .evenOdd
43 | layer.backgroundColor = UIColor.black.cgColor
44 | layer.opacity = 0.7
45 | return layer
46 | }()
47 | }
48 |
49 | /// :nodoc:
50 | private extension CircleLayer {
51 | func updateSublayers() {
52 | outsideLayer.path = outsidePath
53 | }
54 |
55 | var outsidePath: CGPath {
56 | let origin = CGPoint(x: circleCenter.x - circleRadius, y: circleCenter.y - circleRadius)
57 | let rect = CGRect(origin: origin, size: CGSize(width: circleRadius * 2, height: circleRadius * 2))
58 | let path = UIBezierPath(ovalIn: rect)
59 | path.append(UIBezierPath(rect: imageFrame))
60 | return path.cgPath
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/Layers/CropLayer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CropLayer.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 12/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CropLayer: CALayer {
12 | var imageFrame = CGRect.zero {
13 | didSet {
14 | updateSublayers()
15 | }
16 | }
17 |
18 | var cropRect = CGRect.zero {
19 | didSet {
20 | updateSublayers()
21 | }
22 | }
23 |
24 | override init() {
25 | super.init()
26 | addSublayer(outsideLayer)
27 | addSublayer(gridLayer)
28 | addSublayer(cornersLayer)
29 | }
30 |
31 | required init?(coder _: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 |
35 | private lazy var outsideLayer: CAShapeLayer = {
36 | let layer = CAShapeLayer()
37 | layer.path = outsidePath
38 | layer.fillRule = .evenOdd
39 | layer.backgroundColor = UIColor.black.cgColor
40 | layer.opacity = 0.5
41 | return layer
42 | }()
43 |
44 | private lazy var gridLayer: CAShapeLayer = {
45 | let layer = CAShapeLayer()
46 | layer.path = gridPath
47 | layer.lineWidth = 0.5
48 | layer.strokeColor = UIColor.white.cgColor
49 | layer.backgroundColor = UIColor.clear.cgColor
50 | layer.fillColor = UIColor.clear.cgColor
51 | return layer
52 | }()
53 |
54 | private lazy var cornersLayer: CAShapeLayer = {
55 | let layer = CAShapeLayer()
56 | layer.path = cornersPath
57 | layer.lineWidth = 2
58 | layer.strokeColor = UIColor.white.cgColor
59 | layer.backgroundColor = UIColor.clear.cgColor
60 | layer.fillColor = UIColor.clear.cgColor
61 | return layer
62 | }()
63 | }
64 |
65 | /// :nodoc:
66 | private extension CropLayer {
67 | func updateSublayers() {
68 | outsideLayer.path = outsidePath
69 | gridLayer.path = gridPath
70 | cornersLayer.path = cornersPath
71 | }
72 |
73 | var outsidePath: CGPath {
74 | let path = UIBezierPath(rect: cropRect)
75 | path.append(UIBezierPath(rect: imageFrame))
76 | return path.cgPath
77 | }
78 |
79 | var gridPath: CGPath {
80 | let gridWidth = cropRect.size.width / 3
81 | let gridHeight = cropRect.size.height / 3
82 | let path = UIBezierPath(rect: cropRect)
83 | path.move(to: cropRect.origin.movedBy(x: gridWidth))
84 | path.addLine(to: path.currentPoint.movedBy(y: gridHeight * 3))
85 | path.move(to: cropRect.origin.movedBy(x: gridWidth * 2))
86 | path.addLine(to: path.currentPoint.movedBy(y: gridHeight * 3))
87 | path.move(to: cropRect.origin.movedBy(y: gridHeight))
88 | path.addLine(to: path.currentPoint.movedBy(x: gridWidth * 3))
89 | path.move(to: cropRect.origin.movedBy(y: gridHeight * 2))
90 | path.addLine(to: path.currentPoint.movedBy(x: gridWidth * 3))
91 | return path.cgPath
92 | }
93 |
94 | var cornersPath: CGPath {
95 | let thickness: CGFloat = 2
96 | let lenght: CGFloat = 20
97 | let horizontalWidth = min(lenght, cropRect.size.width) + thickness
98 | let verticalWidth = min(lenght, cropRect.size.height)
99 | let margin = UIEdgeInsets(top: -thickness / 2, left: -thickness / 2, bottom: -thickness / 2, right: -thickness / 2)
100 | let outerRect = cropRect.inset(by: margin)
101 | let path = UIBezierPath()
102 | path.move(to: outerRect.origin.movedBy(y: verticalWidth))
103 | path.addLine(to: path.currentPoint.movedBy(y: -verticalWidth))
104 | path.addLine(to: path.currentPoint.movedBy(x: horizontalWidth))
105 | path.move(to: outerRect.origin.movedBy(x: outerRect.size.width - horizontalWidth))
106 | path.addLine(to: path.currentPoint.movedBy(x: horizontalWidth))
107 | path.addLine(to: path.currentPoint.movedBy(y: verticalWidth))
108 | path.move(to: outerRect.origin.movedBy(x: outerRect.size.width, y: outerRect.size.height - verticalWidth))
109 | path.addLine(to: path.currentPoint.movedBy(y: verticalWidth))
110 | path.addLine(to: path.currentPoint.movedBy(x: -horizontalWidth))
111 | path.move(to: outerRect.origin.movedBy(y: outerRect.size.height - verticalWidth))
112 | path.addLine(to: path.currentPoint.movedBy(y: verticalWidth))
113 | path.addLine(to: path.currentPoint.movedBy(x: horizontalWidth))
114 | return path.cgPath
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/ViewController/EditorViewController+EditDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorViewController+EditDataSource.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 23/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import UIKit
11 |
12 | protocol EditDataSource: AnyObject {
13 | var imageFrame: CGRect { get }
14 | var imageSize: CGSize { get }
15 | var imageOrigin: CGPoint { get }
16 | var imageActualSize: CGSize { get }
17 | }
18 |
19 | extension EditorViewController: EditDataSource {
20 | var imageFrame: CGRect {
21 | return AVMakeRect(aspectRatio: imageActualSize, insideRect: imageView.bounds)
22 | }
23 |
24 | var imageSize: CGSize {
25 | return imageFrame.size
26 | }
27 |
28 | var imageOrigin: CGPoint {
29 | return imageFrame.origin
30 | }
31 |
32 | var imageActualSize: CGSize {
33 | return editor?.editedImage.size ?? .zero
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/ViewController/EditorViewController+ToolbarDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorViewController+ToolbarDelegate.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 23/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | extension EditorViewController: EditorToolbarDelegate {
12 | func cancelSelected() {
13 | dismiss(animated: true) {
14 | self.completion?(nil)
15 | }
16 | }
17 |
18 | func rotateSelected() {
19 | perform(command: .rotate(clockwise: false))
20 | cropHandler.rotateCounterClockwise()
21 | circleHandler.rotateCounterClockwise()
22 | }
23 |
24 | func cropSelected() {
25 | switch editMode {
26 | case .crop: editMode = .none
27 | case .circle, .none: editMode = .crop
28 | }
29 | }
30 |
31 | func circleSelected() {
32 | switch editMode {
33 | case .circle: editMode = .none
34 | case .crop, .none: editMode = .circle
35 | }
36 | }
37 |
38 | func saveSelected() {
39 | switch editMode {
40 | case .crop: perform(command: .crop(insets: cropHandler.actualEdgeInsets))
41 | case .circle: perform(command: .circled(center: circleHandler.actualCenter, radius: circleHandler.actualRadius))
42 | case .none: return
43 | }
44 |
45 | editMode = .none
46 | }
47 |
48 | func doneSelected() {
49 | dismiss(animated: true) {
50 | let editedImage = self.editor?.editedImage.cgImageBackedCopy()
51 | self.editor = nil
52 | self.completion?(editedImage)
53 | }
54 | }
55 |
56 | func undoSelected() {
57 | perform(command: .undo)
58 | }
59 |
60 | func redoSelected() {
61 | perform(command: .redo)
62 | }
63 |
64 | // MARK: - Private Functions
65 |
66 | private func perform(command: ImageEditorCommand) {
67 | guard let editor = editor else { return }
68 |
69 | switch command {
70 | case let .rotate(clockwise):
71 | editor.rotate(clockwise: clockwise)
72 | case let .crop(insets):
73 | editor.crop(insets: insets)
74 | case let .circled(center, radius):
75 | editor.cropCircled(center: center, radius: radius)
76 | case .undo:
77 | editor.undo()
78 | case .redo:
79 | editor.redo()
80 | case .reset:
81 | editor.reset()
82 | }
83 |
84 | imageView.image = editor.editedImage
85 | editMode = .none
86 | cropHandler.reset()
87 | circleHandler.reset()
88 | topToolbar.setActions(showUndo: editor.canUndo(), showRedo: editor.canRedo())
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/ViewController/EditorViewController+ViewSetup.swift:
--------------------------------------------------------------------------------
1 | //
2 | // EditorViewController+ViewSetup.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 23/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension EditorViewController {
12 | func setupGestureRecognizer() {
13 | panGestureRecognizer.delegate = self
14 | panGestureRecognizer.addTarget(self, action: #selector(handlePanGesture(recognizer:)))
15 | pinchGestureRecognizer.delegate = self
16 | pinchGestureRecognizer.addTarget(self, action: #selector(handlePinchGesture(recognizer:)))
17 | imageView.isUserInteractionEnabled = true
18 | imageView.addGestureRecognizer(panGestureRecognizer)
19 | imageView.addGestureRecognizer(pinchGestureRecognizer)
20 | }
21 |
22 | func setupView() {
23 | view.backgroundColor = backgroundColor
24 | bottomToolbar.editorDelegate = self
25 | topToolbar.editorDelegate = self
26 | setupImageView()
27 | setupPreview()
28 | connectViews()
29 | }
30 | }
31 |
32 | private extension EditorViewController {
33 | var backgroundColor: UIColor {
34 | return UIColor(white: 31 / 255, alpha: 1)
35 | }
36 |
37 | func setupImageView() {
38 | imageView.image = editor?.editedImage
39 | imageView.isOpaque = false
40 | imageView.contentMode = .redraw
41 | preview.addSubview(imageClearBackground)
42 | imageClearBackground.backgroundColor = UIColor(patternImage: .fromFilestackBundle("clear-pattern"))
43 | imageClearBackground.frame = imageFrame.applying(CGAffineTransform(translationX: 4, y: 4))
44 | }
45 |
46 | func setupPreview() {
47 | preview.backgroundColor = backgroundColor
48 | }
49 |
50 | func connectViews() {
51 | connectBottomToolbar()
52 | connectTopToolbar()
53 | connectPreview()
54 | }
55 |
56 | func connectBottomToolbar() {
57 | view.fill(with: bottomToolbar, connectingEdges: [.bottom], withSafeAreaRespecting: true)
58 | view.fill(with: bottomToolbar, connectingEdges: [.left, .right], withSafeAreaRespecting: false)
59 | bottomToolbar.heightAnchor.constraint(equalToConstant: 44).isActive = true
60 | }
61 |
62 | func connectTopToolbar() {
63 | view.fill(with: topToolbar, connectingEdges: [.left, .right], withSafeAreaRespecting: false)
64 | topToolbar.bottomAnchor.constraint(equalTo: bottomToolbar.topAnchor).isActive = true
65 | topToolbar.heightAnchor.constraint(equalToConstant: 44).isActive = true
66 | }
67 |
68 | func connectPreview() {
69 | preview.fill(with: imageView, inset: 4)
70 | view.fill(with: preview, connectingEdges: [.top, .left, .right], withSafeAreaRespecting: true)
71 | preview.bottomAnchor.constraint(equalTo: topToolbar.topAnchor).isActive = true
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/EditionController/Views/ImageEditorView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageEditorView.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 7/4/19.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ImageEditorView: UIView {
12 | weak var image: UIImage? {
13 | didSet {
14 | cgBackedImage = image?.cgImageBackedCopy()
15 | setNeedsDisplay()
16 | }
17 | }
18 |
19 | // CIImage-backed UIImages are not rendered correctly in iOS 13 if they have some transformations
20 | // applied (e.g. cropping), so we use a CGImage-backed UIImage instead for rendering.
21 | private var cgBackedImage: UIImage?
22 |
23 | override func draw(_: CGRect) {
24 | guard let image = cgBackedImage else { return }
25 |
26 | // Clear background
27 | UIColor.clear.setFill()
28 | UIRectFill(bounds)
29 |
30 | // Calculate image draw rect
31 | let imageRect = CGRect(origin: CGPoint.zero, size: image.size)
32 | let ratio = max(imageRect.width / bounds.width, imageRect.height / bounds.height)
33 | let size = CGSize(width: (imageRect.width / ratio).rounded(), height: (imageRect.height / ratio).rounded())
34 | let origin = CGPoint(x: (bounds.midX - (size.width / 2)).rounded(), y: (bounds.midY - (size.height / 2)).rounded())
35 | let drawRect = CGRect(origin: origin, size: size)
36 |
37 | // Draw image
38 | image.draw(in: drawRect)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/SelectionList/SelectionListViewController+FlowLayout.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectionListViewController+FlowLayout.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 24/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension SelectionListViewController: UICollectionViewDelegateFlowLayout {
12 | func collectionView(_: UICollectionView,
13 | layout _: UICollectionViewLayout,
14 | insetForSectionAt _: Int) -> UIEdgeInsets {
15 | return UIEdgeInsets(top: cellSpacing, left: cellSpacing, bottom: cellSpacing, right: cellSpacing)
16 | }
17 |
18 | func collectionView(_: UICollectionView,
19 | layout _: UICollectionViewLayout,
20 | sizeForItemAt _: IndexPath) -> CGSize {
21 | return cellSize
22 | }
23 |
24 | func collectionView(_: UICollectionView,
25 | layout _: UICollectionViewLayout,
26 | minimumLineSpacingForSectionAt _: Int) -> CGFloat {
27 | return cellSpacing
28 | }
29 |
30 | func collectionView(_: UICollectionView,
31 | layout _: UICollectionViewLayout,
32 | minimumInteritemSpacingForSectionAt _: Int) -> CGFloat {
33 | return cellSpacing
34 | }
35 | }
36 |
37 | private extension SelectionListViewController {
38 | var cellSize: CGSize {
39 | return CGSize(width: cellSide, height: cellSide)
40 | }
41 |
42 | var cellSide: CGFloat {
43 | let totalSpacing = cellSpacing * (columnsCount + 1)
44 | return (totalWidth - totalSpacing) / columnsCount
45 | }
46 |
47 | var totalWidth: CGFloat {
48 | return view.safeAreaLayoutGuide.layoutFrame.width
49 | }
50 |
51 | var columnsCount: CGFloat {
52 | return (totalWidth / targetSide).rounded(.down)
53 | }
54 |
55 | var targetSide: CGFloat {
56 | return 100.0
57 | }
58 |
59 | var cellSpacing: CGFloat {
60 | return 6
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/SelectionList/SelectionListViewController+UICollectionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SelectionListViewController+UICollectionView.swift
3 | // EditImage
4 | //
5 | // Created by Mihály Papp on 26/07/2018.
6 | // Copyright © 2018 Mihály Papp. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | extension SelectionListViewController {
12 | override func collectionView(_: UICollectionView, numberOfItemsInSection _: Int) -> Int {
13 | return numberOfCells
14 | }
15 |
16 | override func collectionView(_ collectionView: UICollectionView,
17 | cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
18 | guard let cell = collectionView.reuse(SelectionCell.self, for: indexPath) else { return UICollectionViewCell() }
19 | cellWasDisplayed(cell, on: indexPath.row)
20 | return cell
21 | }
22 |
23 | override func collectionView(_: UICollectionView, didSelectItemAt indexPath: IndexPath) {
24 | cellWasPressed(on: indexPath.row)
25 | }
26 | }
27 |
28 | // MARK: LongPress handling
29 |
30 | extension SelectionListViewController {
31 | override func collectionView(_: UICollectionView,
32 | shouldShowMenuForItemAt indexPath: IndexPath) -> Bool {
33 | cellWasLongPressed(on: indexPath.row)
34 | return true
35 | }
36 |
37 | override func collectionView(_: UICollectionView,
38 | canPerformAction _: Selector,
39 | forItemAt _: IndexPath,
40 | withSender _: Any?) -> Bool {
41 | return false
42 | }
43 |
44 | override func collectionView(_: UICollectionView,
45 | performAction _: Selector,
46 | forItemAt _: IndexPath,
47 | withSender _: Any?) {
48 | return
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoEditor/SelectionList/Uploadable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Uploadable.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 30/07/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import AVFoundation
10 | import UIKit
11 |
12 | protocol Uploadable: AnyObject {
13 | var isEditable: Bool { get }
14 | var associatedImage: UIImage { get }
15 | var typeIcon: UIImage { get }
16 | var additionalInfo: String? { get }
17 | }
18 |
19 | extension UIImage: Uploadable {
20 | var isEditable: Bool { true }
21 | var associatedImage: UIImage { self }
22 | var typeIcon: UIImage { UIImage.fromFilestackBundle("icon-image").withRenderingMode(.alwaysTemplate) }
23 | var additionalInfo: String? { nil }
24 | }
25 |
26 | extension AVAsset: Uploadable {
27 | var isEditable: Bool { false }
28 |
29 | var associatedImage: UIImage {
30 | let beginning = CMTime(seconds: 0, preferredTimescale: 1)
31 | do {
32 | let cgImage = try AVAssetImageGenerator(asset: self).copyCGImage(at: beginning, actualTime: nil)
33 | return UIImage(cgImage: cgImage)
34 | } catch _ {
35 | return UIImage() // TODO: return placeholder
36 | }
37 | }
38 |
39 | var typeIcon: UIImage { UIImage.fromFilestackBundle("icon-file-video").withRenderingMode(.alwaysTemplate) }
40 |
41 | var additionalInfo: String? { DurationFormatter().string(from: duration.seconds) }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/AlbumList/AlbumCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlbumListCell.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 23/05/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Photos
10 | import UIKit
11 |
12 | class AlbumCell: UITableViewCell {
13 | @IBOutlet var coverImage: UIImageView!
14 | @IBOutlet var titleLabel: UILabel!
15 |
16 | private var requestIDs: [PHImageRequestID] = []
17 |
18 | func configure(for album: Album) {
19 | selectionStyle = .none
20 | titleLabel.text = album.title
21 | coverImage.contentMode = .scaleAspectFill
22 | coverImage.clipsToBounds = true
23 |
24 | let requestID = album.elements.last?.fetchImage(forSize: coverImage.frame.size) { image, requestID in
25 | self.requestIDs.removeAll { $0 == requestID }
26 |
27 | DispatchQueue.main.async { self.coverImage.image = image }
28 | }
29 |
30 | if let requestID = requestID {
31 | requestIDs.append(requestID)
32 | }
33 | }
34 |
35 | deinit {
36 | for requestID in requestIDs {
37 | PHImageManager.default().cancelImageRequest(requestID)
38 | }
39 |
40 | requestIDs.removeAll()
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/AlbumList/AlbumListViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AlbumListViewController.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 23/05/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class AlbumListViewController: UITableViewController {
12 | weak var pickerController: PhotoPickerController?
13 |
14 | private var activityIndicator: UIActivityIndicatorView?
15 | private var albumList = [Album]()
16 |
17 | var repository: PhotoAlbumRepository? {
18 | return pickerController?.albumRepository
19 | }
20 |
21 | override func viewDidLoad() {
22 | super.viewDidLoad()
23 | setupView()
24 | }
25 | }
26 |
27 | private extension AlbumListViewController {
28 | func setupView() {
29 | setupNavigation()
30 | configureAndShowEmptyTableView()
31 | createAndStartLaodingView()
32 | fetchData()
33 | }
34 |
35 | func setupNavigation() {
36 | if let pickerController = pickerController {
37 | navigationItem.leftBarButtonItem = pickerController.cancelBarButton
38 | navigationItem.rightBarButtonItems = pickerController.rightBarItems
39 | }
40 | }
41 | }
42 |
43 | private extension AlbumListViewController {
44 | func set(_ albums: [Album]) {
45 | albumList = albums
46 | stopLoading()
47 | if !albumList.isEmpty {
48 | configureTableViewWithContent()
49 | }
50 | DispatchQueue.main.async {
51 | self.tableView.reloadData()
52 | }
53 | }
54 |
55 | func fetchData() {
56 | repository?.getAlbums { albums in self.set(albums) }
57 | }
58 | }
59 |
60 | // MARK: - Empty Table
61 |
62 | private extension AlbumListViewController {
63 | func configureAndShowEmptyTableView() {
64 | DispatchQueue.main.async {
65 | self.tableView.backgroundView = UIView(frame: self.tableView.frame)
66 | self.tableView.separatorStyle = .none
67 | }
68 | }
69 |
70 | func configureTableViewWithContent() {
71 | DispatchQueue.main.async {
72 | self.tableView.separatorStyle = .singleLine
73 | }
74 | }
75 | }
76 |
77 | // MARK: - Loading Indicator
78 |
79 | private extension AlbumListViewController {
80 | func createAndStartLaodingView() {
81 | DispatchQueue.main.async {
82 | let indicator = UIActivityIndicatorView(style: .medium)
83 | indicator.center = self.view.center
84 | indicator.startAnimating()
85 | self.view.addSubview(indicator)
86 | self.activityIndicator = indicator
87 | }
88 | }
89 |
90 | func stopLoading() {
91 | DispatchQueue.main.async {
92 | self.activityIndicator?.stopAnimating()
93 | self.activityIndicator?.removeFromSuperview()
94 | }
95 | }
96 | }
97 |
98 | // MARK: - TableView Delegate & DataSource
99 |
100 | extension AlbumListViewController {
101 | override func numberOfSections(in _: UITableView) -> Int {
102 | return 1
103 | }
104 |
105 | override func tableView(_: UITableView, numberOfRowsInSection _: Int) -> Int {
106 | return albumList.count
107 | }
108 |
109 | override func tableView(_ tableView: UITableView,
110 | cellForRowAt indexPath: IndexPath) -> UITableViewCell {
111 | guard let cell = tableView.dequeueReusableCell(withIdentifier: "AlbumCell") as? AlbumCell else {
112 | return UITableViewCell()
113 | }
114 | let album = albumList[indexPath.row]
115 | cell.configure(for: album)
116 | return cell
117 | }
118 |
119 | override func tableView(_: UITableView, viewForFooterInSection _: Int) -> UIView? {
120 | return UIView()
121 | }
122 |
123 | override func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {
124 | let album = albumList[indexPath.row]
125 |
126 | if let collectionView = pickerController?.assetCollection {
127 | collectionView.configure(with: album)
128 | navigationController?.pushViewController(collectionView, animated: true)
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/AssetCollection/AssetCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AssetCell.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 04/06/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Photos
10 | import UIKit
11 |
12 | class AssetCell: UICollectionViewCell {
13 | @IBOutlet var image: UIImageView!
14 | @IBOutlet var selectedCheckbox: UIImageView!
15 | @IBOutlet var dimView: UIView!
16 | @IBOutlet var additionalInfoLabel: UILabel!
17 | private lazy var gradientLayer = CAGradientLayer()
18 |
19 | private var asset: PHAsset!
20 | private var requestIDs: [PHImageRequestID] = []
21 | private let uploadableExtractor = UploadableExtractor()
22 |
23 | override func layoutSubviews() {
24 | super.layoutSubviews()
25 | gradientLayer.frame = image.frame
26 | }
27 |
28 | func configure(for asset: PHAsset, isSelected: Bool) {
29 | self.asset = asset
30 |
31 | requestIDs.append(asset.fetchImage(forSize: image.frame.size) { image, requestID in
32 | self.requestIDs.removeAll { $0 == requestID }
33 |
34 | DispatchQueue.main.async {
35 | self.configure(with: image)
36 | self.set(selected: isSelected)
37 | }
38 | })
39 |
40 | if asset.mediaType == .video {
41 | if let requestID = (uploadableExtractor.fetchUploadable(using: asset) { uploadable, requestID in
42 | self.requestIDs.removeAll { $0 == requestID }
43 |
44 | DispatchQueue.main.async {
45 | self.additionalInfoLabel.text = uploadable?.additionalInfo
46 | self.setupGradientLayer()
47 | }
48 | }) {
49 | requestIDs.append(requestID)
50 | }
51 | }
52 | }
53 |
54 | deinit {
55 | for requestID in requestIDs {
56 | uploadableExtractor.cancelFetch(using: requestID)
57 | }
58 |
59 | requestIDs.removeAll()
60 | }
61 |
62 | func set(selected: Bool) {
63 | dimView.isHidden = !selected
64 | selectedCheckbox.isHidden = !selected
65 | }
66 | }
67 |
68 | private extension AssetCell {
69 | func configure(with image: UIImage?) {
70 | self.image.image = image
71 | selectedCheckbox.image = UIImage(named: "icon-selected", in: bundle, compatibleWith: nil)
72 | }
73 |
74 | func setupGradientLayer() {
75 | image.layer.insertSublayer(gradientLayer, at: 0)
76 | gradientLayer.colors = [UIColor.clear.cgColor, UIColor.clear.cgColor, UIColor(white: 0.55, alpha: 0.6).cgColor]
77 | gradientLayer.locations = [NSNumber(value: 0), NSNumber(value: 0.6), NSNumber(value: 1)]
78 | gradientLayer.startPoint = CGPoint(x: 1, y: 0)
79 | gradientLayer.endPoint = CGPoint(x: 0, y: 1)
80 | gradientLayer.frame = image.frame
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/PhotoAlbumRepository.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoAlbumRepository.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 29/05/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Photos
10 |
11 | struct Album {
12 | let title: String
13 | let elements: [PHAsset]
14 | }
15 |
16 | class PhotoAlbumRepository {
17 | private var cachedAlbums: [Album]?
18 |
19 | init() {
20 | fetchAndCacheAlbums(completion: nil)
21 | }
22 |
23 | func getAlbums(completion: @escaping ([Album]) -> Void) {
24 | if let cachedAlbums = cachedAlbums {
25 | completion(cachedAlbums)
26 | }
27 | fetchAndCacheAlbums(completion: completion)
28 | }
29 |
30 | private func fetchAndCacheAlbums(completion: (([Album]) -> Void)?) {
31 | DispatchQueue.global(qos: .default).async {
32 | let collections = PHAssetCollection.allCollections(types: [.smartAlbum, .album])
33 | let allAlbums = collections.map { Album(title: $0.localizedTitle ?? "", elements: $0.allAssets) }
34 | let nonEmptyAlbums = allAlbums.filter { !$0.elements.isEmpty }
35 | self.cachedAlbums = nonEmptyAlbums
36 | completion?(nonEmptyAlbums)
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/PhotoPickerController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoPickerController.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 23/05/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Photos
10 | import UIKit
11 |
12 | protocol PhotoPickerControllerDelegate: AnyObject {
13 | func photoPickerControllerDidCancel(controller: UINavigationController)
14 | func photoPicker(controller: UINavigationController, didSelectAssets assets: [PHAsset])
15 | }
16 |
17 | class PhotoPickerController {
18 | // MARK: - Internal Properties
19 |
20 | let albumRepository = PhotoAlbumRepository()
21 | let maximumSelectionAllowed: UInt
22 | var isMaximumLimitSet: Bool { maximumSelectionAllowed != Config.kMaximumSelectionNoLimit }
23 | var selectedAssets = Set()
24 |
25 | weak var delegate: PhotoPickerControllerDelegate?
26 |
27 | var assetCollection: AssetCollectionViewController {
28 | let vc = viewController(with: "AssetCollectionViewController")
29 |
30 | guard let assetCollection = vc as? AssetCollectionViewController else {
31 | fatalError("AssetCollectionViewController type is corrupted")
32 | }
33 |
34 | assetCollection.pickerController = self
35 |
36 | return assetCollection
37 | }
38 |
39 | lazy var navigation = UINavigationController(rootViewController: albumList)
40 |
41 | var cancelBarButton: UIBarButtonItem {
42 | return UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(dismissWithoutSelection))
43 | }
44 |
45 | var rightBarItems: [UIBarButtonItem] {
46 | guard !selectedAssets.isEmpty else { return [] }
47 |
48 | return [selectionCountBarButton, doneBarButton]
49 | }
50 |
51 | var doneBarButton: UIBarButtonItem {
52 | return UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(dismissWithSelection))
53 | }
54 |
55 | var selectionCountBarButton: UIBarButtonItem {
56 | let maximum = isMaximumLimitSet ? "/\(maximumSelectionAllowed)" : ""
57 | let title = "(\(selectedAssets.count)\(maximum))"
58 |
59 | return UIBarButtonItem(title: title, style: .done, target: self, action: #selector(dismissWithSelection))
60 | }
61 |
62 | // MARK: - Private Properties
63 |
64 | private lazy var albumList: AlbumListViewController = {
65 | let vc = viewController(with: "AlbumListViewController")
66 |
67 | guard let albumList = vc as? AlbumListViewController else {
68 | fatalError("AlbumListViewController type is corrupted")
69 | }
70 |
71 | albumList.pickerController = self
72 |
73 | return albumList
74 | }()
75 |
76 |
77 | // MARK: - Lifecycle
78 |
79 | init(maximumSelection: UInt) {
80 | maximumSelectionAllowed = maximumSelection
81 |
82 | albumRepository.getAlbums { _ in
83 | DispatchQueue.main.async {
84 | self.albumList.tableView.reloadData()
85 | }
86 | }
87 | }
88 | }
89 |
90 | // MARK: - Private Functions
91 |
92 | private extension PhotoPickerController {
93 | func viewController(with name: String) -> UIViewController {
94 | let storyboard = UIStoryboard(name: "PhotoPicker", bundle: bundle)
95 |
96 | return storyboard.instantiateViewController(withIdentifier: name)
97 | }
98 | }
99 |
100 | // MARK: - Navigation Bar Actions
101 |
102 | private extension PhotoPickerController {
103 | @objc func dismissWithSelection() {
104 | delegate?.photoPicker(controller: navigation, didSelectAssets: Array(selectedAssets))
105 | }
106 |
107 | @objc func dismissWithoutSelection() {
108 | delegate?.photoPickerControllerDidCancel(controller: navigation)
109 | }
110 | }
111 |
112 | // MARK: - AssetSelectionDelegate Conformance
113 |
114 | extension PhotoPickerController: AssetSelectionDelegate {
115 | func add(asset: PHAsset) {
116 | selectedAssets.insert(asset)
117 |
118 | if maximumSelectionAllowed == 1 {
119 | dismissWithSelection()
120 | } else {
121 | updateNavBar()
122 | }
123 | }
124 |
125 | func remove(asset: PHAsset) {
126 | selectedAssets.remove(asset)
127 | updateNavBar()
128 | }
129 |
130 | func updateNavBar() {
131 | for vc in navigation.viewControllers {
132 | vc.navigationItem.rightBarButtonItems = rightBarItems
133 | }
134 | }
135 | }
136 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/PhotoPicker/PhotosExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotosExtensions.swift
3 | // Filestack
4 | //
5 | // Created by Mihály Papp on 29/05/2018.
6 | // Copyright © 2018 Filestack. All rights reserved.
7 | //
8 |
9 | import Photos
10 | import UIKit
11 |
12 | // MARK: - PHAsset Public Properties
13 |
14 | extension PHAsset {
15 | func fetchImage(forSize size: CGSize, completion: @escaping (UIImage?, PHImageRequestID) -> Void) -> PHImageRequestID {
16 | let getMaximumSize = (size == PHImageManagerMaximumSize)
17 | let scaledSize = getMaximumSize ? PHImageManagerMaximumSize : adjustToScreenScale(size: size)
18 | let manager = PHImageManager.default()
19 |
20 | var requestID: PHImageRequestID!
21 |
22 | requestID = manager.requestImage(for: self,
23 | targetSize: scaledSize,
24 | contentMode: .aspectFit,
25 | options: requestOptions) { image, _ in
26 | completion(image, requestID)
27 | }
28 |
29 | return requestID
30 | }
31 | }
32 |
33 | // MARK: - PHAsset Computed Properties
34 |
35 | private extension PHAsset {
36 | var requestOptions: PHImageRequestOptions {
37 | let option = PHImageRequestOptions()
38 |
39 | option.deliveryMode = .highQualityFormat
40 | option.isNetworkAccessAllowed = true
41 | option.isSynchronous = false
42 |
43 | return option
44 | }
45 | }
46 |
47 | // MARK: - PHAsset Private Functions
48 |
49 | private extension PHAsset {
50 | func adjustToScreenScale(size: CGSize) -> CGSize {
51 | let scale = UIScreen.main.scale
52 |
53 | return CGSize(width: size.width * scale, height: size.height * scale)
54 | }
55 | }
56 |
57 | // MARK: - PHAssetCollection Functions
58 |
59 | extension PHAssetCollection {
60 | class func allCollections(types: [PHAssetCollectionType]) -> [PHAssetCollection] {
61 | var allCollections = [PHAssetCollection]()
62 |
63 | for type in types {
64 | let fetch = PHAssetCollection.fetchAssetCollections(with: type, subtype: .any, options: nil)
65 |
66 | fetch.enumerateObjects { collection, _, _ in allCollections.append(collection) }
67 | }
68 |
69 | return allCollections
70 | }
71 | }
72 |
73 | // MARK: - PHAssetCollection Computed Properties
74 |
75 | extension PHAssetCollection {
76 | var allAssets: [PHAsset] {
77 | var allAssets = [PHAsset]()
78 | let fetch = PHAsset.fetchAssets(in: self, options: nil)
79 |
80 | fetch.enumerateObjects { asset, _, _ in allAssets.append(asset) }
81 |
82 | return allAssets
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Protocols/CellDescriptibleSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CellDescriptibleSource.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/7/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | protocol CellDescriptibleSource {
12 | var iconImage: UIImage { get }
13 | var textDescription: String { get }
14 | }
15 |
16 | func == (lhs: CellDescriptibleSource, rhs: CellDescriptibleSource) -> Bool {
17 | return lhs.textDescription == rhs.textDescription
18 | }
19 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Protocols/CloudSourceDataSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudSourceDataSource.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/16/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import MobileCoreServices.UTType
11 | import UIKit
12 |
13 | protocol CloudSourceDataSource: AnyObject {
14 | var client: Client! { get }
15 | var storeOptions: StorageOptions! { get }
16 | var source: CloudSource! { get }
17 | var path: String! { get set }
18 | var nextPageToken: String? { get set }
19 | var items: [CloudItem]? { get }
20 | var thumbnailCache: NSCache { get }
21 |
22 | func store(item: CloudItem)
23 | func navigate(to item: CloudItem)
24 | func loadNextPage(completionHandler: @escaping (() -> Void))
25 | func search(text: String, completionHandler: @escaping (() -> Void))
26 | func refresh(completionHandler: @escaping (() -> Void))
27 | func cacheThumbnail(for item: CloudItem, completionHandler: @escaping ((UIImage) -> Void))
28 | }
29 |
30 | extension CloudSourceDataSource {
31 | // Based on user config, can `item` be selected?
32 | func canSelect(item: CloudItem) -> Bool {
33 | let config = client.config
34 |
35 | if item.isFolder || config.cloudSourceAllowedUTIs.isEmpty {
36 | return true
37 | }
38 |
39 | guard let uti = item.name.UTI else { return false }
40 |
41 | // Try to find at least an UTI in `cloudSourceAllowedUTIs` that comforms to our item's UTI,
42 | // or return false if none match.
43 | for allowedUTI in config.cloudSourceAllowedUTIs {
44 | if UTTypeConformsTo(uti, allowedUTI as CFString) {
45 | return true
46 | }
47 | }
48 |
49 | return false
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Protocols/CloudSourceDataSourceConsumer.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudSourceDataSourceConsumer.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol CloudSourceDataSourceConsumer {
12 | func dataSourceReceivedInitialResults(dataSource: CloudSourceDataSource)
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Protocols/Scene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Scene.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/13/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | protocol Scene {
12 | associatedtype ViewController
13 |
14 | var identifier: String { get }
15 |
16 | func configureViewController(_ viewController: ViewController)
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Scenes/CloudSourceBarTabScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudSourceBarTabScene.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | struct CloudSourceTabBarScene: Scene {
13 | let client: Client
14 | let storeOptions: StorageOptions
15 | let source: CloudSource
16 |
17 | var customSourceName: String?
18 | var path: String?
19 | var nextPageToken: String?
20 | var viewType: CloudSourceViewType
21 |
22 | func configureViewController(_ viewController: CloudSourceTabBarController) {
23 | // Inject the dependencies
24 | viewController.client = client
25 | viewController.storeOptions = storeOptions
26 | viewController.source = source
27 | viewController.customSourceName = customSourceName
28 | viewController.nextPageToken = nextPageToken
29 | viewController.path = path ?? "/"
30 | viewController.viewType = viewType
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Scenes/PickerNavigationScene.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PickerNavigationScene.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import Foundation
11 |
12 | struct PickerNavigationScene: Scene {
13 | let client: Client
14 | let storeOptions: StorageOptions
15 |
16 | func configureViewController(_ viewController: PickerNavigationController) {
17 | viewController.client = client
18 | viewController.storeOptions = storeOptions
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Table View Cells/ActivityIndicatorTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ActivityIndicatorTableViewCell.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/13/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class ActivityIndicatorTableViewCell: UITableViewCell {
12 | @IBOutlet var activityIndicator: UIActivityIndicatorView!
13 | }
14 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Internal/Table View Cells/CloudItemTableViewCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CloudItemTableViewCell.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/17/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class CloudItemTableViewCell: UITableViewCell {}
12 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Controllers/PickerNavigationController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationController.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/8/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import FilestackSDK
10 | import UIKit
11 |
12 | /// This class represents a navigation controller containing UI elements that allow picking files from local and cloud
13 | /// sources.
14 | @objc(FSPickerNavigationController) public class PickerNavigationController: UINavigationController {
15 | var client: Client!
16 | var storeOptions: StorageOptions!
17 |
18 | /// This setting determines what should happen after picking files (see `PickerBehavior` for more information.)
19 | ///
20 | /// The default value is `.uploadAndStore`
21 | public var behavior: PickerBehavior = .uploadAndStore(uploadOptions: .defaults)
22 |
23 | /// Stylizer used for changing default colors and fonts.
24 | @objc public lazy var stylizer = Stylizer(delegate: self)
25 |
26 | /// The picker delegate. Optional
27 | @objc public weak var pickerDelegate: PickerNavigationControllerDelegate?
28 |
29 | deinit {
30 | pickerDelegate?.pickerWasDismissed?(picker: self)
31 | }
32 | }
33 |
34 | /// This protocol contains the function signatures any `PickerNavigationController` delegate should conform to.
35 | @objc(FSPickerNavigationControllerDelegate) public protocol PickerNavigationControllerDelegate: AnyObject {
36 | /// Called when the picker finishes picking files originating from the local device.
37 | @objc func pickerPickedFiles(picker: PickerNavigationController, fileURLs: [URL])
38 |
39 | /// Called when the picker finishes storing a file originating from a cloud source into the storage destination.
40 | @objc func pickerStoredFile(picker: PickerNavigationController, response: StoreResponse)
41 |
42 | /// Called when the picker finishes uploading files originating from the local device to the storage destination. Optional.
43 | @objc optional func pickerUploadedFiles(picker: PickerNavigationController, responses: [JSONResponse])
44 |
45 | /// Called when the picker reports progress during a file or set of files being uploaded. Optional.
46 | @objc optional func pickerReportedUploadProgress(picker: PickerNavigationController, progress: Float)
47 |
48 | /// Called after the picker was dismissed. Optional.
49 | @objc optional func pickerWasDismissed(picker: PickerNavigationController)
50 | }
51 |
52 | extension PickerNavigationController: StylizerDelegate {
53 | /// :nodoc:
54 | public func updateStyle() {
55 | navigationBar.tintColor = stylizer.navBar.tintColor
56 | navigationBar.titleTextAttributes = [.foregroundColor: stylizer.navBar.titleColor]
57 | navigationBar.barStyle = stylizer.navBar.style
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Enums/LocalProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalProvider.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// Represents a local provider to be used by a `LocalSource`.
12 | @objc(FSLocalProvider) public enum LocalProvider: UInt {
13 | /// Camera
14 | case camera
15 |
16 | /// Photo Library
17 | case photoLibrary
18 |
19 | /// Documents
20 | case documents
21 |
22 | /// Custom Source
23 | case customSource
24 | }
25 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Models/LocalSource.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalSource.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/7/17.
6 | // Copyright © 2017 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import FilestackSDK
11 |
12 | /// Represents a type of local source to be used in the picker.
13 | @objc(FSLocalSource) public class LocalSource: NSObject, CellDescriptibleSource {
14 | let provider: LocalProvider
15 | let iconImage: UIImage
16 | let textDescription: String
17 | let sourceProvider: SourceProvider?
18 |
19 | /// Initializer for a `LocalSource` accepting a `LocalProvider`.
20 | ///
21 | /// - Parameter description: A `String` describing the local source.
22 | /// - Parameter image: An `UIImage` visually describing the local source.
23 | /// - Parameter provider: A `LocalProvider` that better represents the local source.
24 | @objc public init(description: String, image: UIImage, provider: LocalProvider) {
25 | self.textDescription = description
26 | self.iconImage = image
27 | self.provider = provider
28 | self.sourceProvider = nil
29 | }
30 |
31 | /// Initializer for a `LocalSource` accepting a `SourceProvider`.
32 | ///
33 | /// - Parameter description: A `String` describing the local source.
34 | /// - Parameter image: An `UIImage` visually describing the local source.
35 | /// - Parameter sourceProvider: A `SourceProvider` that presents a custom user-provided view controller.
36 | public init(description: String, image: UIImage, sourceProvider: SourceProvider) {
37 | self.textDescription = description
38 | self.iconImage = image
39 | self.provider = .customSource
40 | self.sourceProvider = sourceProvider
41 | }
42 |
43 | /// Camera
44 | @objc public static var camera = LocalSource(description: "Camera",
45 | image: .templatedFilestackImage("icon-camera"),
46 | provider: .camera)
47 |
48 | /// Photo Library
49 | @objc public static var photoLibrary = LocalSource(description: "Photo Library",
50 | image: .templatedFilestackImage("icon-photolibrary"),
51 | provider: .photoLibrary)
52 |
53 | /// Documents
54 | @objc public static var documents = LocalSource(description: "iOS Files",
55 | image: .templatedFilestackImage("icon-documents"),
56 | provider: .documents)
57 |
58 | /// Returns all the supported sources.
59 | @objc public static func all() -> [LocalSource] {
60 | return [.camera, .photoLibrary, .documents]
61 | }
62 |
63 | /// Returns this source's title.
64 | @objc public static func title() -> String {
65 | return "Local"
66 | }
67 |
68 | /// Returns an user-provided local source that uses a custom `SourceProvider`.
69 | public static func custom(description: String, image: UIImage, provider: SourceProvider) -> LocalSource {
70 | return LocalSource(description: description,
71 | image: image,
72 | sourceProvider: provider)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Protocols/SourceProvider.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SourceProvider.swift
3 | // SourceProvider
4 | //
5 | // Created by Ruben Nine on 30/7/21.
6 | // Copyright © 2021 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | /// `SourceProvider` defines the protocol that must be implemented by any view controllers that should be used to
12 | /// pick files using an user-provided implementation.
13 | public protocol SourceProvider: UIViewController {
14 | /// Defines the source provider delegate.
15 | var sourceProviderDelegate: SourceProviderDelegate? { set get }
16 |
17 | /// Initializer for this source provider.
18 | init()
19 | }
20 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Protocols/SourceProviderDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SourceProviderDelegate.swift
3 | // SourceProviderDelegate
4 | //
5 | // Created by Ruben Nine on 30/7/21.
6 | // Copyright © 2021 Filestack. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | public protocol SourceProviderDelegate: AnyObject {
12 | /// Called when one or more URLs were picked by this source provider.
13 | func sourceProviderPicked(urls: [URL])
14 |
15 | /// Called when source provider is cancelled (either by the user or programmatically.)
16 | func sourceProviderCancelled()
17 | }
18 |
--------------------------------------------------------------------------------
/Sources/Filestack/UI/Public/Protocols/StylizerDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // StylizerDelegate.swift
3 | // Filestack
4 | //
5 | // Created by Ruben Nine on 11/09/2019.
6 | // Copyright © 2019 Filestack. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | /// :nodoc:
12 | @objc(FSStylizerDelegate) public protocol StylizerDelegate: AnyObject {
13 | @objc func updateStyle()
14 | }
15 |
--------------------------------------------------------------------------------
/Sources/Filestack/VERSION:
--------------------------------------------------------------------------------
1 | ../../VERSION
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | 3.0.1
2 |
--------------------------------------------------------------------------------
/bin/apply-swiftformat.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | if ! which swiftformat >/dev/null; then
4 | if which brew >/dev/null; then
5 | brew update && brew install swiftformat
6 | else
7 | echo "WARNING: Tried to automatically install SwiftFormat using `brew` but `brew` itself could not be found. Please install SwiftFormat manually and try again."
8 | fi
9 | fi
10 |
11 | if which swiftformat >/dev/null; then
12 | swiftformat .
13 | else
14 | echo "WARNING: SwiftFormat is missing. Please install it manually and try again."
15 | fi
16 |
--------------------------------------------------------------------------------
/bin/generate-and-deploy-docs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -o errexit #abort if any command fails
3 |
4 | ./bin/generate-docs.sh
5 |
6 | GIT_DEPLOY_DIR=docs GIT_DEPLOY_BRANCH=gh-pages GIT_DEPLOY_REPO=git@github.com:filestack/filestack-ios.git ./bin/deploy-docs.sh
7 |
--------------------------------------------------------------------------------
/bin/generate-docs.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -o errexit #abort if any command fails
3 |
4 | swift doc generate Sources --module-name Filestack --format html --output docs --base-url https://filestack.github.io/filestack-ios/
5 |
--------------------------------------------------------------------------------
/bin/set-buildnumber.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # set-buildnumber.sh
4 | # Filestack
5 | #
6 | # Created by Ruben Nine on 7/5/17.
7 | # Copyright © 2017 Filestack. All rights reserved.
8 |
9 | infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
10 | buildnum=$(git --git-dir="$SRCROOT/.git" log --oneline | wc -l | tr -d '[:space:]')
11 |
12 | if [ -z "$buildnum" ]; then
13 | echo "Failed to set buildNum."
14 | exit 1
15 | fi
16 |
17 | /usr/libexec/PlistBuddy -c "Set CFBundleVersion $buildnum" "$infoplist"
18 |
--------------------------------------------------------------------------------
/bin/set-version.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # set-version.sh
4 | # Filestack
5 | #
6 | # Created by Ruben Nine on 7/5/17.
7 | # Copyright © 2017 Filestack. All rights reserved.
8 |
9 | infoplist="$BUILT_PRODUCTS_DIR/$INFOPLIST_PATH"
10 | shortVersionString=$(cat $SRCROOT/VERSION)
11 |
12 | if [ -z "$shortVersionString" ]; then
13 | echo "Failed to set shortVersionString."
14 | exit 1
15 | fi
16 |
17 | /usr/libexec/PlistBuddy -c "Set CFBundleShortVersionString $shortVersionString" "$infoplist"
18 |
--------------------------------------------------------------------------------