Full Code of cloudinary/cloudinary_ios for AI

master ea94096c41ad cached
444 files
3.2 MB
865.0k tokens
1 symbols
1 requests
Download .txt
Showing preview only (3,458K chars total). Download the full file or copy to clipboard to get everything.
Repository: cloudinary/cloudinary_ios
Branch: master
Commit: ea94096c41ad
Files: 444
Total size: 3.2 MB

Directory structure:
gitextract_f1wonv0o/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── pull_request_template.md
├── .gitignore
├── .gitmodules
├── .swiftpm/
│   └── xcode/
│       └── package.xcworkspace/
│           └── contents.xcworkspacedata
├── .travis.yml
├── CHANGELOG.md
├── Cloudinary/
│   ├── Assets/
│   │   └── .gitkeep
│   └── Classes/
│       ├── Core/
│       │   ├── BaseNetwork/
│       │   │   ├── CLDNConvertible.swift
│       │   │   ├── CLDNError.swift
│       │   │   ├── CLDNMultipartFormData.swift
│       │   │   ├── CLDNParameterEncoding.swift
│       │   │   ├── CLDNRequest.swift
│       │   │   ├── CLDNResponse.swift
│       │   │   ├── CLDNResponseSerialization.swift
│       │   │   ├── CLDNResult.swift
│       │   │   ├── CLDNSessionDelegate.swift
│       │   │   ├── CLDNSessionManager.swift
│       │   │   ├── CLDNTaskDelegate.swift
│       │   │   ├── CLDNTimeline.swift
│       │   │   ├── CLDNValidation.swift
│       │   │   └── DispatchQueue+Cloudinary.swift
│       │   ├── CLDAnalytics.swift
│       │   ├── CLDCloudinary.swift
│       │   ├── CLDCompatibility.swift
│       │   ├── CLDConfiguration.swift
│       │   ├── CLDEagerTransformation.swift
│       │   ├── CLDResponsiveParams.swift
│       │   ├── Cloudinary.h
│       │   ├── Features/
│       │   │   ├── CacheSystem/
│       │   │   │   └── Enum/
│       │   │   │       └── HTTPStatusCode.swift
│       │   │   ├── Downloader/
│       │   │   │   └── CLDDownloader.swift
│       │   │   ├── Helpers/
│       │   │   │   ├── CLDBaseNetworkObject.swift
│       │   │   │   ├── CLDConditionExpression.swift
│       │   │   │   ├── CLDDefinitions.swift
│       │   │   │   ├── CLDExpression.swift
│       │   │   │   ├── CLDOperators.swift
│       │   │   │   ├── CLDTransformation.swift
│       │   │   │   ├── CLDVariable.swift
│       │   │   │   ├── Layers/
│       │   │   │   │   ├── CLDFetchLayer.swift
│       │   │   │   │   ├── CLDLayer.swift
│       │   │   │   │   ├── CLDSubtitlesLayer.swift
│       │   │   │   │   └── CLDTextLayer.swift
│       │   │   │   ├── RequestParams/
│       │   │   │   │   ├── CLDRequestParams.swift
│       │   │   │   │   └── Helpers/
│       │   │   │   │       └── CLDRequestParamsHelpers.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   └── CLDRequest.swift
│       │   │   │   └── Results/
│       │   │   │       ├── CLDBaseResult.swift
│       │   │   │       └── Helpers/
│       │   │   │           ├── AccessibilityAnalysis/
│       │   │   │           │   ├── CLDAccessibilityAnalysisResult.swift
│       │   │   │           │   └── CLDColorblindAccessibilityAnalysisResult.swift
│       │   │   │           ├── CLDBoundingBox.swift
│       │   │   │           ├── CLDDetection.swift
│       │   │   │           ├── CLDFace.swift
│       │   │   │           ├── CLDInfo.swift
│       │   │   │           ├── CLDOcr/
│       │   │   │           │   ├── CLDAdvOcrResult.swift
│       │   │   │           │   ├── CLDOcrBlockResult.swift
│       │   │   │           │   ├── CLDOcrBoundindBlockResult.swift
│       │   │   │           │   ├── CLDOcrDataResult.swift
│       │   │   │           │   ├── CLDOcrDetectedLanguagesResult.swift
│       │   │   │           │   ├── CLDOcrFullTextAnnotationResult.swift
│       │   │   │           │   ├── CLDOcrPageResult.swift
│       │   │   │           │   ├── CLDOcrParagraphResult.swift
│       │   │   │           │   ├── CLDOcrPropertyResult.swift
│       │   │   │           │   ├── CLDOcrResult.swift
│       │   │   │           │   ├── CLDOcrSymbolResult.swift
│       │   │   │           │   ├── CLDOcrTextAnnotationResult.swift
│       │   │   │           │   └── CLDOcrWordResult.swift
│       │   │   │           ├── CLDQualityAnalysis/
│       │   │   │           │   └── CLDQualityAnalysis.swift
│       │   │   │           ├── CLDRekognitionFace.swift
│       │   │   │           └── CommonResultKeys.swift
│       │   │   ├── ManagementApi/
│       │   │   │   ├── CLDManagementApi.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   ├── CLDDeleteRequest.swift
│       │   │   │   │   ├── CLDExplicitRequest.swift
│       │   │   │   │   ├── CLDExplodeRequest.swift
│       │   │   │   │   ├── CLDMultiRequest.swift
│       │   │   │   │   ├── CLDRenameRequest.swift
│       │   │   │   │   ├── CLDSpriteRequest.swift
│       │   │   │   │   ├── CLDTagRequest.swift
│       │   │   │   │   └── CLDTextRequest.swift
│       │   │   │   ├── RequestsParams/
│       │   │   │   │   ├── CLDDeleteByTokenRequestParams.swift
│       │   │   │   │   ├── CLDDestroyRequestParams.swift
│       │   │   │   │   ├── CLDExplicitRequestParams.swift
│       │   │   │   │   ├── CLDExplodeRequestParams.swift
│       │   │   │   │   ├── CLDMultiRequestParams.swift
│       │   │   │   │   ├── CLDRenameRequestParams.swift
│       │   │   │   │   ├── CLDSpriteRequestParams.swift
│       │   │   │   │   ├── CLDTagsRequestParams.swift
│       │   │   │   │   └── CLDTextRequestParams.swift
│       │   │   │   └── Results/
│       │   │   │       ├── CLDDeleteResult.swift
│       │   │   │       ├── CLDExplicitResult.swift
│       │   │   │       ├── CLDExplodeResult.swift
│       │   │   │       ├── CLDMultiResult.swift
│       │   │   │       ├── CLDRenameResult.swift
│       │   │   │       ├── CLDSpriteResult.swift
│       │   │   │       ├── CLDTagResult.swift
│       │   │   │       └── CLDTextResult.swift
│       │   │   ├── UploadWidget/
│       │   │   │   ├── CLDUploaderWidget.swift
│       │   │   │   ├── CropView/
│       │   │   │   │   ├── CLDCropOverlayView.swift
│       │   │   │   │   ├── CLDCropScrollView.swift
│       │   │   │   │   ├── CLDCropScrollViewController.swift
│       │   │   │   │   ├── CLDCropView.swift
│       │   │   │   │   ├── CLDCropViewCalculator.swift
│       │   │   │   │   └── CLDCropViewUIManager.swift
│       │   │   │   ├── WidgetConfiguration/
│       │   │   │   │   └── CLDWidgetConfiguration.swift
│       │   │   │   ├── WidgetElements/
│       │   │   │   │   ├── CLDWidgetAssetContainer.swift
│       │   │   │   │   └── CLDWidgetPreviewCollectionCell.swift
│       │   │   │   ├── WidgetImages/
│       │   │   │   │   ├── BackIconInstructions.swift
│       │   │   │   │   ├── CropIconInstructions.swift
│       │   │   │   │   ├── CropRotateIconInstructions.swift
│       │   │   │   │   ├── DoneIconInstructions.swift
│       │   │   │   │   ├── RatioLockedIconInstructions.swift
│       │   │   │   │   ├── RatioOpenedIconInstructions.swift
│       │   │   │   │   └── RotateIconInstructions.swift
│       │   │   │   ├── WidgetVideo/
│       │   │   │   │   ├── CLDDisplayLinkObserver.swift
│       │   │   │   │   ├── CLDVideoControlsState.swift
│       │   │   │   │   ├── CLDVideoControlsView.swift
│       │   │   │   │   ├── CLDVideoHiddenAndPausedState.swift
│       │   │   │   │   ├── CLDVideoHiddenAndPlayingState.swift
│       │   │   │   │   ├── CLDVideoPlayerView.swift
│       │   │   │   │   ├── CLDVideoShownAndPausedState.swift
│       │   │   │   │   ├── CLDVideoShownAndPlayingState.swift
│       │   │   │   │   └── CLDVideoView.swift
│       │   │   │   └── WidgetViewControllers/
│       │   │   │       ├── CLDWidgetEditViewController.swift
│       │   │   │       ├── CLDWidgetPreviewViewController.swift
│       │   │   │       └── CLDWidgetViewController.swift
│       │   │   ├── Uploader/
│       │   │   │   ├── CLDUploader.swift
│       │   │   │   ├── Preupload/
│       │   │   │   │   └── CLDPreprocessChain.swift
│       │   │   │   ├── RequestParams/
│       │   │   │   │   └── CLDUploadRequestParams.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   ├── CLDDefaultUploadRequest.swift
│       │   │   │   │   ├── CLDUploadRequest.swift
│       │   │   │   │   └── CLDUploadRequestWrapper.swift
│       │   │   │   └── Results/
│       │   │   │       └── CLDUploadResult.swift
│       │   │   └── Url/
│       │   │       └── CLDUrl.swift
│       │   ├── Network/
│       │   │   ├── Adapter/
│       │   │   │   └── CLDNetworkAdapter.swift
│       │   │   ├── CLDDefaultNetworkAdapter.swift
│       │   │   ├── CLDNetworkCoordinator.swift
│       │   │   ├── NetworkRequest/
│       │   │   │   ├── CLDAsyncNetworkUploadRequest.swift
│       │   │   │   ├── CLDErrorRequest.swift
│       │   │   │   ├── CLDGenericNetworkRequest.swift
│       │   │   │   ├── CLDNetworkDataRequestImpl.swift
│       │   │   │   ├── CLDNetworkDownloadRequest.swift
│       │   │   │   └── CLDNetworkUploadRequest.swift
│       │   │   └── PrivacyInfo.xcprivacy
│       │   └── Utils/
│       │       ├── CLDBuildParamsUtils.swift
│       │       ├── CLDCryptoUtils.swift
│       │       ├── CLDDictionaryUtils.swift
│       │       ├── CLDError.swift
│       │       ├── CLDFileUtils.swift
│       │       ├── CLDImageGenerator/
│       │       │   └── CLDImageGenerator.swift
│       │       ├── CLDImageUtils.swift
│       │       ├── CLDJsonUtils.swift
│       │       ├── CLDLogManager.swift
│       │       └── CLDStringUtils.swift
│       └── ios/
│           ├── Extensions/
│           │   ├── CLDTransformation+Ios.swift
│           │   ├── ExtensionCLDDownloader.swift
│           │   ├── UIButton+Cloudinary.swift
│           │   ├── UIImageView+Cloudinary.swift
│           │   └── UIView+Cloudinary.swift
│           ├── NetworkRequest/
│           │   ├── CLDFetchAssetRequestImpl.swift
│           │   └── CLDFetchImageRequestImpl.swift
│           ├── UIViews/
│           │   ├── CLDResponsiveViewHelper.swift
│           │   └── CLDUIImageView.swift
│           ├── Uploader/
│           │   ├── CLDImagePreprocessChain.swift
│           │   ├── CLDPreprocessHelpers.swift
│           │   ├── CLDVideoPreprocessChain.swift
│           │   ├── CLDVideoPreprocessHelpers.swift
│           │   └── CLDVideoTranscode.swift
│           └── Video/
│               ├── Analytics/
│               │   ├── VideoAnalytics.swift
│               │   └── VideoEventsManager.swift
│               └── CLDVideoPlayer.swift
├── Cloudinary.podspec
├── Cloudinary.xcodeproj/
│   ├── Cloudinary_Info.plist
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       ├── IDEWorkspaceChecks.plist
│   │       └── WorkspaceSettings.xcsettings
│   └── xcshareddata/
│       └── xcschemes/
│           └── Cloudinary-Package.xcscheme
├── Example/
│   ├── Cloudinary/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   ├── Contents.json
│   │   │   │   └── app_icon 1.heic
│   │   │   ├── Contents.json
│   │   │   ├── Delivery/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── Inner/
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   ├── ski.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   └── sofa.imageset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── Video/
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   ├── instagram.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   ├── tiktok.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   └── youtube.imageset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── background_normalizing.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── color_alternation.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── delivery-city.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── localization_branding.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── smart_cropping.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Upload/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── question_mark.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── upload_missing.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Video/
│   │   │   │   ├── Contents.json
│   │   │   │   └── TikTok/
│   │   │   │       ├── Contents.json
│   │   │   │       ├── tiktok_bar_icon.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_comments.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_discover.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_home.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_inbox.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_like.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_more.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_music.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_note.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_share.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       └── tiktok_social_icon.imageset/
│   │   │   │           └── Contents.json
│   │   │   ├── Widgets/
│   │   │   │   ├── Contents.json
│   │   │   │   └── house.imageset/
│   │   │   │       ├── Contents.json
│   │   │   │       └── Frame_871_ao5o4r_2_viaeyi.heic
│   │   │   ├── back_arrow.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── car-speed-limiter.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── cloudinary_logo.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── info_icon.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── splash.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── splash_without_logo.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── upload.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── video.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── widgets.imageset/
│   │   │       └── Contents.json
│   │   ├── CldModel.xcdatamodeld/
│   │   │   └── CldModel.xcdatamodel/
│   │   │       └── contents
│   │   ├── Cloudinary_Example-Bridging-Header.h
│   │   ├── Colors.xcassets/
│   │   │   ├── Contents.json
│   │   │   ├── gradient-first.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── gradient-second.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── primary.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── secondary.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── size_green.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── size_red.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── surface.colorset/
│   │   │   │   └── Contents.json
│   │   │   └── text_not_selected.colorset/
│   │   │       └── Contents.json
│   │   ├── Controllers/
│   │   │   ├── Base/
│   │   │   │   └── BaseViewController.swift
│   │   │   ├── Inner Views/
│   │   │   │   ├── Transform/
│   │   │   │   │   ├── DeliveryViewController.swift
│   │   │   │   │   ├── Optimization/
│   │   │   │   │   │   └── OptimizationViewController.swift
│   │   │   │   │   ├── Transformation/
│   │   │   │   │   │   ├── DeliveryTransformCollectionController.swift
│   │   │   │   │   │   ├── Reveal Image/
│   │   │   │   │   │   │   └── RevealImageController.swift
│   │   │   │   │   │   ├── Smart Cropping/
│   │   │   │   │   │   │   └── SmartCroppingController.swift
│   │   │   │   │   │   ├── TransformCollectionCell.swift
│   │   │   │   │   │   ├── TransformCollectionController.swift
│   │   │   │   │   │   ├── TransformViewController.swift
│   │   │   │   │   │   └── TransformationCell.swift
│   │   │   │   │   └── Use Cases/
│   │   │   │   │       ├── DeliveryUseCasesCollectionController.swift
│   │   │   │   │       ├── Screens/
│   │   │   │   │       │   ├── ConditionalProductViewController.swift
│   │   │   │   │       │   ├── IntegrateAIViewController.swift
│   │   │   │   │       │   └── NormalizingProductAssetsViewController.swift
│   │   │   │   │       ├── UseCaseCell.swift
│   │   │   │   │       ├── UseCaseCollectionCell.swift
│   │   │   │   │       ├── UseCasesCollectionController.swift
│   │   │   │   │       └── UseCasesViewController.swift
│   │   │   │   ├── Upload/
│   │   │   │   │   ├── InnerUploadFrame.swift
│   │   │   │   │   ├── Single Upload/
│   │   │   │   │   │   ├── SingleUploadCell.swift
│   │   │   │   │   │   ├── SingleUploadCollectionController.swift
│   │   │   │   │   │   ├── SingleUploadCollectionLayout.swift
│   │   │   │   │   │   ├── SingleUploadPreview.swift
│   │   │   │   │   │   └── SingleUploadViewController.swift
│   │   │   │   │   ├── Upload Does Not Exist/
│   │   │   │   │   │   └── UploadDoesNotExistController.swift
│   │   │   │   │   ├── UploadChoiceController.swift
│   │   │   │   │   ├── UploadNoCloudController.swift
│   │   │   │   │   └── UploadViewController.swift
│   │   │   │   └── Video/
│   │   │   │       ├── Video Feed/
│   │   │   │       │   └── VideoFeedCell.swift
│   │   │   │       └── VideoFeedCollectionController.swift
│   │   │   ├── MainViewController.swift
│   │   │   ├── SplashViewController.swift
│   │   │   ├── Video Feed/
│   │   │   │   ├── Controllers/
│   │   │   │   │   ├── MainPageController.swift
│   │   │   │   │   ├── VideoFeedContainerController.swift
│   │   │   │   │   ├── VideoFeedController.swift
│   │   │   │   │   ├── VideoFeedViewController.swift
│   │   │   │   │   ├── VideoSocialOverlaysController.swift
│   │   │   │   │   └── VideoViewController.swift
│   │   │   │   ├── Helpers/
│   │   │   │   │   └── VideoHelper.swift
│   │   │   │   └── Resources/
│   │   │   │       └── video_links.plist
│   │   │   └── Widgets/
│   │   │       ├── ImageWidgetViewController.swift
│   │   │       ├── UploadWidgetViewController.swift
│   │   │       └── WidgetsViewController.swift
│   │   ├── Core Data/
│   │   │   ├── AssetItems+CoreDataClass.swift
│   │   │   ├── AssetItems+CoreDataProperties.swift
│   │   │   ├── AssetModel.swift
│   │   │   ├── AssetModel.xcdatamodeld/
│   │   │   │   └── Model.xcdatamodel/
│   │   │   │       └── contents
│   │   │   └── CoreDataHelper.swift
│   │   ├── Custom Views/
│   │   │   ├── GradientView.swift
│   │   │   ├── RevealImageView.swift
│   │   │   ├── ToolBar/
│   │   │   │   ├── Item/
│   │   │   │   │   ├── ToolbarItem.swift
│   │   │   │   │   └── ToolbarItem.xib
│   │   │   │   ├── Toolbar.swift
│   │   │   │   └── Toolbar.xib
│   │   │   └── Upload Loading View/
│   │   │       ├── UploadLoadingView.swift
│   │   │       └── UploadLoadingView.xib
│   │   ├── Extensions/
│   │   │   └── Double+Extension.swift
│   │   ├── GoogleService-Info.plist
│   │   ├── Helpers/
│   │   │   ├── AnimationHelper.swift
│   │   │   ├── Events/
│   │   │   │   ├── EventObject.swift
│   │   │   │   ├── EventsHandler.swift
│   │   │   │   ├── EventsProvider.swift
│   │   │   │   └── FirebaseEventsHandler.swift
│   │   │   └── ImageHelper.swift
│   │   ├── Info.plist
│   │   ├── SceneDelegate.swift
│   │   ├── Utils/
│   │   │   ├── CloudinaryHelper.swift
│   │   │   └── FileUtils.swift
│   │   └── Views/
│   │       ├── Base.lproj/
│   │       │   ├── LaunchScreen.storyboard
│   │       │   └── Main.storyboard
│   │       ├── Inner Views/
│   │       │   ├── Base/
│   │       │   │   └── Base.storyboard
│   │       │   ├── Transform/
│   │       │   │   ├── Delivery.storyboard
│   │       │   │   ├── Optimization.storyboard
│   │       │   │   ├── Use Cases/
│   │       │   │   │   ├── ConditionalProduct.storyboard
│   │       │   │   │   ├── IntegrateAI.storyboard
│   │       │   │   │   └── NormalizingProductAssets.storyboard
│   │       │   │   └── UseCases.storyboard
│   │       │   ├── Upload/
│   │       │   │   ├── InnerUploadFrame.xib
│   │       │   │   ├── Upload.storyboard
│   │       │   │   └── UploadNoCloud.storyboard
│   │       │   ├── Video/
│   │       │   │   ├── Social/
│   │       │   │   │   └── VideoSocialOverlays.storyboard
│   │       │   │   ├── Video.storyboard
│   │       │   │   └── VideoFeed.storyboard
│   │       │   └── Widgets/
│   │       │       ├── ImageWidget.storyboard
│   │       │       ├── UploadWidget.storyboard
│   │       │       └── Widgets.storyboard
│   │       ├── Splash.storyboard
│   │       ├── Transform/
│   │       │   ├── RevealImage.storyboard
│   │       │   ├── SmartCropping.storyboard
│   │       │   └── Transform.storyboard
│   │       └── Upload/
│   │           ├── SingleUpload.storyboard
│   │           ├── SingleUploadPreview.storyboard
│   │           ├── UploadChoice.storyboard
│   │           └── UploadDoesNotExist.storyboard
│   ├── Cloudinary.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       └── IDEWorkspaceChecks.plist
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           ├── Cloudinary-Example.xcscheme
│   │           └── travis_public_scheme.xcscheme
│   ├── Podfile
│   ├── README.md
│   ├── Tests/
│   │   ├── BaseNetwork/
│   │   │   ├── BaseTestCase.swift
│   │   │   ├── Core/
│   │   │   │   ├── AuthenticationTests.swift
│   │   │   │   ├── CLDCloudinaryTests.m
│   │   │   │   ├── CLDCloudinaryTests.swift
│   │   │   │   ├── ParameterEncodingTests.swift
│   │   │   │   ├── RequestTests.swift
│   │   │   │   ├── ResponseTests.swift
│   │   │   │   ├── ResultTests.swift
│   │   │   │   ├── SessionDelegateTests.swift
│   │   │   │   ├── SessionManagerTests.swift
│   │   │   │   └── UploadTests.swift
│   │   │   ├── Extensions/
│   │   │   │   ├── CLDNDataResponse+CloudinaryTests.swift
│   │   │   │   ├── CLDNError+CloudinaryTests.swift
│   │   │   │   ├── CLDNResult+CloudinaryTests.swift
│   │   │   │   └── FileManager+CloudinaryTests.swift
│   │   │   ├── Features/
│   │   │   │   ├── CacheTests.swift
│   │   │   │   ├── MultipartFormDataTests.swift
│   │   │   │   ├── ResponseSerializationTests.swift
│   │   │   │   ├── URLProtocolTests.swift
│   │   │   │   └── ValidationTests.swift
│   │   │   ├── ObjcBaseTestCase.h
│   │   │   ├── ObjcBaseTestCase.m
│   │   │   └── Resources/
│   │   │       └── Responses/
│   │   │           ├── JSON/
│   │   │           │   ├── empty_data.json
│   │   │           │   ├── invalid_data.json
│   │   │           │   └── valid_data.json
│   │   │           ├── Property List/
│   │   │           │   ├── empty.data
│   │   │           │   ├── invalid.data
│   │   │           │   └── valid.data
│   │   │           └── String/
│   │   │               ├── empty_string.txt
│   │   │               ├── utf32_string.txt
│   │   │               └── utf8_string.txt
│   │   ├── ConfigurationTests/
│   │   │   ├── CLDAnalyticsTests.swift
│   │   │   ├── CLDConfigurationTests.m
│   │   │   └── CLDConfigurationTests.swift
│   │   ├── CryptoUtilsTests/
│   │   │   ├── CryptoUtilsTests.m
│   │   │   └── CryptoUtilsTests.swift
│   │   ├── FileUtilsTests.swift
│   │   ├── GenerateUrlTests/
│   │   │   ├── UrlTests.m
│   │   │   └── UrlTests.swift
│   │   ├── Info.plist
│   │   ├── NetworkTests/
│   │   │   ├── AccessibilityUploderTests/
│   │   │   │   ├── UploaderAccessibilityTests.m
│   │   │   │   └── UploaderAccessibilityTests.swift
│   │   │   ├── DownloaderAssetTests.swift
│   │   │   ├── DownloaderTests.swift
│   │   │   ├── ManagementApiTests.swift
│   │   │   ├── NetworkBaseTests/
│   │   │   │   ├── NetworkBaseTest.swift
│   │   │   │   ├── NetworkBaseTestObjc.h
│   │   │   │   └── NetworkBaseTestObjc.m
│   │   │   ├── NetworkTestUtils.swift
│   │   │   └── UploaderTests/
│   │   │       ├── MockProvider/
│   │   │       │   └── BaseMockProvider.swift
│   │   │       ├── OcrUploaderTests/
│   │   │       │   ├── ExplicitMockOcrTests.m
│   │   │       │   ├── ExplicitMockOcrTests.swift
│   │   │       │   ├── OcrMockProvider.swift
│   │   │       │   ├── UploaderMockOcrTests.m
│   │   │       │   ├── UploaderMockOcrTests.swift
│   │   │       │   └── UploaderOcrTests.swift
│   │   │       ├── PreprocessUploaderTests/
│   │   │       │   └── PreprocessUploaderTests.swift
│   │   │       ├── QualityAnalysisUploaderTests/
│   │   │       │   ├── MockProviderQualityAnalysis.swift
│   │   │       │   ├── ObjcQualityAnalysisExplicitResultParserTests.m
│   │   │       │   ├── ObjcQualityAnalysisUploadResultParserTests.m
│   │   │       │   ├── ObjcUploaderQualityAnalysisTests.m
│   │   │       │   ├── QualityAnalysisExplicitResultParserTests.swift
│   │   │       │   ├── QualityAnalysisUploadResultParserTests.swift
│   │   │       │   └── UploaderQualityAnalysisTests.swift
│   │   │       └── UploaderTests.swift
│   │   ├── ParamTests/
│   │   │   └── UploadRequestParamsTests.swift
│   │   ├── Preprocess/
│   │   │   ├── PreprocessTests.swift
│   │   │   └── VideoPreprocessTests.swift
│   │   ├── Resources/
│   │   │   └── Docs/
│   │   │       └── docx.docx
│   │   ├── StringUtilsTest.swift
│   │   ├── TestableCloudinary.swift
│   │   ├── TransformationTests/
│   │   │   ├── CLDConditionExpressionTests/
│   │   │   │   ├── CLDConditionExpressionHelpersTests.swift
│   │   │   │   ├── CLDConditionExpressionTests.m
│   │   │   │   └── CLDConditionExpressionTests.swift
│   │   │   ├── CLDExpressionTests/
│   │   │   │   ├── CLDExpressionTests.m
│   │   │   │   └── CLDExpressionTests.swift
│   │   │   ├── CLDTransformationTests/
│   │   │   │   ├── CLDTransformationBaselineTests.swift
│   │   │   │   ├── CLDTransformationConditionsTests.swift
│   │   │   │   ├── CLDTransformationExpressionsTests.swift
│   │   │   │   ├── CLDTransformationTests.m
│   │   │   │   ├── CLDTransformationTests.swift
│   │   │   │   └── CLDTransformationVariablesTests.swift
│   │   │   └── CLDVariableTests/
│   │   │       ├── CLDVariableTests.m
│   │   │       └── CLDVariableTests.swift
│   │   ├── UIExtensions/
│   │   │   ├── CLDVideoPlayerTests.swift
│   │   │   ├── UIBaseTest.swift
│   │   │   ├── UIButtonTests.swift
│   │   │   ├── UIImageViewTests.swift
│   │   │   └── Video Analytics/
│   │   │       ├── VideoEventsManagerTests.swift
│   │   │       └── VideoEventsTests.swift
│   │   └── UploadWidgetTests/
│   │       ├── UploadWidgetHelpersTests/
│   │       │   ├── UploaderWidgetAssetContainerTests.swift
│   │       │   ├── UploaderWidgetConfigurationTests.m
│   │       │   └── UploaderWidgetConfigurationTests.swift
│   │       ├── UploadWidgetVideoTests/
│   │       │   ├── UploaderWidgetVideoControlsTests.swift
│   │       │   ├── UploaderWidgetVideoDisplayLinkTests.swift
│   │       │   ├── UploaderWidgetVideoPlayerTests.swift
│   │       │   └── UploaderWidgetVideoViewTests.swift
│   │       ├── UploaderWidgetEditTests/
│   │       │   └── UploaderWidgetEditViewControllerTests.swift
│   │       ├── UploaderWidgetPreviewTests/
│   │       │   ├── UploaderWidgetCollectionCellTests.swift
│   │       │   └── UploaderWidgetPreviewViewControllerTests.swift
│   │       ├── UploaderWidgetTests/
│   │       │   ├── UploaderWidgetTests.m
│   │       │   └── UploaderWidgetTests.swift
│   │       ├── UploaderWidgetViewControllerTests/
│   │       │   └── UploaderWidgetViewControllerTests.swift
│   │       └── WidgetBaseTest.swift
│   └── WidgetUITests/
│       ├── Info.plist
│       └── WidgetUITests.swift
├── LICENSE
├── Package.swift
├── Package@swift-5.3.swift
├── README.md
└── tools/
    └── get_test_cloud.sh

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

## Bug report for Cloudinary iOS SDK
Before proceeding, please update to latest version and test if the issue persists

## Describe the bug in a sentence or two.
…

## Issue Type (Can be multiple)
[ ] Build - Can’t install or import the SDK
[ ] Performance - Performance issues
[ ] Behaviour - Functions aren’t working as expected (Such as generate URL)
[ ] Documentation - Inconsistency between the docs and behaviour
[ ] Other (Specify)

## Steps to reproduce
… if applicable

## Error screenshots or Stack Trace (if applicable)
…

## Build/Dependency management
[ ] Cocoa-Pods
[ ] Carthage
[ ] Manual import
[ ] Other (Specify)

## Is the issue reproducible only on a specific device?
[ ] No
[ ] Yes (specify model + iOS version)

## Versions and Libraries (fill in the version numbers)
iOS Cloudinary SDK version - 0.0.0
OSX (on the dev environment) - 0.0.0 
XCode - 0.0.0
Swift - 0.0.0
Target iOS - 0.0.0

Repository
If possible, please provide a link to a reproducible repository that showcases the problem


================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

## Feature request for Cloudinary iOS SDK
…(If your feature is for other SDKs, please request them there)

## Explain your use case
… (A high level explanation of why you need this feature)

## Describe the problem you’re trying to solve
… (A more technical view of what you’d like to accomplish, and how this feature will help you achieve it)

## Do you have a proposed solution?
… (yes, no? Please elaborate if needed)


================================================
FILE: .github/pull_request_template.md
================================================
### Brief Summary of Changes
<!-- Provide some context as to what was changed, from an implementation standpoint. -->

#### What does this PR address?
- [ ] GitHub issue (Add reference - #XX)
- [ ] Refactoring
- [ ] New feature
- [ ] Bug fix
- [ ] Adds more tests

#### Are tests included?
- [ ] Yes
- [ ] No

#### Reviewer, please note:
<!--
List anything here that the reviewer should pay special attention to. This might
include, for example:
* Dependence on other PRs
* Reference to other Cloudinary SDKs
* Changes that seem arbitrary without further explanations
-->

#### Checklist:
<!--- Go over all the following points, and put an `x` in all the boxes that apply. -->
<!--- If you're unsure about any of these, don't hesitate to ask. We're here to help! -->
- [ ] My code follows the code style of this project.
- [ ] My change requires a change to the documentation.
- [ ] I ran the full test suite before pushing the changes and all the tests pass.


================================================
FILE: .gitignore
================================================
# Xcode
#
# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore

*.DS_Store

## Build generated
build/
DerivedData/

## Various settings
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata/

## Other
*.moved-aside
*.xcuserstate

## Obj-C/Swift specific
*.hmap
*.ipa
*.dSYM.zip
*.dSYM

## Playgrounds
timeline.xctimeline
playground.xcworkspace

# Swift Package Manager
#
# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
# Packages/
.build/

# CocoaPods
#
# We recommend against adding the Pods directory to your .gitignore. However
# you should judge for yourself, the pros and cons are mentioned at:
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
#
# Pods/
_Pods.xcodeproj

# Carthage
#
# Add this line if you want to avoid checking in source code from Carthage dependencies.
Example/Carthage/Checkouts

Example/Carthage/Build

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md

fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
iOSInjectionProject/

Cloudinary/Frameworks/CLDCrypto

# Samples
AppDelegate+config.swift
Example/Pods/
Example/Podfile.lock
Example/Cloudinary.xcworkspace/



================================================
FILE: .gitmodules
================================================



================================================
FILE: .swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
================================================
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
   version = "1.0">
   <FileRef
      location = "self:">
   </FileRef>
</Workspace>


================================================
FILE: .travis.yml
================================================
language: objective-c
os: osx
before_script: >
  export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);
  echo cloud_name: "$(echo $CLOUDINARY_URL | cut -d'@' -f2)"
notifications:
  email:
    recipients:
      - sdk_developers@cloudinary.com
jobs:
    - osx_image: xcode12
      xcode_workspace: Example/Cloudinary.xcworkspace
      xcode_scheme: travis_public_scheme
      podfile: Example/Podfile
      install: pod install --project-directory=Example
      env: CLOUDINARY_URL=$CLOUDINARY_URL
      xcode_destination: platform=iOS Simulator,OS=13.0,name=iPhone 8
    - osx_image: xcode12
      xcode_workspace: Example/Cloudinary.xcworkspace
      xcode_scheme: travis_public_scheme
      podfile: Example/Podfile
      install: pod install --project-directory=Example
      env: CLOUDINARY_URL=$CLOUDINARY_URL
      xcode_destination: platform=iOS Simulator,OS=14.0,name=iPhone 8
    - osx_image: xcode13.2
      xcode_workspace: Example/Cloudinary.xcworkspace
      xcode_scheme: travis_public_scheme
      podfile: Example/Podfile
      install: pod install --project-directory=Example
      env: CLOUDINARY_URL=$CLOUDINARY_URL
      xcode_destination: platform=iOS Simulator,OS=15.2,name=iPhone 8


================================================
FILE: CHANGELOG.md
================================================
5.2.5 / 2026-01-08
==================

* Remove _Pods.xcodeproj

5.2.4 / 2025-11-30
==================

Other changes
-------------
* Fix duration attribute for video analytics 

5.2.3 / 2025-08-12
==================

Other Changes
-------------
* Add video analytics duration event
* Add flush events support to video analytics

5.2.2 / 2025-07-30
==================

Other Changes
--------------
* Fix video analytics duration event

5.2.1 / 2025-06-25
==================

Other Changes
--------------
* Add video analytics as public API

5.2.0 / 2025-06-18
==================

New functionality
-----------------
* Add support for `auto_chaptering` and `auto_transcription` in Upload API
* Add extra headers support

Other Changes
--------------
* Fix API parameters signature


5.1.0 / 2024-09-17
==================

New functionality
-----------------
* Add video transcode
* Add auto_chaptering and auto_transcription to Upload API

5.0.0 / 2024-04-14
==================

Breaking Changes
----------------
* Remove CLDURLCache in favor of native URLCache
* Remove ImageCache
* Old cache saved to the disk will be purged

New functionality
-----------------
* Add URLCache support for `CLDUIImageVIew`


4.7.0 / 2024-03-25
==================

Other Changes
-------------
  * Fix privacy manifest
  * Fix `CLDVideoPlayer`


4.6.0 / 2024-03-12
==================

New functionality
-----------------
* Add support for `media_metadata`

Other Changes
-------------
* Fix video analytics

4.5.0 / 2024-02-18
==================

Other Changes
-------------
  * Add privacy manifest

4.4.0 / 2024-01-15
==================

Other Changes
-------------
  * Update analytics token

4.3.0 / 2023-12-25
==================

Other Changes
-------------
  * Add video player analytics

4.2.0 / 2023-10-12
==================

Other Changes
-------------
  * Update analytics token

4.1.1 / 2023-09-18
==================

Other Changes
-------------
  * Fix analytics import


4.1.0 / 2023-08-06
==================

New functionality
-----------------
  * Add video player widget

4.0.1 / 2023-06-12
==================

Other changes
-------------
  * Fix analytics token prefix

4.0.0 / 2023-05-22
==================

New functionality
-----------------
  * Turn on `CLDURLCache` on by default
  
Other changes
-------------
  * Make upload request respect timeout

3.4.0 / 2023-03-12
==================

New functionality
-----------------
  * Add URLCache support for images
  * Add tests for explicit and rename
  
Other changes
-------------
  * Fix warning using `URLCredentialStorage`

3.3.0 / 2022-06-12
==================

New functionality
-----------------
  * Add support for folder decoupling
  * Add support for `startOffset` and `endOffset` as expression
  * Allow to disable b-frames
  * Send tags as an array
  * Add support for `originalFilename` upload parameter

Other changes
-------------
  * Fix expression normalisation

3.2.1 / 2022-01-11
==================
  * Fix `Carthage`

3.2.0 / 2022-01-10
==================

New functionality
-----------------
  * Add support for `apiKey` argument in Upload API
  * Add support for `preview` effect
  
Other changes
-------------
  * Update travis job to support multiple iOS versions
  * Add test for preview effect with duration parameter
  * Recover `Cloudinary.xcodeproj` file
  * Fix `ImageView` size in preview widget `CollectionView`
  * Improve network error handling
  * Add `PNG` image upload unit tests

3.1.0 / 2021-12-03
==================

New functionality
-----------------
* Add support for `backgroundRemoval` upload parameter

Other changes
-------------
  * Fix `Carthage`

3.0.3 / 2021-11-14
==================

  * Fix error handling in `fetchImage`

3.0.2 / 2021-11-02
==================

  * Improve error handling in `fetchImage`

3.0.1 / 2021-08-31
=============

  * Fix deprecation warnings

3.0.0 / 2021-04-26
=============

Breaking changes
-----------------
  * Bump minimum iOS target version to 9.0 (#334)

New functionality
-----------------
  * Add new url cache system (#331)
  * Add operationQueue names (#333)
  * Add support for video assets in upload widget (#322)

Other changes
-------------
  * Fix image assets for tests in multiple OS versions (#329)
  * Add missing imports for SPM (#326)

2.10.1 / 2021-01-20
==================

  * Re-add `Cloudinary.xscheme` to fix carthage support (#324)

2.10.0 / 2021-01-10
==================

New functionality
-----------------
* Add Upload Widget (#320)
* Add support for crop preprocess action (#263)
* Add support for rotation preprocess action (#264)
* Add support for enhanced quality analysis scores
* Add support for user defined variables and conditional expressions (#238)
* Support array of values for radius (#235)
* Add `eval` parameter to the upload Params.
* Add support for accessibility analysis (#260)
* Add option to control url signature algorithm (#256)
* Add support for custom pre-functions (#253)
* Add support for Long signature in URLs (#250)
* Add global timeout support(#251)
* Add OCR support un transformations and Upload APIs.

Other changes
-------------
* Add checks to validate responsive transformation.
* Fix space encoding in a generated URL (#274)
* Change local cache-keys encoding to `sha256`
* Fix OCR parameter usage in `UploadRequestParamsTests` (#262)
* Support urls with mime-type suffix in uploader.
* Update SPM definitions file (#234)

2.9.0 / 2020-04-16
==================
* Remove Alamofire dependency (#229)
* Add Swift Package Manager support.
* Add Carthage support.
* Rearrange workspace (#226)
* Add support for `assist_colorblind` effect. (#227)
* Validate URL scheme (#224)


2.9.0-beta.2 / 2020-04-05
=========================

  * Remove Alamofire dependency (#229)

2.9.0-beta.1 / 2020-03-23
=========================

  * Add Swift Package Manager support.
  * Add Carthage support.
  * Rearrange workspace (#226)
  * Add support for `assist_colorblind` effect. (#227)
  * Validate URL scheme (#224)

2.8.0 / 2019-11-11
==================

  * Add shared scheme (#220)
  * Add support for artistic filters. (#214)
  * Fix chunk upload to keep original file name. (#217)
  * Fix face coordinates test. (#215)

2.7.0 / 2019-04-07
==================

New functionality
-----------------
  * Support swift 5, fix related warnings.
  * Bump Alamofire version to 4.8.2
  * Add support for fetch layer in transformations. (#197)
  * Add support for `quality_analysis` parameter in upload. (#195)
  * Add support for font antialiasing and hinting in text overlays. (#193)
  * Add support for `streaming_profile` param in `CLDTransformation`. (#194)
  * Support excluding the default version from the generated cloudinary-url. (#206)
  * Add quality_override parameter to upload/explicit. (#199)
  
Other changes
-------------
  * Replace crypto kit files with `CommonCrypto` import.
  * Fix memory cache limit not getting initially set in `CLDImageCache` (#201)
  * Fix bas64 string validation in uploader. (#196)

2.6.1 / 2019-01-30
==================

  * Fix project setup and tests for swift 4.2 (#191)

2.6.0 / 2019-01-29
==================

New functionality
-----------------
  * Add support for google storage (`gs://`) urls in uploader. (#184)
  * Add support for `fps` parameter in Transformations. (#182)
  * Add format field to responsive breakpoints in upload params (#152)
  * Add `removeFromCache` to `CLDCloudinary`

Other changes
-------------
  * Bump Alamofire version to 4.8.1
  * Add device and os data to user agent. (#180)
  * Remove duplicate Alamofire reference
  
2.5.0 / 2018-11-05
==================

  * Migrate to Swift 4.2
  * Add support for custom transformation functions
  * Fix duplicate upload callbacks when using preprocessing
  * Bump Alamofire to version to 4.7.3
  * Add String extensions for base64 encoding
  * Add `@discardableResult` annotations for uploadLarge methods

2.4.0 / 2018-06-26
==================

New functionality
-----------------
  * Add support for `async` upload parameter
  * Add support for eager transformation format
  * Add support for automatic quality in transformations. (#150)
  * Add cache max-memory config (#98)
  * Add Keyframe interval transformation parameter (#90)

Other changes
-------------
  * Refactor CLDBaseParam for compatibility with iOS 11.4
  * Remove wrong project config (library search paths)
  * Bump Alamofire version to 4.7.2
  * Fix Alamofire submodule definition
  * Fix signature parameter handling in Management Api. (#161)
  * Fix `faceCenter` and `facesCenter` values in `CLDGravity` (#159)
  * Fix calculation of UIImage memory cache cost

2.3.0 / 2018-03-16
==================

  * Add access control parameter to upload (#142)

2.2.2 / 2018-03-05
==================

  * Add baseline Objective-C test.
  * Use `@objcMembers` where applies, for improved Objective-C compatibility.

2.2.1 / 2018-02-14
==================

   * Fix objective-c compatibility issues with `CLDResponsiveParams` properties.

2.2.0 / 2018-02-01
==================

New functionality
-----------------

  * Add support for responsive image download
  * Add `auto` to `CLDGravity`
    
Other changes
-------------

  * Fix synchronization issue when using `cldSetImage()` on `UIViews` within view collections.

2.1.0 / 2018-01-04
==================

New functionality
-----------------

  * Add image preprocessing and validations
    * Resizing
    * Format and quality settings
    * Support for custom preprocessors and validators
    
Other changes
-------------

  * Remove custom operators
  * Update Alamofire to version 4.6.0

2.0.4 / 2017-12-20
==================

* Fix user-agent sdk version
* Replace CommonCrypto wrapper with CryptoKit based code
* Remove autotagging test (behaviour change)
* Support Swift 4

2.0.3 / 2017-11-23
==================

New functionality
-----------------
  * Add support for chunked upload

Other changes
-------------

  * Update Readme to point to HTTPS URLs of cloudinary.com
  * Fix manual installation repository url.

2.0.2 / 2017-06-08
==================

New functionality
-----------------

  * Support SEO suffix for private images.

Other changes
-------------

  * Escape `|` and `=` characters in context values.
  * Double encode commas and slashes instead of using special UTF-8 values
  * Generate CLDCrypto framework in every build. Fixes #80.
  * Removing extra CLDCrypto path from podspec
  * Update `README.md` Alamo version to 4.1.0
  * Add 3 images in PhotoViewController to demonstrate transformations.
  * Add progress indicator
  * Updated the framework and project deployment target to 8.0, updated podspec deployment target to 8.0
  * Fixed signed upload using CLDSignature - Added unit test to test a signed upload using CLDSignature

2.0.1 / 2017-01-24
==================

  * Fix pod install issue. Fixes #57.
  * Fix shellScript
  * Fix URLs in tests
  * Increment Alamofire to ~>4.1. Fixes #59.
  * Update configuration

2.0.1 / 2017-01-22
==================

  * Fix Framework path in podspec/xcconfig

2.0.0 / 2017-01-18
==================

  * New Swift 3.0 code


================================================
FILE: Cloudinary/Assets/.gitkeep
================================================


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNConvertible.swift
================================================
//
//  CLDNConvertible.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// Types adopting the `CLDNURLConvertible` protocol can be used to construct URLs, which are then used to construct
/// URL requests.
internal protocol CLDNURLConvertible {
    /// Returns a URL that conforms to RFC 2396 or throws an `Error`.
    ///
    /// - throws: An `Error` if the type cannot be converted to a `URL`.
    ///
    /// - returns: A URL or throws an `Error`.
    func CLDN_AsURL() throws -> URL
}

extension String: CLDNURLConvertible {
    /// Returns a URL if `self` represents a valid URL string that conforms to RFC 2396 or throws an `CLDNError`.
    ///
    /// - throws: An `CLDNError.invalidURL` if `self` is not a valid URL string.
    ///
    /// - returns: A URL or throws an `CLDNError`.
    internal func CLDN_AsURL() throws -> URL {
        guard let url = URL(string: self) else { throw CLDNError.invalidURL(url: self) }
        return url
    }
}

extension URL: CLDNURLConvertible {
    /// Returns self.
    internal func CLDN_AsURL() throws -> URL { return self }
}

extension URLComponents: CLDNURLConvertible {
    /// Returns a URL if `url` is not nil, otherwise throws an `Error`.
    ///
    /// - throws: An `CLDNError.invalidURL` if `url` is `nil`.
    ///
    /// - returns: A URL or throws an `CLDNError`.
    internal func CLDN_AsURL() throws -> URL {
        guard let url = url else { throw CLDNError.invalidURL(url: self) }
        return url
    }
}

// MARK: -

/// Types adopting the `CLDNURLRequestConvertible` protocol can be used to construct URL requests.
internal protocol CLDNURLRequestConvertible {
    /// Returns a URL request or throws if an `Error` was encountered.
    ///
    /// - throws: An `Error` if the underlying `URLRequest` is `nil`.
    ///
    /// - returns: A URL request.
    func CLDN_AsURLRequest() throws -> URLRequest
}

extension CLDNURLRequestConvertible {
    /// The URL request.
    internal var urlRequest: URLRequest? { return try? CLDN_AsURLRequest() }
}

extension URLRequest: CLDNURLRequestConvertible {
    /// Returns a URL request or throws if an `Error` was encountered.
    internal func CLDN_AsURLRequest() throws -> URLRequest { return self }
}

// MARK: -

extension URLRequest {
    /// Creates an instance with the specified `method`, `urlString` and `headers`.
    ///
    /// - parameter url:     The URL.
    /// - parameter method:  The HTTP method.
    /// - parameter headers: The HTTP headers. `nil` by default.
    ///
    /// - returns: The new `URLRequest` instance.
    internal init(url: CLDNURLConvertible, method: CLDNHTTPMethod, headers: CLDNHTTPHeaders? = nil) throws {
        let url = try url.CLDN_AsURL()

        self.init(url: url)

        httpMethod = method.rawValue

        if let headers = headers {
            for (headerField, headerValue) in headers {
                setValue(headerValue, forHTTPHeaderField: headerField)
            }
        }
    }

    func adapt(using adapter: CLDNRequestAdapter?) throws -> URLRequest {
        guard let adapter = adapter else { return self }
        return try adapter.CLDN_Adapt(self)
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNError.swift
================================================
//
//  CLDNError.swift
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// `CLDNError` is the error type returned by Cloudinary. It encompasses a few different types of errors, each with
/// their own associated reasons.
///
/// - invalidURL:                  Returned when a `URLConvertible` type fails to create a valid `URL`.
/// - parameterEncodingFailed:     Returned when a parameter encoding object throws an error during the encoding process.
/// - multipartEncodingFailed:     Returned when some step in the multipart encoding process fails.
/// - responseValidationFailed:    Returned when a `validate()` call fails.
/// - responseSerializationFailed: Returned when a response serializer encounters an error in the serialization process.
internal enum CLDNError: Error {
    /// The underlying reason the parameter encoding error occurred.
    ///
    /// - missingURL:                 The URL request did not have a URL to encode.
    /// - jsonEncodingFailed:         JSON serialization failed with an underlying system error during the
    ///                               encoding process.
    /// - propertyListEncodingFailed: Property list serialization failed with an underlying system error during
    ///                               encoding process.
    internal enum ParameterEncodingFailureReason {
        case missingURL
        case jsonEncodingFailed(error: Error)
        case propertyListEncodingFailed(error: Error)
    }

    /// The underlying reason the multipart encoding error occurred.
    ///
    /// - bodyPartURLInvalid:                   The `fileURL` provided for reading an encodable body part isn't a
    ///                                         file URL.
    /// - bodyPartFilenameInvalid:              The filename of the `fileURL` provided has either an empty
    ///                                         `lastPathComponent` or `pathExtension.
    /// - bodyPartFileNotReachable:             The file at the `fileURL` provided was not reachable.
    /// - bodyPartFileNotReachableWithError:    Attempting to check the reachability of the `fileURL` provided threw
    ///                                         an error.
    /// - bodyPartFileIsDirectory:              The file at the `fileURL` provided is actually a directory.
    /// - bodyPartFileSizeNotAvailable:         The size of the file at the `fileURL` provided was not returned by
    ///                                         the system.
    /// - bodyPartFileSizeQueryFailedWithError: The attempt to find the size of the file at the `fileURL` provided
    ///                                         threw an error.
    /// - bodyPartInputStreamCreationFailed:    An `InputStream` could not be created for the provided `fileURL`.
    /// - outputStreamCreationFailed:           An `OutputStream` could not be created when attempting to write the
    ///                                         encoded data to disk.
    /// - outputStreamFileAlreadyExists:        The encoded body data could not be writtent disk because a file
    ///                                         already exists at the provided `fileURL`.
    /// - outputStreamURLInvalid:               The `fileURL` provided for writing the encoded body data to disk is
    ///                                         not a file URL.
    /// - outputStreamWriteFailed:              The attempt to write the encoded body data to disk failed with an
    ///                                         underlying error.
    /// - inputStreamReadFailed:                The attempt to read an encoded body part `InputStream` failed with
    ///                                         underlying system error.
    internal enum MultipartEncodingFailureReason {
        case bodyPartURLInvalid(url: URL)
        case bodyPartFilenameInvalid(in: URL)
        case bodyPartFileNotReachable(at: URL)
        case bodyPartFileNotReachableWithError(atURL: URL, error: Error)
        case bodyPartFileIsDirectory(at: URL)
        case bodyPartFileSizeNotAvailable(at: URL)
        case bodyPartFileSizeQueryFailedWithError(forURL: URL, error: Error)
        case bodyPartInputStreamCreationFailed(for: URL)

        case outputStreamCreationFailed(for: URL)
        case outputStreamFileAlreadyExists(at: URL)
        case outputStreamURLInvalid(url: URL)
        case outputStreamWriteFailed(error: Error)

        case inputStreamReadFailed(error: Error)
    }

    /// The underlying reason the response validation error occurred.
    ///
    /// - dataFileNil:             The data file containing the server response did not exist.
    /// - dataFileReadFailed:      The data file containing the server response could not be read.
    /// - missingContentType:      The response did not contain a `Content-Type` and the `acceptableContentTypes`
    ///                            provided did not contain wildcard type.
    /// - unacceptableContentType: The response `Content-Type` did not match any type in the provided
    ///                            `acceptableContentTypes`.
    /// - unacceptableStatusCode:  The response status code was not acceptable.
    internal enum ResponseValidationFailureReason {
        case dataFileNil
        case dataFileReadFailed(at: URL)
        case missingContentType(acceptableContentTypes: [String])
        case unacceptableContentType(acceptableContentTypes: [String], responseContentType: String)
        case unacceptableStatusCode(code: Int)
    }

    /// The underlying reason the response serialization error occurred.
    ///
    /// - inputDataNil:                    The server response contained no data.
    /// - inputDataNilOrZeroLength:        The server response contained no data or the data was zero length.
    /// - inputFileNil:                    The file containing the server response did not exist.
    /// - inputFileReadFailed:             The file containing the server response could not be read.
    /// - stringSerializationFailed:       String serialization failed using the provided `String.Encoding`.
    /// - jsonSerializationFailed:         JSON serialization failed with an underlying system error.
    /// - propertyListSerializationFailed: Property list serialization failed with an underlying system error.
    internal enum ResponseSerializationFailureReason {
        case inputDataNil
        case inputDataNilOrZeroLength
        case inputFileNil
        case inputFileReadFailed(at: URL)
        case stringSerializationFailed(encoding: String.Encoding)
        case jsonSerializationFailed(error: Error)
        case propertyListSerializationFailed(error: Error)
    }

    case invalidURL(url: CLDNURLConvertible)
    case parameterEncodingFailed(reason: ParameterEncodingFailureReason)
    case multipartEncodingFailed(reason: MultipartEncodingFailureReason)
    case responseValidationFailed(reason: ResponseValidationFailureReason)
    case responseSerializationFailed(reason: ResponseSerializationFailureReason)
}

// MARK: - Adapt Error

struct AdaptError: Error {
    let error: Error
}

extension Error {
    var underlyingAdaptError: Error? { return (self as? AdaptError)?.error }
}

// MARK: - Error Booleans

extension CLDNError {
    /// Returns whether the CLDNError is an invalid URL error.
    internal var isInvalidURLError: Bool {
        if case .invalidURL = self { return true }
        return false
    }

    /// Returns whether the CLDNError is a parameter encoding error. When `true`, the `underlyingError` property will
    /// contain the associated value.
    internal var isParameterEncodingError: Bool {
        if case .parameterEncodingFailed = self { return true }
        return false
    }

    /// Returns whether the CLDNError is a multipart encoding error. When `true`, the `url` and `underlyingError` properties
    /// will contain the associated values.
    internal var isMultipartEncodingError: Bool {
        if case .multipartEncodingFailed = self { return true }
        return false
    }

    /// Returns whether the `CLDNError` is a response validation error. When `true`, the `acceptableContentTypes`,
    /// `responseContentType`, and `responseCode` properties will contain the associated values.
    internal var isResponseValidationError: Bool {
        if case .responseValidationFailed = self { return true }
        return false
    }

    /// Returns whether the `CLDNError` is a response serialization error. When `true`, the `failedStringEncoding` and
    /// `underlyingError` properties will contain the associated values.
    internal var isResponseSerializationError: Bool {
        if case .responseSerializationFailed = self { return true }
        return false
    }
}

// MARK: - Convenience Properties

extension CLDNError {
    /// The `URLConvertible` associated with the error.
    var urlConvertible: CLDNURLConvertible? {
        switch self {
        case .invalidURL(let url):
            return url
        default:
            return nil
        }
    }

    /// The `URL` associated with the error.
    internal var url: URL? {
        switch self {
        case .multipartEncodingFailed(let reason):
            return reason.url
        default:
            return nil
        }
    }

    /// The `Error` returned by a system framework associated with a `.parameterEncodingFailed`,
    /// `.multipartEncodingFailed` or `.responseSerializationFailed` error.
    internal var underlyingError: Error? {
        switch self {
        case .parameterEncodingFailed(let reason):
            return reason.underlyingError
        case .multipartEncodingFailed(let reason):
            return reason.underlyingError
        case .responseSerializationFailed(let reason):
            return reason.underlyingError
        default:
            return nil
        }
    }

    /// The acceptable `Content-Type`s of a `.responseValidationFailed` error.
    internal var acceptableContentTypes: [String]? {
        switch self {
        case .responseValidationFailed(let reason):
            return reason.acceptableContentTypes
        default:
            return nil
        }
    }

    /// The response `Content-Type` of a `.responseValidationFailed` error.
    internal var responseContentType: String? {
        switch self {
        case .responseValidationFailed(let reason):
            return reason.responseContentType
        default:
            return nil
        }
    }

    /// The response code of a `.responseValidationFailed` error.
    internal var responseCode: Int? {
        switch self {
        case .responseValidationFailed(let reason):
            return reason.responseCode
        default:
            return nil
        }
    }

    /// The `String.Encoding` associated with a failed `.stringResponse()` call.
    internal var failedStringEncoding: String.Encoding? {
        switch self {
        case .responseSerializationFailed(let reason):
            return reason.failedStringEncoding
        default:
            return nil
        }
    }
}

extension CLDNError.ParameterEncodingFailureReason {
    var underlyingError: Error? {
        switch self {
        case .jsonEncodingFailed(let error), .propertyListEncodingFailed(let error):
            return error
        default:
            return nil
        }
    }
}

extension CLDNError.MultipartEncodingFailureReason {
    var url: URL? {
        switch self {
        case .bodyPartURLInvalid(let url), .bodyPartFilenameInvalid(let url), .bodyPartFileNotReachable(let url),
             .bodyPartFileIsDirectory(let url), .bodyPartFileSizeNotAvailable(let url),
             .bodyPartInputStreamCreationFailed(let url), .outputStreamCreationFailed(let url),
             .outputStreamFileAlreadyExists(let url), .outputStreamURLInvalid(let url),
             .bodyPartFileNotReachableWithError(let url, _), .bodyPartFileSizeQueryFailedWithError(let url, _):
            return url
        default:
            return nil
        }
    }

    var underlyingError: Error? {
        switch self {
        case .bodyPartFileNotReachableWithError(_, let error), .bodyPartFileSizeQueryFailedWithError(_, let error),
             .outputStreamWriteFailed(let error), .inputStreamReadFailed(let error):
            return error
        default:
            return nil
        }
    }
}

extension CLDNError.ResponseValidationFailureReason {
    var acceptableContentTypes: [String]? {
        switch self {
        case .missingContentType(let types), .unacceptableContentType(let types, _):
            return types
        default:
            return nil
        }
    }

    var responseContentType: String? {
        switch self {
        case .unacceptableContentType(_, let responseType):
            return responseType
        default:
            return nil
        }
    }

    var responseCode: Int? {
        switch self {
        case .unacceptableStatusCode(let code):
            return code
        default:
            return nil
        }
    }
}

extension CLDNError.ResponseSerializationFailureReason {
    var failedStringEncoding: String.Encoding? {
        switch self {
        case .stringSerializationFailed(let encoding):
            return encoding
        default:
            return nil
        }
    }

    var underlyingError: Error? {
        switch self {
        case .jsonSerializationFailed(let error), .propertyListSerializationFailed(let error):
            return error
        default:
            return nil
        }
    }
}

// MARK: - Error Descriptions

extension CLDNError: LocalizedError {
    internal var errorDescription: String? {
        switch self {
        case .invalidURL(let url):
            return "URL is not valid: \(url)"
        case .parameterEncodingFailed(let reason):
            return reason.localizedDescription
        case .multipartEncodingFailed(let reason):
            return reason.localizedDescription
        case .responseValidationFailed(let reason):
            return reason.localizedDescription
        case .responseSerializationFailed(let reason):
            return reason.localizedDescription
        }
    }
}

extension CLDNError.ParameterEncodingFailureReason {
    var localizedDescription: String {
        switch self {
        case .missingURL:
            return "URL request to encode was missing a URL"
        case .jsonEncodingFailed(let error):
            return "JSON could not be encoded because of error:\n\(error.localizedDescription)"
        case .propertyListEncodingFailed(let error):
            return "PropertyList could not be encoded because of error:\n\(error.localizedDescription)"
        }
    }
}

extension CLDNError.MultipartEncodingFailureReason {
    var localizedDescription: String {
        switch self {
        case .bodyPartURLInvalid(let url):
            return "The URL provided is not a file URL: \(url)"
        case .bodyPartFilenameInvalid(let url):
            return "The URL provided does not have a valid filename: \(url)"
        case .bodyPartFileNotReachable(let url):
            return "The URL provided is not reachable: \(url)"
        case .bodyPartFileNotReachableWithError(let url, let error):
            return (
                "The system returned an error while checking the provided URL for " +
                "reachability.\nURL: \(url)\nError: \(error)"
            )
        case .bodyPartFileIsDirectory(let url):
            return "The URL provided is a directory: \(url)"
        case .bodyPartFileSizeNotAvailable(let url):
            return "Could not fetch the file size from the provided URL: \(url)"
        case .bodyPartFileSizeQueryFailedWithError(let url, let error):
            return (
                "The system returned an error while attempting to fetch the file size from the " +
                "provided URL.\nURL: \(url)\nError: \(error)"
            )
        case .bodyPartInputStreamCreationFailed(let url):
            return "Failed to create an InputStream for the provided URL: \(url)"
        case .outputStreamCreationFailed(let url):
            return "Failed to create an OutputStream for URL: \(url)"
        case .outputStreamFileAlreadyExists(let url):
            return "A file already exists at the provided URL: \(url)"
        case .outputStreamURLInvalid(let url):
            return "The provided OutputStream URL is invalid: \(url)"
        case .outputStreamWriteFailed(let error):
            return "OutputStream write failed with error: \(error)"
        case .inputStreamReadFailed(let error):
            return "InputStream read failed with error: \(error)"
        }
    }
}

extension CLDNError.ResponseSerializationFailureReason {
    var localizedDescription: String {
        switch self {
        case .inputDataNil:
            return "Response could not be serialized, input data was nil."
        case .inputDataNilOrZeroLength:
            return "Response could not be serialized, input data was nil or zero length."
        case .inputFileNil:
            return "Response could not be serialized, input file was nil."
        case .inputFileReadFailed(let url):
            return "Response could not be serialized, input file could not be read: \(url)."
        case .stringSerializationFailed(let encoding):
            return "String could not be serialized with encoding: \(encoding)."
        case .jsonSerializationFailed(let error):
            return "JSON could not be serialized because of error:\n\(error.localizedDescription)"
        case .propertyListSerializationFailed(let error):
            return "PropertyList could not be serialized because of error:\n\(error.localizedDescription)"
        }
    }
}

extension CLDNError.ResponseValidationFailureReason {
    var localizedDescription: String {
        switch self {
        case .dataFileNil:
            return "Response could not be validated, data file was nil."
        case .dataFileReadFailed(let url):
            return "Response could not be validated, data file could not be read: \(url)."
        case .missingContentType(let types):
            return (
                "Response Content-Type was missing and acceptable content types " +
                "(\(types.joined(separator: ","))) do not match \"*/*\"."
            )
        case .unacceptableContentType(let acceptableTypes, let responseType):
            return (
                "Response Content-Type \"\(responseType)\" does not match any acceptable types: " +
                "\(acceptableTypes.joined(separator: ","))."
            )
        case .unacceptableStatusCode(let code):
            return "Response status code was unacceptable: \(code)."
        }
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNMultipartFormData.swift
================================================
//
//  CLDNMultipartFormData.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

#if os(iOS) || os(watchOS) || os(tvOS)
import MobileCoreServices
#elseif os(macOS)
import CoreServices
#endif

/// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
/// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
/// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
/// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
/// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
///
/// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
/// and the w3 form documentation.
///
/// - https://www.ietf.org/rfc/rfc2388.txt
/// - https://www.ietf.org/rfc/rfc2045.txt
/// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
internal class CLDNMultipartFormData {

    // MARK: - Helper Types

    struct EncodingCharacters {
        static let crlf = "\r\n"
    }

    struct BoundaryGenerator {
        enum BoundaryType {
            case initial, encapsulated, final
        }

        static func randomBoundary() -> String {
            return String(format: "Cloudinary.boundary.%08x%08x", arc4random(), arc4random())
        }

        static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
            let boundaryText: String

            switch boundaryType {
            case .initial:
                boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
            case .encapsulated:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
            case .final:
                boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
            }

            return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
        }
    }

    class BodyPart {
        let headers: CLDNHTTPHeaders
        let bodyStream: InputStream
        let bodyContentLength: UInt64
        var hasInitialBoundary = false
        var hasFinalBoundary = false

        init(headers: CLDNHTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
            self.headers = headers
            self.bodyStream = bodyStream
            self.bodyContentLength = bodyContentLength
        }
    }

    // MARK: - Properties

    /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
    internal lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"

    /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
    internal var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }

    /// The boundary used to separate the body parts in the encoded form data.
    internal var boundary: String

    private var bodyParts: [BodyPart]
    private var bodyPartError: CLDNError?
    private let streamBufferSize: Int

    // MARK: - Lifecycle

    /// Creates a multipart form data object.
    ///
    /// - returns: The multipart form data object.
    internal init() {
        self.boundary = BoundaryGenerator.randomBoundary()
        self.bodyParts = []

        ///
        /// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
        /// information, please refer to the following article:
        ///   - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
        ///

        self.streamBufferSize = 1024
    }

    // MARK: - Body Parts

    /// Creates a body part from the data and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
    /// - Encoded data
    /// - Multipart form boundary
    ///
    /// - parameter data: The data to encode into the multipart form data.
    /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
    internal func append(_ data: Data, withName name: String) {
        let headers = contentHeaders(withName: name)
        let stream = InputStream(data: data)
        let length = UInt64(data.count)

        append(stream, withLength: length, headers: headers)
    }

    /// Creates a body part from the data and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
    /// - `Content-Type: #{generated mimeType}` (HTTP Header)
    /// - Encoded data
    /// - Multipart form boundary
    ///
    /// - parameter data:     The data to encode into the multipart form data.
    /// - parameter name:     The name to associate with the data in the `Content-Disposition` HTTP header.
    /// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header.
    internal func append(_ data: Data, withName name: String, mimeType: String) {
        let headers = contentHeaders(withName: name, mimeType: mimeType)
        let stream = InputStream(data: data)
        let length = UInt64(data.count)

        append(stream, withLength: length, headers: headers)
    }

    /// Creates a body part from the data and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
    /// - `Content-Type: #{mimeType}` (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// - parameter data:     The data to encode into the multipart form data.
    /// - parameter name:     The name to associate with the data in the `Content-Disposition` HTTP header.
    /// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header.
    /// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header.
    internal func append(_ data: Data, withName name: String, fileName: String, mimeType: String) {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
        let stream = InputStream(data: data)
        let length = UInt64(data.count)

        append(stream, withLength: length, headers: headers)
    }

    /// Creates a body part from the file and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
    /// - `Content-Type: #{generated mimeType}` (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
    /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
    /// system associated MIME type.
    ///
    /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
    /// - parameter name:    The name to associate with the file content in the `Content-Disposition` HTTP header.
    internal func append(_ fileURL: URL, withName name: String) {
        let fileName = fileURL.lastPathComponent
        let pathExtension = fileURL.pathExtension

        if !fileName.isEmpty && !pathExtension.isEmpty {
            let mime = mimeType(forPathExtension: pathExtension)
            append(fileURL, withName: name, fileName: fileName, mimeType: mime)
        } else {
            setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
        }
    }

    /// Creates a body part from the file and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
    /// - Content-Type: #{mimeType} (HTTP Header)
    /// - Encoded file data
    /// - Multipart form boundary
    ///
    /// - parameter fileURL:  The URL of the file whose content will be encoded into the multipart form data.
    /// - parameter name:     The name to associate with the file content in the `Content-Disposition` HTTP header.
    /// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header.
    /// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header.
    internal func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)

        //============================================================
        //                 Check 1 - is file URL?
        //============================================================

        guard fileURL.isFileURL else {
            setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
            return
        }

        //============================================================
        //              Check 2 - is file URL reachable?
        //============================================================

        do {
            let isReachable = try fileURL.checkPromisedItemIsReachable()
            guard isReachable else {
                setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
                return
            }
        } catch {
            setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
            return
        }

        //============================================================
        //            Check 3 - is file URL a directory?
        //============================================================

        var isDirectory: ObjCBool = false
        let path = fileURL.path

        guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
            setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
            return
        }

        //============================================================
        //          Check 4 - can the file size be extracted?
        //============================================================

        let bodyContentLength: UInt64

        do {
            guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
                setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
                return
            }

            bodyContentLength = fileSize.uint64Value
        }
        catch {
            setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
            return
        }

        //============================================================
        //       Check 5 - can a stream be created from file URL?
        //============================================================

        guard let stream = InputStream(url: fileURL) else {
            setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
            return
        }

        append(stream, withLength: bodyContentLength, headers: headers)
    }

    /// Creates a body part from the stream and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
    /// - `Content-Type: #{mimeType}` (HTTP Header)
    /// - Encoded stream data
    /// - Multipart form boundary
    ///
    /// - parameter stream:   The input stream to encode in the multipart form data.
    /// - parameter length:   The content length of the stream.
    /// - parameter name:     The name to associate with the stream content in the `Content-Disposition` HTTP header.
    /// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header.
    /// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header.
    internal func append(
        _ stream: InputStream,
        withLength length: UInt64,
        name: String,
        fileName: String,
        mimeType: String)
    {
        let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
        append(stream, withLength: length, headers: headers)
    }

    /// Creates a body part with the headers, stream and length and appends it to the multipart form data object.
    ///
    /// The body part data will be encoded using the following format:
    ///
    /// - HTTP headers
    /// - Encoded stream data
    /// - Multipart form boundary
    ///
    /// - parameter stream:  The input stream to encode in the multipart form data.
    /// - parameter length:  The content length of the stream.
    /// - parameter headers: The HTTP headers for the body part.
    internal func append(_ stream: InputStream, withLength length: UInt64, headers: CLDNHTTPHeaders) {
        let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
        bodyParts.append(bodyPart)
    }

    // MARK: - Data Encoding

    /// Encodes all the appended body parts into a single `Data` value.
    ///
    /// It is important to note that this method will load all the appended body parts into memory all at the same
    /// time. This method should only be used when the encoded data will have a small memory footprint. For large data
    /// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method.
    ///
    /// - throws: An `CLDNError` if encoding encounters an error.
    ///
    /// - returns: The encoded `Data` if encoding is successful.
    internal func encode() throws -> Data {
        if let bodyPartError = bodyPartError {
            throw bodyPartError
        }

        var encoded = Data()

        bodyParts.first?.hasInitialBoundary = true
        bodyParts.last?.hasFinalBoundary = true

        for bodyPart in bodyParts {
            let encodedData = try encode(bodyPart)
            encoded.append(encodedData)
        }

        return encoded
    }

    /// Writes the appended body parts into the given file URL.
    ///
    /// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
    /// this approach is very memory efficient and should be used for large body part data.
    ///
    /// - parameter fileURL: The file URL to write the multipart form data into.
    ///
    /// - throws: An `CLDNError` if encoding encounters an error.
    internal func writeEncodedData(to fileURL: URL) throws {
        if let bodyPartError = bodyPartError {
            throw bodyPartError
        }

        if FileManager.default.fileExists(atPath: fileURL.path) {
            throw CLDNError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
        } else if !fileURL.isFileURL {
            throw CLDNError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
        }

        guard let outputStream = OutputStream(url: fileURL, append: false) else {
            throw CLDNError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
        }

        outputStream.open()
        defer { outputStream.close() }

        self.bodyParts.first?.hasInitialBoundary = true
        self.bodyParts.last?.hasFinalBoundary = true

        for bodyPart in self.bodyParts {
            try write(bodyPart, to: outputStream)
        }
    }

    // MARK: - Private - Body Part Encoding

    private func encode(_ bodyPart: BodyPart) throws -> Data {
        var encoded = Data()

        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
        encoded.append(initialData)

        let headerData = encodeHeaders(for: bodyPart)
        encoded.append(headerData)

        let bodyStreamData = try encodeBodyStream(for: bodyPart)
        encoded.append(bodyStreamData)

        if bodyPart.hasFinalBoundary {
            encoded.append(finalBoundaryData())
        }

        return encoded
    }

    private func encodeHeaders(for bodyPart: BodyPart) -> Data {
        var headerText = ""

        for (key, value) in bodyPart.headers {
            headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
        }
        headerText += EncodingCharacters.crlf

        return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
    }

    private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
        let inputStream = bodyPart.bodyStream
        inputStream.open()
        defer { inputStream.close() }

        var encoded = Data()

        while inputStream.hasBytesAvailable {
            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)

            if let error = inputStream.streamError {
                throw CLDNError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
            }

            if bytesRead > 0 {
                encoded.append(buffer, count: bytesRead)
            } else {
                break
            }
        }

        return encoded
    }

    // MARK: - Private - Writing Body Part to Output Stream

    private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
        try writeInitialBoundaryData(for: bodyPart, to: outputStream)
        try writeHeaderData(for: bodyPart, to: outputStream)
        try writeBodyStream(for: bodyPart, to: outputStream)
        try writeFinalBoundaryData(for: bodyPart, to: outputStream)
    }

    private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
        return try write(initialData, to: outputStream)
    }

    private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let headerData = encodeHeaders(for: bodyPart)
        return try write(headerData, to: outputStream)
    }

    private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        let inputStream = bodyPart.bodyStream

        inputStream.open()
        defer { inputStream.close() }

        while inputStream.hasBytesAvailable {
            var buffer = [UInt8](repeating: 0, count: streamBufferSize)
            let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)

            if let streamError = inputStream.streamError {
                throw CLDNError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
            }

            if bytesRead > 0 {
                if buffer.count != bytesRead {
                    buffer = Array(buffer[0..<bytesRead])
                }

                try write(&buffer, to: outputStream)
            } else {
                break
            }
        }
    }

    private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
        if bodyPart.hasFinalBoundary {
            return try write(finalBoundaryData(), to: outputStream)
        }
    }

    // MARK: - Private - Writing Buffered Data to Output Stream

    private func write(_ data: Data, to outputStream: OutputStream) throws {
        var buffer = [UInt8](repeating: 0, count: data.count)
        data.copyBytes(to: &buffer, count: data.count)

        return try write(&buffer, to: outputStream)
    }

    private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
        var bytesToWrite = buffer.count

        while bytesToWrite > 0, outputStream.hasSpaceAvailable {
            let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)

            if let error = outputStream.streamError {
                throw CLDNError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
            }

            bytesToWrite -= bytesWritten

            if bytesToWrite > 0 {
                buffer = Array(buffer[bytesWritten..<buffer.count])
            }
        }
    }

    // MARK: - Private - Mime Type

    private func mimeType(forPathExtension pathExtension: String) -> String {
        if
            let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
            let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue()
        {
            return contentType as String
        }

        return "application/octet-stream"
    }

    // MARK: - Private - Content Headers

    private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] {
        var disposition = "form-data; name=\"\(name)\""
        if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }

        var headers = ["Content-Disposition": disposition]
        if let mimeType = mimeType { headers["Content-Type"] = mimeType }

        return headers
    }

    // MARK: - Private - Boundary Encoding

    private func initialBoundaryData() -> Data {
        return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
    }

    private func encapsulatedBoundaryData() -> Data {
        return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
    }

    private func finalBoundaryData() -> Data {
        return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
    }

    // MARK: - Private - Errors

    private func setBodyPartError(withReason reason: CLDNError.MultipartEncodingFailureReason) {
        guard bodyPartError == nil else { return }
        bodyPartError = CLDNError.multipartEncodingFailed(reason: reason)
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNParameterEncoding.swift
================================================
//
//  CLDNParameterEncoding.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// HTTP method definitions.
///
/// See https://tools.ietf.org/html/rfc7231#section-4.3
internal enum CLDNHTTPMethod: String {
    case options = "OPTIONS"
    case get     = "GET"
    case head    = "HEAD"
    case post    = "POST"
    case put     = "PUT"
    case patch   = "PATCH"
    case delete  = "DELETE"
    case trace   = "TRACE"
    case connect = "CONNECT"
}

// MARK: -

/// A dictionary of parameters to apply to a `URLRequest`.
internal typealias CLDNParameters = [String: Any]

/// A type used to define how a set of parameters are applied to a `URLRequest`.
internal protocol CLDNParameterEncoding {
    /// Creates a URL request by encoding parameters and applying them onto an existing request.
    ///
    /// - parameter urlRequest: The request to have parameters applied.
    /// - parameter parameters: The parameters to apply.
    ///
    /// - throws: An `CLDNError.parameterEncodingFailed` error if encoding fails.
    ///
    /// - returns: The encoded request.
    func CLDN_Encode(_ urlRequest: CLDNURLRequestConvertible, with parameters: CLDNParameters?) throws -> URLRequest
}

// MARK: -

/// Creates a url-encoded query string to be set as or appended to any existing URL query string or set as the HTTP
/// body of the URL request. Whether the query string is set or appended to any existing URL query string or set as
/// the HTTP body depends on the destination of the encoding.
///
/// The `Content-Type` HTTP header field of an encoded request with HTTP body is set to
/// `application/x-www-form-urlencoded; charset=utf-8`.
///
/// There is no published specification for how to encode collection types. By default the convention of appending
/// `[]` to the key for array values (`foo[]=1&foo[]=2`), and appending the key surrounded by square brackets for
/// nested dictionary values (`foo[bar]=baz`) is used. Optionally, `ArrayEncoding` can be used to omit the
/// square brackets appended to array keys.
///
/// `BoolEncoding` can be used to configure how boolean values are encoded. The default behavior is to encode
/// `true` as 1 and `false` as 0.
internal struct CLDNURLEncoding: CLDNParameterEncoding {

    // MARK: Helper Types

    /// Defines whether the url-encoded query string is applied to the existing query string or HTTP body of the
    /// resulting URL request.
    ///
    /// - methodDependent: Applies encoded query string result to existing query string for `GET`, `HEAD` and `DELETE`
    ///                    requests and sets as the HTTP body for requests with any other HTTP method.
    /// - queryString:     Sets or appends encoded query string result to existing query string.
    /// - httpBody:        Sets encoded query string result as the HTTP body of the URL request.
    internal enum Destination {
        case methodDependent, queryString, httpBody
    }

    /// Configures how `Array` parameters are encoded.
    ///
    /// - brackets:        An empty set of square brackets is appended to the key for every value.
    ///                    This is the default behavior.
    /// - noBrackets:      No brackets are appended. The key is encoded as is.
    internal enum ArrayEncoding {
        case brackets, noBrackets

        func encode(key: String) -> String {
            switch self {
            case .brackets:
                return "\(key)[]"
            case .noBrackets:
                return key
            }
        }
    }

    /// Configures how `Bool` parameters are encoded.
    ///
    /// - numeric:         Encode `true` as `1` and `false` as `0`. This is the default behavior.
    /// - literal:         Encode `true` and `false` as string literals.
    internal enum BoolEncoding {
        case numeric, literal

        func encode(value: Bool) -> String {
            switch self {
            case .numeric:
                return value ? "1" : "0"
            case .literal:
                return value ? "true" : "false"
            }
        }
    }

    // MARK: Properties

    /// Returns a default `URLEncoding` instance.
    internal static var `default`: CLDNURLEncoding { return CLDNURLEncoding() }

    /// Returns a `URLEncoding` instance with a `.methodDependent` destination.
    internal static var methodDependent: CLDNURLEncoding { return CLDNURLEncoding() }

    /// Returns a `URLEncoding` instance with a `.queryString` destination.
    internal static var queryString: CLDNURLEncoding { return CLDNURLEncoding(destination: .queryString) }

    /// Returns a `URLEncoding` instance with an `.httpBody` destination.
    internal static var httpBody: CLDNURLEncoding { return CLDNURLEncoding(destination: .httpBody) }

    /// The destination defining where the encoded query string is to be applied to the URL request.
    internal let destination: Destination

    /// The encoding to use for `Array` parameters.
    internal let arrayEncoding: ArrayEncoding

    /// The encoding to use for `Bool` parameters.
    internal let boolEncoding: BoolEncoding

    // MARK: Initialization

    /// Creates a `CLDNURLEncoding` instance using the specified destination.
    ///
    /// - parameter destination: The destination defining where the encoded query string is to be applied.
    /// - parameter arrayEncoding: The encoding to use for `Array` parameters.
    /// - parameter boolEncoding: The encoding to use for `Bool` parameters.
    ///
    /// - returns: The new `CLDNURLEncoding` instance.
    internal init(destination: Destination = .methodDependent, arrayEncoding: ArrayEncoding = .brackets, boolEncoding: BoolEncoding = .numeric) {
        self.destination = destination
        self.arrayEncoding = arrayEncoding
        self.boolEncoding = boolEncoding
    }

    // MARK: Encoding

    /// Creates a URL request by encoding parameters and applying them onto an existing request.
    ///
    /// - parameter urlRequest: The request to have parameters applied.
    /// - parameter parameters: The parameters to apply.
    ///
    /// - throws: An `Error` if the encoding process encounters an error.
    ///
    /// - returns: The encoded request.
    internal func CLDN_Encode(_ urlRequest: CLDNURLRequestConvertible, with parameters: CLDNParameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.CLDN_AsURLRequest()

        guard let parameters = parameters else { return urlRequest }

        if let method = CLDNHTTPMethod(rawValue: urlRequest.httpMethod ?? "GET"), encodesParametersInURL(with: method) {
            guard let url = urlRequest.url else {
                throw CLDNError.parameterEncodingFailed(reason: .missingURL)
            }

            if var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false), !parameters.isEmpty {
                let percentEncodedQuery = (urlComponents.percentEncodedQuery.map { $0 + "&" } ?? "") + query(parameters)
                urlComponents.percentEncodedQuery = percentEncodedQuery
                urlRequest.url = urlComponents.url
            }
        } else {
            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/x-www-form-urlencoded; charset=utf-8", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = query(parameters).data(using: .utf8, allowLossyConversion: false)
        }

        return urlRequest
    }

    /// Creates percent-escaped, URL encoded query string components from the given key-value pair using recursion.
    ///
    /// - parameter key:   The key of the query component.
    /// - parameter value: The value of the query component.
    ///
    /// - returns: The percent-escaped, URL encoded query string components.
    internal func queryComponents(fromKey key: String, value: Any) -> [(String, String)] {
        var components: [(String, String)] = []

        if let dictionary = value as? [String: Any] {
            for (nestedKey, value) in dictionary {
                components += queryComponents(fromKey: "\(key)[\(nestedKey)]", value: value)
            }
        } else if let array = value as? [Any] {
            for value in array {
                components += queryComponents(fromKey: arrayEncoding.encode(key: key), value: value)
            }
        } else if let value = value as? NSNumber {
            if value.isBool {
                components.append((escape(key), escape(boolEncoding.encode(value: value.boolValue))))
            } else {
                components.append((escape(key), escape("\(value)")))
            }
        } else if let bool = value as? Bool {
            components.append((escape(key), escape(boolEncoding.encode(value: bool))))
        } else {
            components.append((escape(key), escape("\(value)")))
        }

        return components
    }

    /// Returns a percent-escaped string following RFC 3986 for a query string key or value.
    ///
    /// RFC 3986 states that the following characters are "reserved" characters.
    ///
    /// - General Delimiters: ":", "#", "[", "]", "@", "?", "/"
    /// - Sub-Delimiters: "!", "$", "&", "'", "(", ")", "*", "+", ",", ";", "="
    ///
    /// In RFC 3986 - Section 3.4, it states that the "?" and "/" characters should not be escaped to allow
    /// query strings to include a URL. Therefore, all "reserved" characters with the exception of "?" and "/"
    /// should be percent-escaped in the query string.
    ///
    /// - parameter string: The string to be percent-escaped.
    ///
    /// - returns: The percent-escaped string.
    internal func escape(_ string: String) -> String {
        let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
        let subDelimitersToEncode = "!$&'()*+,;="

        var allowedCharacterSet = CharacterSet.urlQueryAllowed
        allowedCharacterSet.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")

        var escaped = ""

        //==========================================================================================================
        //
        //  Batching is required for escaping due to an internal bug in iOS 8.1 and 8.2. Encoding more than a few
        //  hundred Chinese characters causes various malloc error crashes. To avoid this issue until iOS 8 is no
        //  longer supported, batching MUST be used for encoding. This introduces roughly a 20% overhead. For more
        //  info, please refer to:
        //
        //      - https://github.com/Alamofire/Alamofire/issues/206
        //
        //==========================================================================================================

        if #available(iOS 8.3, *) {
            escaped = string.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? string
        } else {
            let batchSize = 50
            var index = string.startIndex

            while index != string.endIndex {
                let startIndex = index
                let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
                let range = startIndex..<endIndex

                let substring = string[range]

                escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? String(substring)

                index = endIndex
            }
        }

        return escaped
    }

    private func query(_ parameters: [String: Any]) -> String {
        var components: [(String, String)] = []

        for key in parameters.keys.sorted(by: <) {
            let value = parameters[key]!
            components += queryComponents(fromKey: key, value: value)
        }
        return components.map { "\($0)=\($1)" }.joined(separator: "&")
    }

    private func encodesParametersInURL(with method: CLDNHTTPMethod) -> Bool {
        switch destination {
        case .queryString:
            return true
        case .httpBody:
            return false
        default:
            break
        }

        switch method {
        case .get, .head, .delete:
            return true
        default:
            return false
        }
    }
}

// MARK: -

/// Uses `JSONSerialization` to create a JSON representation of the parameters object, which is set as the body of the
/// request. The `Content-Type` HTTP header field of an encoded request is set to `application/json`.
internal struct CLDNJSONEncoding: CLDNParameterEncoding {

    // MARK: Properties

    /// Returns a `JSONEncoding` instance with default writing options.
    internal static var `default`: CLDNJSONEncoding { return CLDNJSONEncoding() }

    /// Returns a `JSONEncoding` instance with `.prettyPrinted` writing options.
    internal static var prettyPrinted: CLDNJSONEncoding { return CLDNJSONEncoding(options: .prettyPrinted) }

    /// The options for writing the parameters as JSON data.
    internal let options: JSONSerialization.WritingOptions

    // MARK: Initialization

    /// Creates a `CLDNJSONEncoding` instance using the specified options.
    ///
    /// - parameter options: The options for writing the parameters as JSON data.
    ///
    /// - returns: The new `CLDNJSONEncoding` instance.
    internal init(options: JSONSerialization.WritingOptions = []) {
        self.options = options
    }

    // MARK: Encoding

    /// Creates a URL request by encoding parameters and applying them onto an existing request.
    ///
    /// - parameter urlRequest: The request to have parameters applied.
    /// - parameter parameters: The parameters to apply.
    ///
    /// - throws: An `Error` if the encoding process encounters an error.
    ///
    /// - returns: The encoded request.
    internal func CLDN_Encode(_ urlRequest: CLDNURLRequestConvertible, with parameters: CLDNParameters?) throws -> URLRequest {
        var urlRequest = try urlRequest.CLDN_AsURLRequest()

        guard let parameters = parameters else { return urlRequest }

        do {
            let data = try JSONSerialization.data(withJSONObject: parameters, options: options)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = data
        } catch {
            throw CLDNError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }

        return urlRequest
    }

    /// Creates a URL request by encoding the JSON object and setting the resulting data on the HTTP body.
    ///
    /// - parameter urlRequest: The request to apply the JSON object to.
    /// - parameter jsonObject: The JSON object to apply to the request.
    ///
    /// - throws: An `Error` if the encoding process encounters an error.
    ///
    /// - returns: The encoded request.
    internal func encode(_ urlRequest: CLDNURLRequestConvertible, withJSONObject jsonObject: Any? = nil) throws -> URLRequest {
        var urlRequest = try urlRequest.CLDN_AsURLRequest()

        guard let jsonObject = jsonObject else { return urlRequest }

        do {
            let data = try JSONSerialization.data(withJSONObject: jsonObject, options: options)

            if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
                urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
            }

            urlRequest.httpBody = data
        } catch {
            throw CLDNError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
        }

        return urlRequest
    }
}

// MARK: -

extension NSNumber {
    fileprivate var isBool: Bool { return CFBooleanGetTypeID() == CFGetTypeID(self) }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNRequest.swift
================================================
//
//  CLDNRequest.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// A type that can inspect and optionally adapt a `URLRequest` in some manner if necessary.
internal protocol CLDNRequestAdapter {
    /// Inspects and adapts the specified `URLRequest` in some manner if necessary and returns the result.
    ///
    /// - parameter urlRequest: The URL request to adapt.
    ///
    /// - throws: An `Error` if the adaptation encounters an error.
    ///
    /// - returns: The adapted `URLRequest`.
    func CLDN_Adapt(_ urlRequest: URLRequest) throws -> URLRequest
}

// MARK: -

/// A closure executed when the `CLDNRequestRetrier` determines whether a `CLDNRequest` should be retried or not.
internal typealias CLDNRequestRetryCompletion = (_ shouldRetry: Bool, _ timeDelay: TimeInterval) -> Void

/// A type that determines whether a request should be retried after being executed by the specified session manager
/// and encountering an error.
internal protocol CLDNRequestRetrier {
    /// Determines whether the `CLDNRequest` should be retried by calling the `completion` closure.
    ///
    /// This operation is fully asynchronous. Any amount of time can be taken to determine whether the request needs
    /// to be retried. The one requirement is that the completion closure is called to ensure the request is properly
    /// cleaned up after.
    ///
    /// - parameter manager:    The session manager the request was executed on.
    /// - parameter request:    The request that failed due to the encountered error.
    /// - parameter error:      The error encountered when executing the request.
    /// - parameter completion: The completion closure to be executed when retry decision has been determined.
    func CLDN_Should(_ manager: CLDNSessionManager, retry request: CLDNRequest, with error: Error, completion: @escaping CLDNRequestRetryCompletion)
}

// MARK: -

protocol CLDNTaskConvertible {
    func CLDN_Task(session: URLSession, adapter: CLDNRequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask
}

/// A dictionary of headers to apply to a `URLRequest`.
internal typealias CLDNHTTPHeaders = [String: String]

// MARK: -

/// Responsible for sending a request and receiving the response and associated data from the server, as well as
/// managing its underlying `URLSessionTask`.
internal class CLDNRequest {

    // MARK: Helper Types

    /// A closure executed when monitoring upload or download progress of a request.
    internal typealias ProgressHandler = (Progress) -> Void

    enum RequestTask {
        case data(CLDNTaskConvertible?, URLSessionTask?)
        case upload(CLDNTaskConvertible?, URLSessionTask?)
    }

    // MARK: Properties

    /// The delegate for the underlying task.
    internal var delegate: CLDNTaskDelegate {
        get {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            return taskDelegate
        }
        set {
            taskDelegateLock.lock() ; defer { taskDelegateLock.unlock() }
            taskDelegate = newValue
        }
    }

    /// The underlying task.
    internal var task: URLSessionTask? { return delegate.task }

    /// The session belonging to the underlying task.
    internal let session: URLSession

    /// The request sent or to be sent to the server.
    internal var request: URLRequest? { return task?.originalRequest }

    /// The response received from the server, if any.
    internal var response: HTTPURLResponse? { return task?.response as? HTTPURLResponse }

    /// The number of times the request has been retried.
    internal var retryCount: UInt = 0

    let originalTask: CLDNTaskConvertible?

    var startTime: CFAbsoluteTime?
    var endTime: CFAbsoluteTime?

    var validations: [() -> Void] = []

    private var taskDelegate: CLDNTaskDelegate
    private var taskDelegateLock = NSLock()

    // MARK: Lifecycle

    init(session: URLSession, requestTask: RequestTask, error: Error? = nil) {
        self.session = session

        switch requestTask {
        case .data(let originalTask, let task):
            taskDelegate = CLDNDataTaskDelegate(task: task)
            self.originalTask = originalTask
        case .upload(let originalTask, let task):
            taskDelegate = CLDNUploadTaskDelegate(task: task)
            self.originalTask = originalTask
        }

        delegate.error = error
        delegate.queue.addOperation { self.endTime = CFAbsoluteTimeGetCurrent() }
    }

    // MARK: Authentication

    /// Associates an HTTP Basic credential with the request.
    ///
    /// - parameter user:        The user.
    /// - parameter password:    The password.
    /// - parameter persistence: The URL credential persistence. `.ForSession` by default.
    ///
    /// - returns: The request.
    @discardableResult
    internal func authenticate(
        user: String,
        password: String,
        persistence: URLCredential.Persistence = .forSession)
        -> Self
    {
        let credential = URLCredential(user: user, password: password, persistence: persistence)
        return authenticate(usingCredential: credential)
    }

    /// Associates a specified credential with the request.
    ///
    /// - parameter credential: The credential.
    ///
    /// - returns: The request.
    @discardableResult
    internal func authenticate(usingCredential credential: URLCredential) -> Self {
        delegate.credential = credential
        return self
    }

    /// Returns a base64 encoded basic authentication credential as an authorization header tuple.
    ///
    /// - parameter user:     The user.
    /// - parameter password: The password.
    ///
    /// - returns: A tuple with Authorization header and credential value if encoding succeeds, `nil` otherwise.
    internal class func authorizationHeader(user: String, password: String) -> (key: String, value: String)? {
        guard let data = "\(user):\(password)".data(using: .utf8) else { return nil }

        let credential = data.base64EncodedString(options: [])

        return (key: "Authorization", value: "Basic \(credential)")
    }

    // MARK: State

    /// Resumes the request.
    internal func resume() {
        guard let task = task else { delegate.queue.isSuspended = false ; return }

        if startTime == nil { startTime = CFAbsoluteTimeGetCurrent() }

        task.resume()

//        NotificationCenter.default.post(
//            name: Notification.Name.Task.DidResume,
//            object: self,
//            userInfo: [Notification.Key.Task: task]
//        )
    }

    /// Suspends the request.
    internal func suspend() {
        guard let task = task else { return }

        task.suspend()
    }

    /// Cancels the request.
    internal func cancel() {
        guard let task = task else { return }

        task.cancel()
    }
}

// MARK: - CustomStringConvertible

extension CLDNRequest: CustomStringConvertible {
    /// The textual representation used when written to an output stream, which includes the HTTP method and URL, as
    /// well as the response status code if a response has been received.
    internal var description: String {
        var components: [String] = []

        if let HTTPMethod = request?.httpMethod {
            components.append(HTTPMethod)
        }

        if let urlString = request?.url?.absoluteString {
            components.append(urlString)
        }

        if let response = response {
            components.append("(\(response.statusCode))")
        }

        return components.joined(separator: " ")
    }
}

// MARK: - CustomDebugStringConvertible

extension CLDNRequest: CustomDebugStringConvertible {
    /// The textual representation used when written to an output stream, in the form of a cURL command.
    internal var debugDescription: String {
        return cURLRepresentation()
    }

    func cURLRepresentation() -> String {
        var components = ["$ curl -v"]

        guard let request = self.request,
              let url = request.url,
              let host = url.host
        else {
            return "$ curl command could not be created"
        }

        if let httpMethod = request.httpMethod, httpMethod != "GET" {
            components.append("-X \(httpMethod)")
        }

        if let credentialStorage = self.session.configuration.urlCredentialStorage {
            let protectionSpace = URLProtectionSpace(
                host: host,
                port: url.port ?? 0,
                protocol: url.scheme,
                realm: host,
                authenticationMethod: NSURLAuthenticationMethodHTTPBasic
            )

            if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
                for credential in credentials {
                    guard let user = credential.user, let password = credential.password else { continue }
                    components.append("-u \(user):\(password)")
                }
            } else {
                if let credential = delegate.credential, let user = credential.user, let password = credential.password {
                    components.append("-u \(user):\(password)")
                }
            }
        }

        if session.configuration.httpShouldSetCookies {
            if
                let cookieStorage = session.configuration.httpCookieStorage,
                let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty
            {
                let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" }

            #if swift(>=3.2)
                components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
            #else
                components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
            #endif
            }
        }

        var headers: [AnyHashable: Any] = [:]

        session.configuration.httpAdditionalHeaders?.filter {  $0.0 != AnyHashable("Cookie") }
                                                    .forEach { headers[$0.0] = $0.1 }

        request.allHTTPHeaderFields?.filter { $0.0 != "Cookie" }
                                    .forEach { headers[$0.0] = $0.1 }

        components += headers.map {
            let escapedValue = String(describing: $0.value).replacingOccurrences(of: "\"", with: "\\\"")

            return "-H \"\($0.key): \(escapedValue)\""
        }

        if let httpBodyData = request.httpBody, let httpBody = String(data: httpBodyData, encoding: .utf8) {
            var escapedBody = httpBody.replacingOccurrences(of: "\\\"", with: "\\\\\"")
            escapedBody = escapedBody.replacingOccurrences(of: "\"", with: "\\\"")

            components.append("-d \"\(escapedBody)\"")
        }

        components.append("\"\(url.absoluteString)\"")

        return components.joined(separator: " \\\n\t")
    }
}

// MARK: -

/// Specific type of `CLDNRequest` that manages an underlying `URLSessionDataTask`.
internal class CLDNDataRequest: CLDNRequest {

    // MARK: Helper Types

    struct Requestable: CLDNTaskConvertible {
        let urlRequest: URLRequest

        func CLDN_Task(session: URLSession, adapter: CLDNRequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let urlRequest = try self.urlRequest.adapt(using: adapter)
                return queue.sync { session.dataTask(with: urlRequest) }
            } catch {
                throw AdaptError(error: error)
            }
        }
    }

    // MARK: Properties

    /// The request sent or to be sent to the server.
    internal override var request: URLRequest? {
        if let request = super.request { return request }
        if let requestable = originalTask as? Requestable { return requestable.urlRequest }

        return nil
    }

    /// The progress of fetching the response data from the server for the request.
    internal var progress: Progress { return dataDelegate.progress }

    var dataDelegate: CLDNDataTaskDelegate { return delegate as! CLDNDataTaskDelegate }

    // MARK: Stream

    /// Sets a closure to be called periodically during the lifecycle of the request as data is read from the server.
    ///
    /// This closure returns the bytes most recently received from the server, not including data from previous calls.
    /// If this closure is set, data will only be available within this closure, and will not be saved elsewhere. It is
    /// also important to note that the server data in any `Response` object will be `nil`.
    ///
    /// - parameter closure: The code to be executed periodically during the lifecycle of the request.
    ///
    /// - returns: The request.
    @discardableResult
    internal func stream(closure: ((Data) -> Void)? = nil) -> Self {
        dataDelegate.dataStream = closure
        return self
    }

    // MARK: Progress

    /// Sets a closure to be called periodically during the lifecycle of the `CLDNRequest` as data is read from the server.
    ///
    /// - parameter queue:   The dispatch queue to execute the closure on.
    /// - parameter closure: The code to be executed periodically as data is read from the server.
    ///
    /// - returns: The request.
    @discardableResult
    internal func downloadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        dataDelegate.progressHandler = (closure, queue)
        return self
    }
}

// MARK: -

/// Specific type of `CLDNRequest` that manages an underlying `URLSessionUploadTask`.
internal class CLDNUploadRequest: CLDNDataRequest {

    // MARK: Helper Types

    enum Uploadable: CLDNTaskConvertible {
        case data(Data, URLRequest)
        case file(URL, URLRequest)
        case stream(InputStream, URLRequest)

        func CLDN_Task(session: URLSession, adapter: CLDNRequestAdapter?, queue: DispatchQueue) throws -> URLSessionTask {
            do {
                let task: URLSessionTask

                switch self {
                case let .data(data, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, from: data) }
                case let .file(url, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(with: urlRequest, fromFile: url) }
                case let .stream(_, urlRequest):
                    let urlRequest = try urlRequest.adapt(using: adapter)
                    task = queue.sync { session.uploadTask(withStreamedRequest: urlRequest) }
                }

                return task
            } catch {
                throw AdaptError(error: error)
            }
        }
    }

    // MARK: Properties

    /// The request sent or to be sent to the server.
    internal override var request: URLRequest? {
        if let request = super.request { return request }

        guard let uploadable = originalTask as? Uploadable else { return nil }

        switch uploadable {
        case .data(_, let urlRequest), .file(_, let urlRequest), .stream(_, let urlRequest):
            return urlRequest
        }
    }

    /// The progress of uploading the payload to the server for the upload request.
    internal var uploadProgress: Progress { return uploadDelegate.uploadProgress }

    var uploadDelegate: CLDNUploadTaskDelegate { return delegate as! CLDNUploadTaskDelegate }

    // MARK: Upload Progress

    /// Sets a closure to be called periodically during the lifecycle of the `CLDNUploadRequest` as data is sent to
    /// the server.
    ///
    /// After the data is sent to the server, the `progress(queue:closure:)` APIs can be used to monitor the progress
    /// of data being read from the server.
    ///
    /// - parameter queue:   The dispatch queue to execute the closure on.
    /// - parameter closure: The code to be executed periodically as data is sent to the server.
    ///
    /// - returns: The request.
    @discardableResult
    internal func uploadProgress(queue: DispatchQueue = DispatchQueue.main, closure: @escaping ProgressHandler) -> Self {
        uploadDelegate.uploadProgressHandler = (closure, queue)
        return self
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNResponse.swift
================================================
//
//  CLDNResponse.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// Used to store all data associated with an non-serialized response of a data or upload request.
internal struct CLDNDefaultDataResponse {
    /// The URL request sent to the server.
    internal let request: URLRequest?

    /// The server's response to the URL request.
    internal let response: HTTPURLResponse?

    /// The data returned by the server.
    internal let data: Data?

    /// The error encountered while executing or validating the request.
    internal let error: Error?

    /// The timeline of the complete lifecycle of the request.
    internal let timeline: CLDNTimeline

    var _metrics: AnyObject?

    /// Creates a `CLDNDefaultDataResponse` instance from the specified parameters.
    ///
    /// - Parameters:
    ///   - request:  The URL request sent to the server.
    ///   - response: The server's response to the URL request.
    ///   - data:     The data returned by the server.
    ///   - error:    The error encountered while executing or validating the request.
    ///   - timeline: The timeline of the complete lifecycle of the request. `CLDNTimeline()` by default.
    ///   - metrics:  The task metrics containing the request / response statistics. `nil` by default.
    internal init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?,
        timeline: CLDNTimeline = CLDNTimeline(),
        metrics: AnyObject? = nil)
    {
        self.request = request
        self.response = response
        self.data = data
        self.error = error
        self.timeline = timeline
    }
}

// MARK: -

/// Used to store all data associated with a serialized response of a data or upload request.
internal struct CLDNDataResponse<Value> {
    /// The URL request sent to the server.
    internal let request: URLRequest?

    /// The server's response to the URL request.
    internal let response: HTTPURLResponse?

    /// The data returned by the server.
    internal let data: Data?

    /// The result of response serialization.
    internal let result: CLDNResult<Value>

    /// The timeline of the complete lifecycle of the request.
    internal let timeline: CLDNTimeline

    /// Returns the associated value of the result if it is a success, `nil` otherwise.
    internal var value: Value? { return result.value }

    /// Returns the associated error value if the result if it is a failure, `nil` otherwise.
    internal var error: Error? { return result.error }

    var _metrics: AnyObject?

    /// Creates a `CLDNDataResponse` instance with the specified parameters derived from response serialization.
    ///
    /// - parameter request:  The URL request sent to the server.
    /// - parameter response: The server's response to the URL request.
    /// - parameter data:     The data returned by the server.
    /// - parameter result:   The result of response serialization.
    /// - parameter timeline: The timeline of the complete lifecycle of the `CLDNRequest`. Defaults to `CLDNTimeline()`.
    ///
    /// - returns: The new `CLDNDataResponse` instance.
    internal init(
        request: URLRequest?,
        response: HTTPURLResponse?,
        data: Data?,
        result: CLDNResult<Value>,
        timeline: CLDNTimeline = CLDNTimeline())
    {
        self.request = request
        self.response = response
        self.data = data
        self.result = result
        self.timeline = timeline
    }
}

// MARK: -

extension CLDNDataResponse: CustomStringConvertible, CustomDebugStringConvertible {
    /// The textual representation used when written to an output stream, which includes whether the result was a
    /// success or failure.
    internal var description: String {
        return result.debugDescription
    }

    /// The debug textual representation used when written to an output stream, which includes the URL request, the URL
    /// response, the server data, the response serialization result and the timeline.
    internal var debugDescription: String {
        let requestDescription = request.map { "\($0.httpMethod ?? "GET") \($0)"} ?? "nil"
        let requestBody = request?.httpBody.map { String(decoding: $0, as: UTF8.self) } ?? "None"
        let responseDescription = response.map { "\($0)" } ?? "nil"
        let responseBody = data.map { String(decoding: $0, as: UTF8.self) } ?? "None"

        return """
        [Request]: \(requestDescription)
        [Request Body]: \n\(requestBody)
        [Response]: \(responseDescription)
        [Response Body]: \n\(responseBody)
        [Result]: \(result)
        [Timeline]: \(timeline.debugDescription)
        """
    }
}

// MARK: -

protocol CLDNResponse {
    /// The task metrics containing the request / response statistics.
    var _metrics: AnyObject? { get set }
    mutating func CLDN_Add(_ metrics: AnyObject?)
}

extension CLDNResponse {
    mutating func CLDN_Add(_ metrics: AnyObject?) {
        #if !os(watchOS)
            guard #available(iOS 10.0, macOS 10.12, tvOS 10.0, *) else { return }
            guard let metrics = metrics as? URLSessionTaskMetrics else { return }

            _metrics = metrics
        #endif
    }
}

// MARK: -

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension CLDNDefaultDataResponse: CLDNResponse {
#if !os(watchOS)
    /// The task metrics containing the request / response statistics.
    internal var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}

@available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
extension CLDNDataResponse: CLDNResponse {
#if !os(watchOS)
    /// The task metrics containing the request / response statistics.
    internal var metrics: URLSessionTaskMetrics? { return _metrics as? URLSessionTaskMetrics }
#endif
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNResponseSerialization.swift
================================================
//
//  CLDNResponseSerialization.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// The type in which all data response serializers must conform to in order to serialize a response.
internal protocol CLDNDataResponseSerializerProtocol {
    /// The type of serialized object to be created by this `DataResponseSerializerType`.
    associatedtype SerializedObject

    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
    var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> CLDNResult<SerializedObject> { get }
}

// MARK: -

/// A generic `DataResponseSerializerType` used to serialize a request, response, and data into a serialized object.
internal struct CLDNDataResponseSerializer<Value>: CLDNDataResponseSerializerProtocol {
    /// The type of serialized object to be created by this `CLDNDataResponseSerializer`.
    internal typealias SerializedObject = Value

    /// A closure used by response handlers that takes a request, response, data and error and returns a result.
    internal var serializeResponse: (URLRequest?, HTTPURLResponse?, Data?, Error?) -> CLDNResult<Value>

    /// Initializes the `ResponseSerializer` instance with the given serialize response closure.
    ///
    /// - parameter serializeResponse: The closure used to serialize the response.
    ///
    /// - returns: The new generic response serializer instance.
    internal init(serializeResponse: @escaping (URLRequest?, HTTPURLResponse?, Data?, Error?) -> CLDNResult<Value>) {
        self.serializeResponse = serializeResponse
    }
}

// MARK: - Timeline

extension CLDNRequest {
    var timeline: CLDNTimeline {
        let requestStartTime = self.startTime ?? CFAbsoluteTimeGetCurrent()
        let requestCompletedTime = self.endTime ?? CFAbsoluteTimeGetCurrent()
        let initialResponseTime = self.delegate.initialResponseTime ?? requestCompletedTime

        return CLDNTimeline(
            requestStartTime: requestStartTime,
            initialResponseTime: initialResponseTime,
            requestCompletedTime: requestCompletedTime,
            serializationCompletedTime: CFAbsoluteTimeGetCurrent()
        )
    }
}

// MARK: - Default

extension CLDNDataRequest {
    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter queue:             The queue on which the completion handler is dispatched.
    /// - parameter completionHandler: The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    internal func response(queue: DispatchQueue? = nil, completionHandler: @escaping (CLDNDefaultDataResponse) -> Void) -> Self {
        delegate.queue.addOperation {
            (queue ?? DispatchQueue.main).async {
                var dataResponse = CLDNDefaultDataResponse(
                    request: self.request,
                    response: self.response,
                    data: self.delegate.data,
                    error: self.delegate.error,
                    timeline: self.timeline
                )

                if #available(iOS 10.0, *)
                {
                    dataResponse.CLDN_Add(self.delegate.metrics)
                }

                completionHandler(dataResponse)
            }
        }

        return self
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter queue:              The queue on which the completion handler is dispatched.
    /// - parameter responseSerializer: The response serializer responsible for serializing the request, response,
    ///                                 and data.
    /// - parameter completionHandler:  The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    internal func response<T: CLDNDataResponseSerializerProtocol>(
        queue: DispatchQueue? = nil,
        responseSerializer: T,
        completionHandler: @escaping (CLDNDataResponse<T.SerializedObject>) -> Void)
        -> Self
    {
        delegate.queue.addOperation {
            let result = responseSerializer.serializeResponse(
                self.request,
                self.response,
                self.delegate.data,
                self.delegate.error
            )

            var dataResponse = CLDNDataResponse<T.SerializedObject>(
                request: self.request,
                response: self.response,
                data: self.delegate.data,
                result: result,
                timeline: self.timeline
            )
            
            if #available(iOS 10.0, *)
            {
                dataResponse.CLDN_Add(self.delegate.metrics)
            }

            (queue ?? DispatchQueue.main).async { completionHandler(dataResponse) }
        }

        return self
    }
}

// MARK: - Data

extension CLDNRequest {
    /// Returns a result data type that contains the response data as-is.
    ///
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    internal static func serializeResponseData(response: HTTPURLResponse?, data: Data?, error: Error?) -> CLDNResult<Data> {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(Data()) }

        guard let validData = data else {
            if let statusCode = response?.statusCode,
               let httpStatusCode = HTTPStatusCode(rawValue: statusCode), httpStatusCode.isError {
                return .failure(CLDError.error(code: statusCode, userInfo: ["message": httpStatusCode.localizedReason]))
            }
            return .failure(CLDNError.responseSerializationFailed(reason: .inputDataNil))
        }

        return .success(validData)
    }
}

extension CLDNDataRequest {
    /// Creates a response serializer that returns the associated data as-is.
    ///
    /// - returns: A data response serializer.
    internal static func dataResponseSerializer() -> CLDNDataResponseSerializer<Data> {
        return CLDNDataResponseSerializer { _, response, data, error in
            return CLDNRequest.serializeResponseData(response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter completionHandler: The code to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    internal func responseData(
        queue: DispatchQueue? = nil,
        completionHandler: @escaping (CLDNDataResponse<Data>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: CLDNDataRequest.dataResponseSerializer(),
            completionHandler: completionHandler
        )
    }
}

// MARK: - String

extension CLDNRequest {
    /// Returns a result string type initialized from the response data with the specified string encoding.
    ///
    /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
    ///                       response, falling back to the default HTTP default character set, ISO-8859-1.
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    internal static func serializeResponseString(
        encoding: String.Encoding?,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> CLDNResult<String>
    {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success("") }

        guard let validData = data else {
            return .failure(CLDNError.responseSerializationFailed(reason: .inputDataNil))
        }

        var convertedEncoding = encoding

        if let encodingName = response?.textEncodingName as CFString?, convertedEncoding == nil {
            convertedEncoding = String.Encoding(rawValue: CFStringConvertEncodingToNSStringEncoding(
                CFStringConvertIANACharSetNameToEncoding(encodingName))
            )
        }

        let actualEncoding = convertedEncoding ?? .isoLatin1

        if let string = String(data: validData, encoding: actualEncoding) {
            return .success(string)
        } else {
            if let statusCode = response?.statusCode,
                let httpStatusCode = HTTPStatusCode(rawValue: statusCode), httpStatusCode.isError {
                return .failure(CLDError.error(code: statusCode, userInfo: ["message": httpStatusCode.localizedReason]))
            }
            return .failure(CLDNError.responseSerializationFailed(reason: .stringSerializationFailed(encoding: actualEncoding)))
        }
    }
}

extension CLDNDataRequest {
    /// Creates a response serializer that returns a result string type initialized from the response data with
    /// the specified string encoding.
    ///
    /// - parameter encoding: The string encoding. If `nil`, the string encoding will be determined from the server
    ///                       response, falling back to the default HTTP default character set, ISO-8859-1.
    ///
    /// - returns: A string response serializer.
    internal static func stringResponseSerializer(encoding: String.Encoding? = nil) -> CLDNDataResponseSerializer<String> {
        return CLDNDataResponseSerializer { _, response, data, error in
            return CLDNRequest.serializeResponseString(encoding: encoding, response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter encoding:          The string encoding. If `nil`, the string encoding will be determined from the
    ///                                server response, falling back to the default HTTP default character set,
    ///                                ISO-8859-1.
    /// - parameter completionHandler: A closure to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    internal func responseString(
        queue: DispatchQueue? = nil,
        encoding: String.Encoding? = nil,
        completionHandler: @escaping (CLDNDataResponse<String>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: CLDNDataRequest.stringResponseSerializer(encoding: encoding),
            completionHandler: completionHandler
        )
    }
}

// MARK: - JSON

extension CLDNRequest {
    /// Returns a JSON object contained in a result type constructed from the response data using `JSONSerialization`
    /// with the specified reading options.
    ///
    /// - parameter options:  The JSON serialization reading options. Defaults to `.allowFragments`.
    /// - parameter response: The response from the server.
    /// - parameter data:     The data returned from the server.
    /// - parameter error:    The error already encountered if it exists.
    ///
    /// - returns: The result data type.
    internal static func serializeResponseJSON(
        options: JSONSerialization.ReadingOptions,
        response: HTTPURLResponse?,
        data: Data?,
        error: Error?)
        -> CLDNResult<Any>
    {
        guard error == nil else { return .failure(error!) }

        if let response = response, emptyDataStatusCodes.contains(response.statusCode) { return .success(NSNull()) }

        guard let validData = data, validData.count > 0 else {
            return .failure(CLDNError.responseSerializationFailed(reason: .inputDataNilOrZeroLength))
        }

        do {
            let json = try JSONSerialization.jsonObject(with: validData, options: options)
            return .success(json)
        } catch {
            if let statusCode = response?.statusCode,
                let httpStatusCode = HTTPStatusCode(rawValue: statusCode), httpStatusCode.isError {
                return .failure(CLDError.error(code: statusCode, userInfo: ["message": httpStatusCode.localizedReason]))
            }
            return .failure(CLDNError.responseSerializationFailed(reason: .jsonSerializationFailed(error: error)))
        }
    }
}

extension CLDNDataRequest {
    /// Creates a response serializer that returns a JSON object result type constructed from the response data using
    /// `JSONSerialization` with the specified reading options.
    ///
    /// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
    ///
    /// - returns: A JSON object response serializer.
    internal static func jsonResponseSerializer(
        options: JSONSerialization.ReadingOptions = .allowFragments)
        -> CLDNDataResponseSerializer<Any>
    {
        return CLDNDataResponseSerializer { _, response, data, error in
            return CLDNRequest.serializeResponseJSON(options: options, response: response, data: data, error: error)
        }
    }

    /// Adds a handler to be called once the request has finished.
    ///
    /// - parameter options:           The JSON serialization reading options. Defaults to `.allowFragments`.
    /// - parameter completionHandler: A closure to be executed once the request has finished.
    ///
    /// - returns: The request.
    @discardableResult
    internal func responseJSON(
        queue: DispatchQueue? = nil,
        options: JSONSerialization.ReadingOptions = .allowFragments,
        completionHandler: @escaping (CLDNDataResponse<Any>) -> Void)
        -> Self
    {
        return response(
            queue: queue,
            responseSerializer: CLDNDataRequest.jsonResponseSerializer(options: options),
            completionHandler: completionHandler
        )
    }
}

/// A set of HTTP response status code that do not contain response data.
private let emptyDataStatusCodes: Set<Int> = [204, 205]


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNResult.swift
================================================
//
//  CLDNResult.swift
//
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// Used to represent whether a request was successful or encountered an error.
///
/// - success: The request and all post processing operations were successful resulting in the serialization of the
///            provided associated value.
///
/// - failure: The request encountered an error resulting in a failure. The associated values are the original data
///            provided by the server as well as the error that caused the failure.
internal enum CLDNResult<Value> {
    case success(Value)
    case failure(Error)

    /// Returns `true` if the result is a success, `false` otherwise.
    internal var isSuccess: Bool {
        switch self {
        case .success:
            return true
        case .failure:
            return false
        }
    }

    /// Returns `true` if the result is a failure, `false` otherwise.
    internal var isFailure: Bool {
        return !isSuccess
    }

    /// Returns the associated value if the result is a success, `nil` otherwise.
    internal var value: Value? {
        switch self {
        case .success(let value):
            return value
        case .failure:
            return nil
        }
    }

    /// Returns the associated error value if the result is a failure, `nil` otherwise.
    internal var error: Error? {
        switch self {
        case .success:
            return nil
        case .failure(let error):
            return error
        }
    }
}

// MARK: - CustomStringConvertible

extension CLDNResult: CustomStringConvertible {
    /// The textual representation used when written to an output stream, which includes whether the result was a
    /// success or failure.
    internal var description: String {
        switch self {
        case .success:
            return "SUCCESS"
        case .failure:
            return "FAILURE"
        }
    }
}

// MARK: - CustomDebugStringConvertible

extension CLDNResult: CustomDebugStringConvertible {
    /// The debug textual representation used when written to an output stream, which includes whether the result was a
    /// success or failure in addition to the value or error.
    internal var debugDescription: String {
        switch self {
        case .success(let value):
            return "SUCCESS: \(value)"
        case .failure(let error):
            return "FAILURE: \(error)"
        }
    }
}

// MARK: - Functional APIs

extension CLDNResult {
    /// Creates a `CLDNResult` instance from the result of a closure.
    ///
    /// A failure result is created when the closure throws, and a success result is created when the closure
    /// succeeds without throwing an error.
    ///
    ///     func someString() throws -> String { ... }
    ///
    ///     let result = CLDNResult(value: {
    ///         return try someString()
    ///     })
    ///
    ///     // The type of result is CLDNResult<String>
    ///
    /// The trailing closure syntax is also supported:
    ///
    ///     let result = CLDNResult { try someString() }
    ///
    /// - parameter value: The closure to execute and create the result for.
    internal init(value: () throws -> Value) {
        do {
            self = try .success(value())
        } catch {
            self = .failure(error)
        }
    }

    /// Returns the success value, or throws the failure error.
    ///
    ///     let possibleString: Result<String> = .success("success")
    ///     try print(possibleString.unwrap())
    ///     // Prints "success"
    ///
    ///     let noString: CLDNResult<String> = .failure(error)
    ///     try print(noString.unwrap())
    ///     // Throws error
    internal func unwrap() throws -> Value {
        switch self {
        case .success(let value):
            return value
        case .failure(let error):
            throw error
        }
    }

    /// Evaluates the specified closure when the `CLDNResult` is a success, passing the unwrapped value as a parameter.
    ///
    /// Use the `map` method with a closure that does not throw. For example:
    ///
    ///     let possibleData: CLDNResult<Data> = .success(Data())
    ///     let possibleInt = possibleData.map { $0.count }
    ///     try print(possibleInt.unwrap())
    ///     // Prints "0"
    ///
    ///     let noData: CLDNResult<Data> = .failure(error)
    ///     let noInt = noData.map { $0.count }
    ///     try print(noInt.unwrap())
    ///     // Throws error
    ///
    /// - parameter transform: A closure that takes the success value of the `CLDNResult` instance.
    ///
    /// - returns: A `CLDNResult` containing the result of the given closure. If this instance is a failure, returns the
    ///            same failure.
    internal func map<T>(_ transform: (Value) -> T) -> CLDNResult<T> {
        switch self {
        case .success(let value):
            return .success(transform(value))
        case .failure(let error):
            return .failure(error)
        }
    }

    /// Evaluates the specified closure when the `CLDNResult` is a success, passing the unwrapped value as a parameter.
    ///
    /// Use the `flatMap` method with a closure that may throw an error. For example:
    ///
    ///     let possibleData: CLDNResult<Data> = .success(Data(...))
    ///     let possibleObject = possibleData.flatMap {
    ///         try JSONSerialization.jsonObject(with: $0)
    ///     }
    ///
    /// - parameter transform: A closure that takes the success value of the instance.
    ///
    /// - returns: A `CLDNResult` containing the result of the given closure. If this instance is a failure, returns the
    ///            same failure.
    internal func flatMap<T>(_ transform: (Value) throws -> T) -> CLDNResult<T> {
        switch self {
        case .success(let value):
            do {
                return try .success(transform(value))
            } catch {
                return .failure(error)
            }
        case .failure(let error):
            return .failure(error)
        }
    }

    /// Evaluates the specified closure when the `CLDNResult` is a failure, passing the unwrapped error as a parameter.
    ///
    /// Use the `mapError` function with a closure that does not throw. For example:
    ///
    ///     let possibleData: CLDNResult<Data> = .failure(someError)
    ///     let withMyError: CLDNResult<Data> = possibleData.mapError { MyError.error($0) }
    ///
    /// - Parameter transform: A closure that takes the error of the instance.
    /// - Returns: A `CLDNResult` instance containing the result of the transform. If this instance is a success, returns
    ///            the same instance.
    internal func mapError<T: Error>(_ transform: (Error) -> T) -> CLDNResult {
        switch self {
        case .failure(let error):
            return .failure(transform(error))
        case .success:
            return self
        }
    }

    /// Evaluates the specified closure when the `CLDNResult` is a failure, passing the unwrapped error as a parameter.
    ///
    /// Use the `flatMapError` function with a closure that may throw an error. For example:
    ///
    ///     let possibleData: CLDNResult<Data> = .success(Data(...))
    ///     let possibleObject = possibleData.flatMapError {
    ///         try someFailableFunction(taking: $0)
    ///     }
    ///
    /// - Parameter transform: A throwing closure that takes the error of the instance.
    ///
    /// - Returns: A `CLDNResult` instance containing the result of the transform. If this instance is a success, returns
    ///            the same instance.
    internal func flatMapError<T: Error>(_ transform: (Error) throws -> T) -> CLDNResult {
        switch self {
        case .failure(let error):
            do {
                return try .failure(transform(error))
            } catch {
                return .failure(error)
            }
        case .success:
            return self
        }
    }

    /// Evaluates the specified closure when the `CLDNResult` is a success, passing the unwrapped value as a parameter.
    ///
    /// Use the `withValue` function to evaluate the passed closure without modifying the `CLDNResult` instance.
    ///
    /// - Parameter closure: A closure that takes the success value of this instance.
    /// - Returns: This `CLDNResult` instance, unmodified.
    @discardableResult
    internal func withValue(_ closure: (Value) throws -> Void) rethrows -> CLDNResult {
        if case let .success(value) = self { try closure(value) }

        return self
    }

    /// Evaluates the specified closure when the `CLDNResult` is a failure, passing the unwrapped error as a parameter.
    ///
    /// Use the `withError` function to evaluate the passed closure without modifying the `CLDNResult` instance.
    ///
    /// - Parameter closure: A closure that takes the success value of this instance.
    /// - Returns: This `CLDNResult` instance, unmodified.
    @discardableResult
    internal func withError(_ closure: (Error) throws -> Void) rethrows -> CLDNResult {
        if case let .failure(error) = self { try closure(error) }

        return self
    }

    /// Evaluates the specified closure when the `CLDNResult` is a success.
    ///
    /// Use the `ifSuccess` function to evaluate the passed closure without modifying the `CLDNResult` instance.
    ///
    /// - Parameter closure: A `Void` closure.
    /// - Returns: This `CLDNResult` instance, unmodified.
    @discardableResult
    internal func ifSuccess(_ closure: () throws -> Void) rethrows -> CLDNResult {
        if isSuccess { try closure() }

        return self
    }

    /// Evaluates the specified closure when the `CLDNResult` is a failure.
    ///
    /// Use the `ifFailure` function to evaluate the passed closure without modifying the `CLDNResult` instance.
    ///
    /// - Parameter closure: A `Void` closure.
    /// - Returns: This `CLDNResult` instance, unmodified.
    @discardableResult
    internal func ifFailure(_ closure: () throws -> Void) rethrows -> CLDNResult {
        if isFailure { try closure() }

        return self
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNSessionDelegate.swift
================================================
//
//  CLDNSessionDelegate.swift
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// Responsible for handling all delegate callbacks for the underlying session.
internal class CLDNSessionDelegate: NSObject {

    // MARK: URLSessionDelegate Overrides

    /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didBecomeInvalidWithError:)`.
    internal var sessionDidBecomeInvalidWithError: ((URLSession, Error?) -> Void)?

    /// Overrides default behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)`.
    internal var sessionDidReceiveChallenge: ((URLSession, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

    /// Overrides all behavior for URLSessionDelegate method `urlSession(_:didReceive:completionHandler:)` and requires the caller to call the `completionHandler`.
    internal var sessionDidReceiveChallengeWithCompletion: ((URLSession, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?

    /// Overrides default behavior for URLSessionDelegate method `urlSessionDidFinishEvents(forBackgroundURLSession:)`.
    internal var sessionDidFinishEventsForBackgroundURLSession: ((URLSession) -> Void)?

    // MARK: URLSessionTaskDelegate Overrides

    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)`.
    internal var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?

    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)` and
    /// requires the caller to call the `completionHandler`.
    internal var taskWillPerformHTTPRedirectionWithCompletion: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest, @escaping (URLRequest?) -> Void) -> Void)?

    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)`.
    internal var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?

    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:didReceive:completionHandler:)` and
    /// requires the caller to call the `completionHandler`.
    internal var taskDidReceiveChallengeWithCompletion: ((URLSession, URLSessionTask, URLAuthenticationChallenge, @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) -> Void)?

    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)`.
    internal var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?

    /// Overrides all behavior for URLSessionTaskDelegate method `urlSession(_:task:needNewBodyStream:)` and
    /// requires the caller to call the `completionHandler`.
    internal var taskNeedNewBodyStreamWithCompletion: ((URLSession, URLSessionTask, @escaping (InputStream?) -> Void) -> Void)?

    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:)`.
    internal var taskDidSendBodyData: ((URLSession, URLSessionTask, Int64, Int64, Int64) -> Void)?

    /// Overrides default behavior for URLSessionTaskDelegate method `urlSession(_:task:didCompleteWithError:)`.
    internal var taskDidComplete: ((URLSession, URLSessionTask, Error?) -> Void)?

    // MARK: URLSessionDataDelegate Overrides

    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)`.
    internal var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?

    /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:completionHandler:)` and
    /// requires caller to call the `completionHandler`.
    internal var dataTaskDidReceiveResponseWithCompletion: ((URLSession, URLSessionDataTask, URLResponse, @escaping (URLSession.ResponseDisposition) -> Void) -> Void)?

    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didBecome:)`.
    internal var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?

    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:didReceive:)`.
    internal var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?

    /// Overrides default behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)`.
    internal var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

    /// Overrides all behavior for URLSessionDataDelegate method `urlSession(_:dataTask:willCacheResponse:completionHandler:)` and
    /// requires caller to call the `completionHandler`.
    internal var dataTaskWillCacheResponseWithCompletion: ((URLSession, URLSessionDataTask, CachedURLResponse, @escaping (CachedURLResponse?) -> Void) -> Void)?

    // MARK: URLSessionDownloadDelegate Overrides

    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didFinishDownloadingTo:)`.
    internal var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> Void)?

    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didWriteData:totalBytesWritten:totalBytesExpectedToWrite:)`.
    internal var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?

    /// Overrides default behavior for URLSessionDownloadDelegate method `urlSession(_:downloadTask:didResumeAtOffset:expectedTotalBytes:)`.
    internal var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?

    // MARK: URLSessionStreamDelegate Overrides

    // MARK: Properties

    var retrier: CLDNRequestRetrier?
    weak var sessionManager: CLDNSessionManager?

    var requests: [Int: CLDNRequest] = [:]
    private let lock = NSLock()

    /// Access the task delegate for the specified task in a thread-safe manner.
    internal subscript(task: URLSessionTask) -> CLDNRequest? {
        get {
            lock.lock() ; defer { lock.unlock() }
            return requests[task.taskIdentifier]
        }
        set {
            lock.lock() ; defer { lock.unlock() }
            requests[task.taskIdentifier] = newValue
        }
    }

    // MARK: Lifecycle

    /// Initializes the `CLDNSessionDelegate` instance.
    ///
    /// - returns: The new `CLDNSessionDelegate` instance.
    internal override init() {
        super.init()
    }

    // MARK: NSObject Overrides

    /// Returns a `Bool` indicating whether the `CLDNSessionDelegate` implements or inherits a method that can respond
    /// to a specified message.
    ///
    /// - parameter selector: A selector that identifies a message.
    ///
    /// - returns: `true` if the receiver implements or inherits a method that can respond to selector, otherwise `false`.
    internal override func responds(to selector: Selector) -> Bool {
        #if !os(macOS)
            if selector == #selector(URLSessionDelegate.urlSessionDidFinishEvents(forBackgroundURLSession:)) {
                return sessionDidFinishEventsForBackgroundURLSession != nil
            }
        #endif

        switch selector {
        case #selector(URLSessionDelegate.urlSession(_:didBecomeInvalidWithError:)):
            return sessionDidBecomeInvalidWithError != nil
        case #selector(URLSessionDelegate.urlSession(_:didReceive:completionHandler:)):
            return (sessionDidReceiveChallenge != nil  || sessionDidReceiveChallengeWithCompletion != nil)
        case #selector(URLSessionTaskDelegate.urlSession(_:task:willPerformHTTPRedirection:newRequest:completionHandler:)):
            return (taskWillPerformHTTPRedirection != nil || taskWillPerformHTTPRedirectionWithCompletion != nil)
        case #selector(URLSessionDataDelegate.urlSession(_:dataTask:didReceive:completionHandler:)):
            return (dataTaskDidReceiveResponse != nil || dataTaskDidReceiveResponseWithCompletion != nil)
        default:
            return type(of: self).instancesRespond(to: selector)
        }
    }
}

// MARK: - URLSessionDelegate

extension CLDNSessionDelegate: URLSessionDelegate {
    /// Tells the delegate that the session has been invalidated.
    ///
    /// - parameter session: The session object that was invalidated.
    /// - parameter error:   The error that caused invalidation, or nil if the invalidation was explicit.
    internal func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
        sessionDidBecomeInvalidWithError?(session, error)
    }

    /// Requests credentials from the delegate in response to a session-level authentication request from the
    /// remote server.
    ///
    /// - parameter session:           The session containing the task that requested authentication.
    /// - parameter challenge:         An object that contains the request for authentication.
    /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
    ///                                and credential.
    internal func urlSession(
        _ session: URLSession,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        guard sessionDidReceiveChallengeWithCompletion == nil else {
            sessionDidReceiveChallengeWithCompletion?(session, challenge, completionHandler)
            return
        }

        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?

        if let sessionDidReceiveChallenge = sessionDidReceiveChallenge {
            (disposition, credential) = sessionDidReceiveChallenge(session, challenge)
        }

        completionHandler(disposition, credential)
    }

#if !os(macOS)

    /// Tells the delegate that all messages enqueued for a session have been delivered.
    ///
    /// - parameter session: The session that no longer has any outstanding requests.
    internal func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
        sessionDidFinishEventsForBackgroundURLSession?(session)
    }

#endif
}

// MARK: - URLSessionTaskDelegate

extension CLDNSessionDelegate: URLSessionTaskDelegate {
    /// Tells the delegate that the remote server requested an HTTP redirect.
    ///
    /// - parameter session:           The session containing the task whose request resulted in a redirect.
    /// - parameter task:              The task whose request resulted in a redirect.
    /// - parameter response:          An object containing the server’s response to the original request.
    /// - parameter request:           A URL request object filled out with the new location.
    /// - parameter completionHandler: A closure that your handler should call with either the value of the request
    ///                                parameter, a modified URL request object, or NULL to refuse the redirect and
    ///                                return the body of the redirect response.
    internal func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        willPerformHTTPRedirection response: HTTPURLResponse,
        newRequest request: URLRequest,
        completionHandler: @escaping (URLRequest?) -> Void)
    {
        guard taskWillPerformHTTPRedirectionWithCompletion == nil else {
            taskWillPerformHTTPRedirectionWithCompletion?(session, task, response, request, completionHandler)
            return
        }

        var redirectRequest: URLRequest? = request

        if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
            redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
        }

        completionHandler(redirectRequest)
    }

    /// Requests credentials from the delegate in response to an authentication request from the remote server.
    ///
    /// - parameter session:           The session containing the task whose request requires authentication.
    /// - parameter task:              The task whose request requires authentication.
    /// - parameter challenge:         An object that contains the request for authentication.
    /// - parameter completionHandler: A handler that your delegate method must call providing the disposition
    ///                                and credential.
    internal func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        guard taskDidReceiveChallengeWithCompletion == nil else {
            taskDidReceiveChallengeWithCompletion?(session, task, challenge, completionHandler)
            return
        }

        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
            let result = taskDidReceiveChallenge(session, task, challenge)
            completionHandler(result.0, result.1)
        } else if let delegate = self[task]?.delegate {
            delegate.urlSession(
                session,
                task: task,
                didReceive: challenge,
                completionHandler: completionHandler
            )
        } else {
            urlSession(session, didReceive: challenge, completionHandler: completionHandler)
        }
    }

    /// Tells the delegate when a task requires a new request body stream to send to the remote server.
    ///
    /// - parameter session:           The session containing the task that needs a new body stream.
    /// - parameter task:              The task that needs a new body stream.
    /// - parameter completionHandler: A completion handler that your delegate method should call with the new body stream.
    internal func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
    {
        guard taskNeedNewBodyStreamWithCompletion == nil else {
            taskNeedNewBodyStreamWithCompletion?(session, task, completionHandler)
            return
        }

        if let taskNeedNewBodyStream = taskNeedNewBodyStream {
            completionHandler(taskNeedNewBodyStream(session, task))
        } else if let delegate = self[task]?.delegate {
            delegate.urlSession(session, task: task, needNewBodyStream: completionHandler)
        }
    }

    /// Periodically informs the delegate of the progress of sending body content to the server.
    ///
    /// - parameter session:                  The session containing the data task.
    /// - parameter task:                     The data task.
    /// - parameter bytesSent:                The number of bytes sent since the last time this delegate method was called.
    /// - parameter totalBytesSent:           The total number of bytes sent so far.
    /// - parameter totalBytesExpectedToSend: The expected length of the body data.
    internal func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didSendBodyData bytesSent: Int64,
        totalBytesSent: Int64,
        totalBytesExpectedToSend: Int64)
    {
        if let taskDidSendBodyData = taskDidSendBodyData {
            taskDidSendBodyData(session, task, bytesSent, totalBytesSent, totalBytesExpectedToSend)
        } else if let delegate = self[task]?.delegate as? CLDNUploadTaskDelegate {
            delegate.URLSession(
                session,
                task: task,
                didSendBodyData: bytesSent,
                totalBytesSent: totalBytesSent,
                totalBytesExpectedToSend: totalBytesExpectedToSend
            )
        }
    }

#if !os(watchOS)

    /// Tells the delegate that the session finished collecting metrics for the task.
    ///
    /// - parameter session: The session collecting the metrics.
    /// - parameter task:    The task whose metrics have been collected.
    /// - parameter metrics: The collected metrics.
    @available(iOS 10.0, macOS 10.12, tvOS 10.0, *)
    @objc(URLSession:task:didFinishCollectingMetrics:)
    internal func urlSession(_ session: URLSession, task: URLSessionTask, didFinishCollecting metrics: URLSessionTaskMetrics) {
        self[task]?.delegate.metrics = metrics
    }

#endif

    /// Tells the delegate that the task finished transferring data.
    ///
    /// - parameter session: The session containing the task whose request finished transferring data.
    /// - parameter task:    The task whose request finished transferring data.
    /// - parameter error:   If an error occurred, an error object indicating how the transfer failed, otherwise nil.
    internal func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        /// Executed after it is determined that the request is not going to be retried
        let completeTask: (URLSession, URLSessionTask, Error?) -> Void = { [weak self] session, task, error in
            guard let strongSelf = self else { return }

            strongSelf.taskDidComplete?(session, task, error)

            strongSelf[task]?.delegate.urlSession(session, task: task, didCompleteWithError: error)

            strongSelf[task] = nil
        }

        guard let request = self[task], let sessionManager = sessionManager else {
            completeTask(session, task, error)
            return
        }

        // Run all validations on the request before checking if an error occurred
        request.validations.forEach { $0() }

        // Determine whether an error has occurred
        var error: Error? = error

        if request.delegate.error != nil {
            error = request.delegate.error
        }
        
        /// If an error occurred and the retrier is set, asynchronously ask the retrier if the request
        /// should be retried. Otherwise, complete the task by notifying the task delegate.
        if let retrier = retrier, let error = error {
            retrier.CLDN_Should(sessionManager, retry: request, with: error) { [weak self] shouldRetry, timeDelay in
                guard shouldRetry else { completeTask(session, task, error) ; return }

                DispatchQueue.CLDNUtility.CLDN_after(timeDelay) { [weak self] in
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.sessionManager?.retry(request) ?? false

                    if retrySucceeded, let task = request.task {
                        strongSelf[task] = request
                        return
                    } else {
                        completeTask(session, task, error)
                    }
                }
            }
        } else {
            completeTask(session, task, error)
        }
    }
}

// MARK: - URLSessionDataDelegate

extension CLDNSessionDelegate: URLSessionDataDelegate {
    /// Tells the delegate that the data task received the initial reply (headers) from the server.
    ///
    /// - parameter session:           The session containing the data task that received an initial reply.
    /// - parameter dataTask:          The data task that received an initial reply.
    /// - parameter response:          A URL response object populated with headers.
    /// - parameter completionHandler: A completion handler that your code calls to continue the transfer, passing a
    ///                                constant to indicate whether the transfer should continue as a data task or
    ///                                should become a download task.
    internal func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        didReceive response: URLResponse,
        completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
    {
        guard dataTaskDidReceiveResponseWithCompletion == nil else {
            dataTaskDidReceiveResponseWithCompletion?(session, dataTask, response, completionHandler)
            return
        }

        var disposition: URLSession.ResponseDisposition = .allow

        if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
            disposition = dataTaskDidReceiveResponse(session, dataTask, response)
        }

        completionHandler(disposition)
    }

    /// Tells the delegate that the data task has received some of the expected data.
    ///
    /// - parameter session:  The session containing the data task that provided data.
    /// - parameter dataTask: The data task that provided data.
    /// - parameter data:     A data object containing the transferred data.
    internal func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
        if let dataTaskDidReceiveData = dataTaskDidReceiveData {
            dataTaskDidReceiveData(session, dataTask, data)
        } else if let delegate = self[dataTask]?.delegate as? CLDNDataTaskDelegate {
            delegate.urlSession(session, dataTask: dataTask, didReceive: data)
        }
    }

    /// Asks the delegate whether the data (or upload) task should store the response in the cache.
    ///
    /// - parameter session:           The session containing the data (or upload) task.
    /// - parameter dataTask:          The data (or upload) task.
    /// - parameter proposedResponse:  The default caching behavior. This behavior is determined based on the current
    ///                                caching policy and the values of certain received headers, such as the Pragma
    ///                                and Cache-Control headers.
    /// - parameter completionHandler: A block that your handler must call, providing either the original proposed
    ///                                response, a modified version of that response, or NULL to prevent caching the
    ///                                response. If your delegate implements this method, it must call this completion
    ///                                handler; otherwise, your app leaks memory.
    internal func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        willCacheResponse proposedResponse: CachedURLResponse,
        completionHandler: @escaping (CachedURLResponse?) -> Void)
    {
        guard dataTaskWillCacheResponseWithCompletion == nil else {
            dataTaskWillCacheResponseWithCompletion?(session, dataTask, proposedResponse, completionHandler)
            return
        }

        if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
            completionHandler(dataTaskWillCacheResponse(session, dataTask, proposedResponse))
        } else if let delegate = self[dataTask]?.delegate as? CLDNDataTaskDelegate {
            delegate.urlSession(
                session,
                dataTask: dataTask,
                willCacheResponse: proposedResponse,
                completionHandler: completionHandler
            )
        } else {
            completionHandler(proposedResponse)
        }
    }
}

// MARK: - URLSessionDownloadDelegate

extension CLDNSessionDelegate: URLSessionDownloadDelegate {
    /// Tells the delegate that a download task has finished downloading.
    ///
    /// - parameter session:      The session containing the download task that finished.
    /// - parameter downloadTask: The download task that finished.
    /// - parameter location:     A file URL for the temporary file. Because the file is temporary, you must either
    ///                           internal the file for reading or move it to a permanent location in your app’s sandbox
    ///                           container directory before returning from this delegate method.
    internal func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didFinishDownloadingTo location: URL)
    {
        if let downloadTaskDidFinishDownloadingToURL = downloadTaskDidFinishDownloadingToURL {
            downloadTaskDidFinishDownloadingToURL(session, downloadTask, location)
        }
    }

    /// Periodically informs the delegate about the download’s progress.
    ///
    /// - parameter session:                   The session containing the download task.
    /// - parameter downloadTask:              The download task.
    /// - parameter bytesWritten:              The number of bytes transferred since the last time this delegate
    ///                                        method was called.
    /// - parameter totalBytesWritten:         The total number of bytes transferred so far.
    /// - parameter totalBytesExpectedToWrite: The expected length of the file, as provided by the Content-Length
    ///                                        header. If this header was not provided, the value is
    ///                                        `NSURLSessionTransferSizeUnknown`.
    internal func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didWriteData bytesWritten: Int64,
        totalBytesWritten: Int64,
        totalBytesExpectedToWrite: Int64)
    {
        if let downloadTaskDidWriteData = downloadTaskDidWriteData {
            downloadTaskDidWriteData(session, downloadTask, bytesWritten, totalBytesWritten, totalBytesExpectedToWrite)
        }
    }

    /// Tells the delegate that the download task has resumed downloading.
    ///
    /// - parameter session:            The session containing the download task that finished.
    /// - parameter downloadTask:       The download task that resumed. See explanation in the discussion.
    /// - parameter fileOffset:         If the file's cache policy or last modified date prevents reuse of the
    ///                                 existing content, then this value is zero. Otherwise, this value is an
    ///                                 integer representing the number of bytes on disk that do not need to be
    ///                                 retrieved again.
    /// - parameter expectedTotalBytes: The expected length of the file, as provided by the Content-Length header.
    ///                                 If this header was not provided, the value is NSURLSessionTransferSizeUnknown.
    internal func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didResumeAtOffset fileOffset: Int64,
        expectedTotalBytes: Int64)
    {
        if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
            downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
        }
    }
}



================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNSessionManager.swift
================================================
//
//  CLDNSessionManager.swift
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// Responsible for creating and managing `CLDNRequest` objects, as well as their underlying `NSURLSession`.
internal class CLDNSessionManager {

    // MARK: - Helper Types

    /// Defines whether the `CLDNMultipartFormData` encoding was successful and contains result of the encoding as
    /// associated values.
    ///
    /// - Success: Represents a successful `CLDNMultipartFormData` encoding and contains the new `CLDNUploadRequest` along with
    ///            streaming information.
    /// - Failure: Used to represent a failure in the `CLDNMultipartFormData` encoding and also contains the encoding
    ///            error.
    internal enum MultipartFormDataEncodingResult {
        case success(request: CLDNUploadRequest, streamingFromDisk: Bool, streamFileURL: URL?)
        case failure(Error)
    }

    // MARK: - Properties

    /// A default instance of `SessionManager`, used by top-level Cloudinary request methods, and suitable for use
    /// directly for any ad hoc requests.
    internal static let `default`: CLDNSessionManager = {
        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = CLDNSessionManager.defaultHTTPHeaders

        return CLDNSessionManager(configuration: configuration)
    }()

    /// Creates default values for the "Accept-Encoding", "Accept-Language" and "User-Agent" headers.
    internal static let defaultHTTPHeaders: CLDNHTTPHeaders = {
        // Accept-Encoding HTTP Header; see https://tools.ietf.org/html/rfc7230#section-4.2.3
        let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"

        // Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
        let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
            let quality = 1.0 - (Double(index) * 0.1)
            return "\(languageCode);q=\(quality)"
        }.joined(separator: ", ")

        // User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
        // Example: `iOS Example/1.0 (org.cloudinary.iOS-Example; build:1; iOS 10.0.0) cloudinary/4.0.0`
        let userAgent: String = {
            if let info = Bundle.main.infoDictionary {
                let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
                let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
                let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
                let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"

                let osNameVersion: String = {
                    let version = ProcessInfo.processInfo.operatingSystemVersion
                    let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"

                    let osName: String = {
                        #if os(iOS)
                            return "iOS"
                        #elseif os(watchOS)
                            return "watchOS"
                        #elseif os(tvOS)
                            return "tvOS"
                        #elseif os(macOS)
                            return "OS X"
                        #elseif os(Linux)
                            return "Linux"
                        #else
                            return "Unknown"
                        #endif
                    }()

                    return "\(osName) \(versionString)"
                }()

                let cloudinaryVersion: String = {
                    guard
                        let afInfo = Bundle(for: CLDNSessionManager.self).infoDictionary,
                        let build = afInfo["CFBundleShortVersionString"]
                    else { return "Unknown" }

                    return "Cloudinary/\(build)"
                }()

                return "\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) \(cloudinaryVersion)"
            }

            return "Cloudinary"
        }()

        return [
            "Accept-Encoding": acceptEncoding,
            "Accept-Language": acceptLanguage,
            "User-Agent": userAgent
        ]
    }()

    /// Default memory threshold used when encoding `MultipartFormData` in bytes.
    internal static let multipartFormDataEncodingMemoryThreshold: UInt64 = 10_000_000

    /// The underlying session.
    internal let session: URLSession

    /// The session delegate handling all the task and session delegate callbacks.
    internal let delegate: CLDNSessionDelegate

    /// Whether to start requests immediately after being constructed. `true` by default.
    internal var startRequestsImmediately: Bool = true

    /// The request adapter called each time a new request is created.
    internal var adapter: CLDNRequestAdapter?

    /// The request retrier called each time a request encounters an error to determine whether to retry the request.
    internal var retrier: CLDNRequestRetrier? {
        get { return delegate.retrier }
        set { delegate.retrier = newValue }
    }

    /// The background completion handler closure provided by the UIApplicationDelegate
    /// `application:handleEventsForBackgroundURLSession:completionHandler:` method. By setting the background
    /// completion handler, the SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` closure implementation
    /// will automatically call the handler.
    ///
    /// If you need to handle your own events before the handler is called, then you need to override the
    /// SessionDelegate `sessionDidFinishEventsForBackgroundURLSession` and manually call the handler when finished.
    ///
    /// `nil` by default.
    internal var backgroundCompletionHandler: (() -> Void)?

    let queue = DispatchQueue(label: "com.cloudinary.session-manager." + UUID().uuidString)

    // MARK: - Lifecycle

    /// Creates an instance with the specified `configuration`, `delegate` and `serverTrustPolicyManager`.
    ///
    /// - parameter configuration:            The configuration used to construct the managed session.
    ///                                       `URLSessionConfiguration.default` by default.
    /// - parameter delegate:                 The delegate used when initializing the session. `CLDNSessionDelegate()` by
    ///                                       default.
    /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
    ///                                       challenges. `nil` by default.
    ///
    /// - returns: The new `CLDNSessionManager` instance.
    internal init(
        configuration: URLSessionConfiguration = URLSessionConfiguration.default,
        delegate: CLDNSessionDelegate = CLDNSessionDelegate())
    {
        configuration.urlCredentialStorage = nil;
        self.delegate = delegate
        self.session = URLSession(configuration: configuration, delegate: delegate, delegateQueue: nil)

        commonInit()
    }

    /// Creates an instance with the specified `session`, `delegate` and `serverTrustPolicyManager`.
    ///
    /// - parameter session:                  The URL session.
    /// - parameter delegate:                 The delegate of the URL session. Must equal the URL session's delegate.
    /// - parameter serverTrustPolicyManager: The server trust policy manager to use for evaluating all server trust
    ///                                       challenges. `nil` by default.
    ///
    /// - returns: The new `CLDNSessionManager` instance if the URL session's delegate matches; `nil` otherwise.
    internal init?(
        session: URLSession,
        delegate: CLDNSessionDelegate)
    {
        guard delegate === session.delegate else { return nil }

        self.delegate = delegate
        self.session = session

        commonInit()
    }

    private func commonInit() {

        delegate.sessionManager = self

        delegate.sessionDidFinishEventsForBackgroundURLSession = { [weak self] session in
            guard let strongSelf = self else { return }
            DispatchQueue.main.async { strongSelf.backgroundCompletionHandler?() }
        }
    }

    deinit {
        session.invalidateAndCancel()
    }

    // MARK: - Data Request

    /// Creates a `CLDNDataRequest` to retrieve the contents of the specified `url`, `method`, `parameters`, `encoding`
    /// and `headers`.
    ///
    /// - parameter url:        The URL.
    /// - parameter method:     The HTTP method. `.get` by default.
    /// - parameter parameters: The parameters. `nil` by default.
    /// - parameter encoding:   The parameter encoding. `CLDNURLEncoding.default` by default.
    /// - parameter headers:    The HTTP headers. `nil` by default.
    ///
    /// - returns: The created `CLDNDataRequest`.
    @discardableResult
    internal func request(
        _ url: CLDNURLConvertible,
        method: CLDNHTTPMethod = .get,
        parameters: CLDNParameters? = nil,
        encoding: CLDNParameterEncoding = CLDNURLEncoding.default,
        headers: CLDNHTTPHeaders? = nil)
        -> CLDNDataRequest
    {
        var originalRequest: URLRequest?

        do {
            var parameters = parameters
            let timeoutParamater = parameters?.removeValue(forKey: CLDConfiguration.ConfigParam.Timeout.description)
            
            originalRequest = try URLRequest(url: url, method: method, headers: headers)
            var encodedURLRequest = try encoding.CLDN_Encode(originalRequest!, with: parameters)
            
            if let timeout = timeoutParamater as? NSNumber {
                encodedURLRequest.timeoutInterval = timeout.doubleValue
            }
            
            return request(encodedURLRequest)
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }

    /// Creates a `CLDNDataRequest` to retrieve the contents of a URL based on the specified `urlRequest`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter urlRequest: The URL request.
    ///
    /// - returns: The created `CLDNDataRequest`.
    @discardableResult
    internal func request(_ urlRequest: CLDNURLRequestConvertible) -> CLDNDataRequest {
        var originalRequest: URLRequest?

        do {
            originalRequest = try urlRequest.CLDN_AsURLRequest()
            let originalTask = CLDNDataRequest.Requestable(urlRequest: originalRequest!)

            let task = try originalTask.CLDN_Task(session: session, adapter: adapter, queue: queue)
            let request = CLDNDataRequest(session: session, requestTask: .data(originalTask, task))

            delegate[task] = request

            if startRequestsImmediately { request.resume() }

            return request
        } catch {
            return request(originalRequest, failedWith: error)
        }
    }

    // MARK: Private - Request Implementation

    private func request(_ urlRequest: URLRequest?, failedWith error: Error) -> CLDNDataRequest {
        var requestTask: CLDNRequest.RequestTask = .data(nil, nil)

        if let urlRequest = urlRequest {
            let originalTask = CLDNDataRequest.Requestable(urlRequest: urlRequest)
            requestTask = .data(originalTask, nil)
        }

        let underlyingError = error.underlyingAdaptError ?? error
        let request = CLDNDataRequest(session: session, requestTask: requestTask, error: underlyingError)

        if let retrier = retrier, error is AdaptError {
            allowRetrier(retrier, toRetry: request, with: underlyingError)
        } else {
            if startRequestsImmediately { request.resume() }
        }

        return request
    }

    // MARK: - Upload Request

    // MARK: File

    /// Creates an `CLDNUploadRequest` from the specified `url`, `method` and `headers` for uploading the `file`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter file:    The file to upload.
    /// - parameter url:     The URL.
    /// - parameter method:  The HTTP method. `.post` by default.
    /// - parameter headers: The HTTP headers. `nil` by default.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(
        _ fileURL: URL,
        to url: CLDNURLConvertible,
        method: CLDNHTTPMethod = .post,
        headers: CLDNHTTPHeaders? = nil)
        -> CLDNUploadRequest
    {
        do {
            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
            return upload(fileURL, with: urlRequest)
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    /// Creates a `CLDNUploadRequest` from the specified `urlRequest` for uploading the `file`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter file:       The file to upload.
    /// - parameter urlRequest: The URL request.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(_ fileURL: URL, with urlRequest: CLDNURLRequestConvertible) -> CLDNUploadRequest {
        do {
            let urlRequest = try urlRequest.CLDN_AsURLRequest()
            return upload(.file(fileURL, urlRequest))
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    // MARK: Data

    /// Creates an `CLDNUploadRequest` from the specified `url`, `method` and `headers` for uploading the `data`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter data:    The data to upload.
    /// - parameter url:     The URL.
    /// - parameter method:  The HTTP method. `.post` by default.
    /// - parameter headers: The HTTP headers. `nil` by default.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(
        _ data: Data,
        to url: CLDNURLConvertible,
        method: CLDNHTTPMethod = .post,
        headers: CLDNHTTPHeaders? = nil)
        -> CLDNUploadRequest
    {
        do {
            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
            return upload(data, with: urlRequest)
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    /// Creates an `CLDNUploadRequest` from the specified `urlRequest` for uploading the `data`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter data:       The data to upload.
    /// - parameter urlRequest: The URL request.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(_ data: Data, with urlRequest: CLDNURLRequestConvertible) -> CLDNUploadRequest {
        do {
            let urlRequest = try urlRequest.CLDN_AsURLRequest()
            return upload(.data(data, urlRequest))
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    // MARK: InputStream

    /// Creates an `CLDNUploadRequest` from the specified `url`, `method` and `headers` for uploading the `stream`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter stream:  The stream to upload.
    /// - parameter url:     The URL.
    /// - parameter method:  The HTTP method. `.post` by default.
    /// - parameter headers: The HTTP headers. `nil` by default.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(
        _ stream: InputStream,
        to url: CLDNURLConvertible,
        method: CLDNHTTPMethod = .post,
        headers: CLDNHTTPHeaders? = nil)
        -> CLDNUploadRequest
    {
        do {
            let urlRequest = try URLRequest(url: url, method: method, headers: headers)
            return upload(stream, with: urlRequest)
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    /// Creates an `CLDNUploadRequest` from the specified `urlRequest` for uploading the `stream`.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter stream:     The stream to upload.
    /// - parameter urlRequest: The URL request.
    ///
    /// - returns: The created `CLDNUploadRequest`.
    @discardableResult
    internal func upload(_ stream: InputStream, with urlRequest: CLDNURLRequestConvertible) -> CLDNUploadRequest {
        do {
            let urlRequest = try urlRequest.CLDN_AsURLRequest()
            return upload(.stream(stream, urlRequest))
        } catch {
            return upload(nil, failedWith: error)
        }
    }

    // MARK: CLDNMultipartFormData

    /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
    /// `CLDNUploadRequest` using the `url`, `method` and `headers`.
    ///
    /// It is important to understand the memory implications of uploading `CLDNMultipartFormData`. If the cummulative
    /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
    /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
    /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
    /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
    /// used for larger payloads such as video content.
    ///
    /// The `encodingMemoryThreshold` parameter allows Cloudinary to automatically determine whether to encode in-memory
    /// or stream from disk. If the content length of the `CLDNMultipartFormData` is below the `encodingMemoryThreshold`,
    /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
    /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
    /// technique was used.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter multipartFormData:       The closure used to append body parts to the `CLDNMultipartFormData`.
    /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
    ///                                      `multipartFormDataEncodingMemoryThreshold` by default.
    /// - parameter url:                     The URL.
    /// - parameter method:                  The HTTP method. `.post` by default.
    /// - parameter headers:                 The HTTP headers. `nil` by default.
    /// - parameter encodingCompletion:      The closure called when the `CLDNMultipartFormData` encoding is complete.
    internal func upload(
        multipartFormData: @escaping (CLDNMultipartFormData) -> Void,
        usingThreshold encodingMemoryThreshold: UInt64 = CLDNSessionManager.multipartFormDataEncodingMemoryThreshold,
        to url: CLDNURLConvertible,
        method: CLDNHTTPMethod = .post,
        headers: CLDNHTTPHeaders? = nil,
        queue: DispatchQueue? = nil,
        timeout: NSNumber? = nil,
        encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
    {
        do {
            var urlRequest = try URLRequest(url: url, method: method, headers: headers)
            if let timeout = timeout {
                urlRequest.timeoutInterval = timeout.doubleValue
            }
            return upload(
                multipartFormData: multipartFormData,
                usingThreshold: encodingMemoryThreshold,
                with: urlRequest,
                queue: queue,
                encodingCompletion: encodingCompletion
            )
        } catch {
            (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
        }
    }

    /// Encodes `multipartFormData` using `encodingMemoryThreshold` and calls `encodingCompletion` with new
    /// `CLDNUploadRequest` using the `urlRequest`.
    ///
    /// It is important to understand the memory implications of uploading `CLDNMultipartFormData`. If the cummulative
    /// payload is small, encoding the data in-memory and directly uploading to a server is the by far the most
    /// efficient approach. However, if the payload is too large, encoding the data in-memory could cause your app to
    /// be terminated. Larger payloads must first be written to disk using input and output streams to keep the memory
    /// footprint low, then the data can be uploaded as a stream from the resulting file. Streaming from disk MUST be
    /// used for larger payloads such as video content.
    ///
    /// The `encodingMemoryThreshold` parameter allows Cloudinary to automatically determine whether to encode in-memory
    /// or stream from disk. If the content length of the `CLDNMultipartFormData` is below the `encodingMemoryThreshold`,
    /// encoding takes place in-memory. If the content length exceeds the threshold, the data is streamed to disk
    /// during the encoding process. Then the result is uploaded as data or as a stream depending on which encoding
    /// technique was used.
    ///
    /// If `startRequestsImmediately` is `true`, the request will have `resume()` called before being returned.
    ///
    /// - parameter multipartFormData:       The closure used to append body parts to the `CLDNMultipartFormData`.
    /// - parameter encodingMemoryThreshold: The encoding memory threshold in bytes.
    ///                                      `multipartFormDataEncodingMemoryThreshold` by default.
    /// - parameter urlRequest:              The URL request.
    /// - parameter encodingCompletion:      The closure called when the `CLDNMultipartFormData` encoding is complete.
    internal func upload(
        multipartFormData: @escaping (CLDNMultipartFormData) -> Void,
        usingThreshold encodingMemoryThreshold: UInt64 = CLDNSessionManager.multipartFormDataEncodingMemoryThreshold,
        with urlRequest: CLDNURLRequestConvertible,
        queue: DispatchQueue? = nil,
        encodingCompletion: ((MultipartFormDataEncodingResult) -> Void)?)
    {
        DispatchQueue.global(qos: .utility).async {
            let formData = CLDNMultipartFormData()
            multipartFormData(formData)

            var tempFileURL: URL?

            do {
                var urlRequestWithContentType = try urlRequest.CLDN_AsURLRequest()
                urlRequestWithContentType.setValue(formData.contentType, forHTTPHeaderField: "Content-Type")

                let isBackgroundSession = self.session.configuration.identifier != nil

                if formData.contentLength < encodingMemoryThreshold && !isBackgroundSession {
                    let data = try formData.encode()

                    let encodingResult = MultipartFormDataEncodingResult.success(
                        request: self.upload(data, with: urlRequestWithContentType),
                        streamingFromDisk: false,
                        streamFileURL: nil
                    )

                    (queue ?? DispatchQueue.main).async { encodingCompletion?(encodingResult) }
                } else {
                    let fileManager = FileManager.default
                    let tempDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory())
                    let directoryURL = tempDirectoryURL.appendingPathComponent("org.cloudinary.manager/multipart.form.data")
                    let fileName = UUID().uuidString
                    let fileURL = directoryURL.appendingPathComponent(fileName)

                    tempFileURL = fileURL

                    var directoryError: Error?

                    // Create directory inside serial queue to ensure two threads don't do this in parallel
                    self.queue.sync {
                        do {
                            try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true, attributes: nil)
                        } catch {
                            directoryError = error
                        }
                    }

                    if let directoryError = directoryError { throw directoryError }

                    try formData.writeEncodedData(to: fileURL)

                    let upload = self.upload(fileURL, with: urlRequestWithContentType)

                    // Cleanup the temp file once the upload is complete
                    upload.delegate.queue.addOperation {
                        do {
                            try FileManager.default.removeItem(at: fileURL)
                        } catch {
                            // No-op
                        }
                    }

                    (queue ?? DispatchQueue.main).async {
                        let encodingResult = MultipartFormDataEncodingResult.success(
                            request: upload,
                            streamingFromDisk: true,
                            streamFileURL: fileURL
                        )

                        encodingCompletion?(encodingResult)
                    }
                }
            } catch {
                // Cleanup the temp file in the event that the multipart form data encoding failed
                if let tempFileURL = tempFileURL {
                    do {
                        try FileManager.default.removeItem(at: tempFileURL)
                    } catch {
                        // No-op
                    }
                }

                (queue ?? DispatchQueue.main).async { encodingCompletion?(.failure(error)) }
            }
        }
    }

    // MARK: Private - Upload Implementation

    private func upload(_ uploadable: CLDNUploadRequest.Uploadable) -> CLDNUploadRequest {
        do {
            let task = try uploadable.CLDN_Task(session: session, adapter: adapter, queue: queue)
            let upload = CLDNUploadRequest(session: session, requestTask: .upload(uploadable, task))

            if case let .stream(inputStream, _) = uploadable {
                upload.delegate.taskNeedNewBodyStream = { _, _ in inputStream }
            }

            delegate[task] = upload

            if startRequestsImmediately { upload.resume() }

            return upload
        } catch {
            return upload(uploadable, failedWith: error)
        }
    }

    private func upload(_ uploadable: CLDNUploadRequest.Uploadable?, failedWith error: Error) -> CLDNUploadRequest {
        var uploadTask: CLDNRequest.RequestTask = .upload(nil, nil)

        if let uploadable = uploadable {
            uploadTask = .upload(uploadable, nil)
        }

        let underlyingError = error.underlyingAdaptError ?? error
        let upload = CLDNUploadRequest(session: session, requestTask: uploadTask, error: underlyingError)

        if let retrier = retrier, error is AdaptError {
            allowRetrier(retrier, toRetry: upload, with: underlyingError)
        } else {
            if startRequestsImmediately { upload.resume() }
        }

        return upload
    }

    // MARK: - Internal - Retry Request

    func retry(_ request: CLDNRequest) -> Bool {
        guard let originalTask = request.originalTask else { return false }

        do {
            let task = try originalTask.CLDN_Task(session: session, adapter: adapter, queue: queue)

            if let originalTask = request.task {
                delegate[originalTask] = nil // removes the old request to avoid endless growth
            }

            request.delegate.task = task // resets all task delegate data

            request.retryCount += 1
            request.startTime = CFAbsoluteTimeGetCurrent()
            request.endTime = nil

            task.resume()

            return true
        } catch {
            request.delegate.error = error.underlyingAdaptError ?? error
            return false
        }
    }

    private func allowRetrier(_ retrier: CLDNRequestRetrier, toRetry request: CLDNRequest, with error: Error) {
        DispatchQueue.CLDNUtility.async { [weak self] in
            guard let strongSelf = self else { return }

            retrier.CLDN_Should(strongSelf, retry: request, with: error) { shouldRetry, timeDelay in
                guard let strongSelf = self else { return }

                guard shouldRetry else {
                    if strongSelf.startRequestsImmediately { request.resume() }
                    return
                }

                DispatchQueue.CLDNUtility.CLDN_after(timeDelay) {
                    guard let strongSelf = self else { return }

                    let retrySucceeded = strongSelf.retry(request)

                    if retrySucceeded, let task = request.task {
                        strongSelf.delegate[task] = request
                    } else {
                        if strongSelf.startRequestsImmediately { request.resume() }
                    }
                }
            }
        }
    }
}


================================================
FILE: Cloudinary/Classes/Core/BaseNetwork/CLDNTaskDelegate.swift
================================================
//
//  CLDNTaskDelegate.swift
//
//  Permission is hereby granted, free of charge, to any person obtaining a copy
//  of this software and associated documentation files (the "Software"), to deal
//  in the Software without restriction, including without limitation the rights
//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//  copies of the Software, and to permit persons to whom the Software is
//  furnished to do so, subject to the following conditions:
//
//  The above copyright notice and this permission notice shall be included in
//  all copies or substantial portions of the Software.
//
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//  THE SOFTWARE.
//

import Foundation

/// The task delegate is responsible for handling all delegate callbacks for the underlying task as well as
/// executing all operations attached to the serial operation queue upon task completion.
internal class CLDNTaskDelegate: NSObject {

    // MARK: Properties

    /// The serial operation queue used to execute all operations after the task completes.
    internal let queue: OperationQueue

    /// The data returned by the server.
    internal var data: Data? { return nil }

    /// The error generated throughout the lifecyle of the task.
    internal var error: Error?

    var task: URLSessionTask? {
        set {
            taskLock.lock(); defer { taskLock.unlock() }
            _task = newValue
        }
        get {
            taskLock.lock(); defer { taskLock.unlock() }
            return _task
        }
    }

    var initialResponseTime: CFAbsoluteTime?
    var credential: URLCredential?
    var metrics: AnyObject? // URLSessionTaskMetrics

    private var _task: URLSessionTask? {
        didSet { reset() }
    }

    private let taskLock = NSLock()

    // MARK: Lifecycle

    init(task: URLSessionTask?) {
        _task = task

        self.queue = {
            let operationQueue = OperationQueue()

            operationQueue.name = "com.cloudinary.CLDNTaskDelegateOperationQueue"
            operationQueue.maxConcurrentOperationCount = 1
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility

            return operationQueue
        }()
    }

    func reset() {
        error = nil
        initialResponseTime = nil
    }

    // MARK: URLSessionTaskDelegate

    var taskWillPerformHTTPRedirection: ((URLSession, URLSessionTask, HTTPURLResponse, URLRequest) -> URLRequest?)?
    var taskDidReceiveChallenge: ((URLSession, URLSessionTask, URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
    var taskNeedNewBodyStream: ((URLSession, URLSessionTask) -> InputStream?)?
    var taskDidCompleteWithError: ((URLSession, URLSessionTask, Error?) -> Void)?

    @objc(URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        willPerformHTTPRedirection response: HTTPURLResponse,
        newRequest request: URLRequest,
        completionHandler: @escaping (URLRequest?) -> Void)
    {
        var redirectRequest: URLRequest? = request

        if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
            redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
        }

        completionHandler(redirectRequest)
    }

    @objc(URLSession:task:didReceiveChallenge:completionHandler:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        didReceive challenge: URLAuthenticationChallenge,
        completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
    {
        var disposition: URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential: URLCredential?

        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
            (disposition, credential) = taskDidReceiveChallenge(session, task, challenge)
        } else {
            if challenge.previousFailureCount > 0 {
                disposition = .rejectProtectionSpace
            } else {
                credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)

                if credential != nil {
                    disposition = .useCredential
                }
            }
        }

        completionHandler(disposition, credential)
    }

    @objc(URLSession:task:needNewBodyStream:)
    func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
    {
        var bodyStream: InputStream?

        if let taskNeedNewBodyStream = taskNeedNewBodyStream {
            bodyStream = taskNeedNewBodyStream(session, task)
        }

        completionHandler(bodyStream)
    }

    @objc(URLSession:task:didCompleteWithError:)
    func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
                if self.error == nil { self.error = error }

            }

            queue.isSuspended = false
        }
    }
}

// MARK: -

class CLDNDataTaskDelegate: CLDNTaskDelegate, URLSessionDataDelegate {

    // MARK: Properties

    var dataTask: URLSessionDataTask { return task as! URLSessionDataTask }

    override var data: Data? {
        if dataStream != nil {
            return nil
        } else {
            return mutableData
        }
    }

    var progress: Pr
Download .txt
gitextract_f1wonv0o/

├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── pull_request_template.md
├── .gitignore
├── .gitmodules
├── .swiftpm/
│   └── xcode/
│       └── package.xcworkspace/
│           └── contents.xcworkspacedata
├── .travis.yml
├── CHANGELOG.md
├── Cloudinary/
│   ├── Assets/
│   │   └── .gitkeep
│   └── Classes/
│       ├── Core/
│       │   ├── BaseNetwork/
│       │   │   ├── CLDNConvertible.swift
│       │   │   ├── CLDNError.swift
│       │   │   ├── CLDNMultipartFormData.swift
│       │   │   ├── CLDNParameterEncoding.swift
│       │   │   ├── CLDNRequest.swift
│       │   │   ├── CLDNResponse.swift
│       │   │   ├── CLDNResponseSerialization.swift
│       │   │   ├── CLDNResult.swift
│       │   │   ├── CLDNSessionDelegate.swift
│       │   │   ├── CLDNSessionManager.swift
│       │   │   ├── CLDNTaskDelegate.swift
│       │   │   ├── CLDNTimeline.swift
│       │   │   ├── CLDNValidation.swift
│       │   │   └── DispatchQueue+Cloudinary.swift
│       │   ├── CLDAnalytics.swift
│       │   ├── CLDCloudinary.swift
│       │   ├── CLDCompatibility.swift
│       │   ├── CLDConfiguration.swift
│       │   ├── CLDEagerTransformation.swift
│       │   ├── CLDResponsiveParams.swift
│       │   ├── Cloudinary.h
│       │   ├── Features/
│       │   │   ├── CacheSystem/
│       │   │   │   └── Enum/
│       │   │   │       └── HTTPStatusCode.swift
│       │   │   ├── Downloader/
│       │   │   │   └── CLDDownloader.swift
│       │   │   ├── Helpers/
│       │   │   │   ├── CLDBaseNetworkObject.swift
│       │   │   │   ├── CLDConditionExpression.swift
│       │   │   │   ├── CLDDefinitions.swift
│       │   │   │   ├── CLDExpression.swift
│       │   │   │   ├── CLDOperators.swift
│       │   │   │   ├── CLDTransformation.swift
│       │   │   │   ├── CLDVariable.swift
│       │   │   │   ├── Layers/
│       │   │   │   │   ├── CLDFetchLayer.swift
│       │   │   │   │   ├── CLDLayer.swift
│       │   │   │   │   ├── CLDSubtitlesLayer.swift
│       │   │   │   │   └── CLDTextLayer.swift
│       │   │   │   ├── RequestParams/
│       │   │   │   │   ├── CLDRequestParams.swift
│       │   │   │   │   └── Helpers/
│       │   │   │   │       └── CLDRequestParamsHelpers.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   └── CLDRequest.swift
│       │   │   │   └── Results/
│       │   │   │       ├── CLDBaseResult.swift
│       │   │   │       └── Helpers/
│       │   │   │           ├── AccessibilityAnalysis/
│       │   │   │           │   ├── CLDAccessibilityAnalysisResult.swift
│       │   │   │           │   └── CLDColorblindAccessibilityAnalysisResult.swift
│       │   │   │           ├── CLDBoundingBox.swift
│       │   │   │           ├── CLDDetection.swift
│       │   │   │           ├── CLDFace.swift
│       │   │   │           ├── CLDInfo.swift
│       │   │   │           ├── CLDOcr/
│       │   │   │           │   ├── CLDAdvOcrResult.swift
│       │   │   │           │   ├── CLDOcrBlockResult.swift
│       │   │   │           │   ├── CLDOcrBoundindBlockResult.swift
│       │   │   │           │   ├── CLDOcrDataResult.swift
│       │   │   │           │   ├── CLDOcrDetectedLanguagesResult.swift
│       │   │   │           │   ├── CLDOcrFullTextAnnotationResult.swift
│       │   │   │           │   ├── CLDOcrPageResult.swift
│       │   │   │           │   ├── CLDOcrParagraphResult.swift
│       │   │   │           │   ├── CLDOcrPropertyResult.swift
│       │   │   │           │   ├── CLDOcrResult.swift
│       │   │   │           │   ├── CLDOcrSymbolResult.swift
│       │   │   │           │   ├── CLDOcrTextAnnotationResult.swift
│       │   │   │           │   └── CLDOcrWordResult.swift
│       │   │   │           ├── CLDQualityAnalysis/
│       │   │   │           │   └── CLDQualityAnalysis.swift
│       │   │   │           ├── CLDRekognitionFace.swift
│       │   │   │           └── CommonResultKeys.swift
│       │   │   ├── ManagementApi/
│       │   │   │   ├── CLDManagementApi.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   ├── CLDDeleteRequest.swift
│       │   │   │   │   ├── CLDExplicitRequest.swift
│       │   │   │   │   ├── CLDExplodeRequest.swift
│       │   │   │   │   ├── CLDMultiRequest.swift
│       │   │   │   │   ├── CLDRenameRequest.swift
│       │   │   │   │   ├── CLDSpriteRequest.swift
│       │   │   │   │   ├── CLDTagRequest.swift
│       │   │   │   │   └── CLDTextRequest.swift
│       │   │   │   ├── RequestsParams/
│       │   │   │   │   ├── CLDDeleteByTokenRequestParams.swift
│       │   │   │   │   ├── CLDDestroyRequestParams.swift
│       │   │   │   │   ├── CLDExplicitRequestParams.swift
│       │   │   │   │   ├── CLDExplodeRequestParams.swift
│       │   │   │   │   ├── CLDMultiRequestParams.swift
│       │   │   │   │   ├── CLDRenameRequestParams.swift
│       │   │   │   │   ├── CLDSpriteRequestParams.swift
│       │   │   │   │   ├── CLDTagsRequestParams.swift
│       │   │   │   │   └── CLDTextRequestParams.swift
│       │   │   │   └── Results/
│       │   │   │       ├── CLDDeleteResult.swift
│       │   │   │       ├── CLDExplicitResult.swift
│       │   │   │       ├── CLDExplodeResult.swift
│       │   │   │       ├── CLDMultiResult.swift
│       │   │   │       ├── CLDRenameResult.swift
│       │   │   │       ├── CLDSpriteResult.swift
│       │   │   │       ├── CLDTagResult.swift
│       │   │   │       └── CLDTextResult.swift
│       │   │   ├── UploadWidget/
│       │   │   │   ├── CLDUploaderWidget.swift
│       │   │   │   ├── CropView/
│       │   │   │   │   ├── CLDCropOverlayView.swift
│       │   │   │   │   ├── CLDCropScrollView.swift
│       │   │   │   │   ├── CLDCropScrollViewController.swift
│       │   │   │   │   ├── CLDCropView.swift
│       │   │   │   │   ├── CLDCropViewCalculator.swift
│       │   │   │   │   └── CLDCropViewUIManager.swift
│       │   │   │   ├── WidgetConfiguration/
│       │   │   │   │   └── CLDWidgetConfiguration.swift
│       │   │   │   ├── WidgetElements/
│       │   │   │   │   ├── CLDWidgetAssetContainer.swift
│       │   │   │   │   └── CLDWidgetPreviewCollectionCell.swift
│       │   │   │   ├── WidgetImages/
│       │   │   │   │   ├── BackIconInstructions.swift
│       │   │   │   │   ├── CropIconInstructions.swift
│       │   │   │   │   ├── CropRotateIconInstructions.swift
│       │   │   │   │   ├── DoneIconInstructions.swift
│       │   │   │   │   ├── RatioLockedIconInstructions.swift
│       │   │   │   │   ├── RatioOpenedIconInstructions.swift
│       │   │   │   │   └── RotateIconInstructions.swift
│       │   │   │   ├── WidgetVideo/
│       │   │   │   │   ├── CLDDisplayLinkObserver.swift
│       │   │   │   │   ├── CLDVideoControlsState.swift
│       │   │   │   │   ├── CLDVideoControlsView.swift
│       │   │   │   │   ├── CLDVideoHiddenAndPausedState.swift
│       │   │   │   │   ├── CLDVideoHiddenAndPlayingState.swift
│       │   │   │   │   ├── CLDVideoPlayerView.swift
│       │   │   │   │   ├── CLDVideoShownAndPausedState.swift
│       │   │   │   │   ├── CLDVideoShownAndPlayingState.swift
│       │   │   │   │   └── CLDVideoView.swift
│       │   │   │   └── WidgetViewControllers/
│       │   │   │       ├── CLDWidgetEditViewController.swift
│       │   │   │       ├── CLDWidgetPreviewViewController.swift
│       │   │   │       └── CLDWidgetViewController.swift
│       │   │   ├── Uploader/
│       │   │   │   ├── CLDUploader.swift
│       │   │   │   ├── Preupload/
│       │   │   │   │   └── CLDPreprocessChain.swift
│       │   │   │   ├── RequestParams/
│       │   │   │   │   └── CLDUploadRequestParams.swift
│       │   │   │   ├── Requests/
│       │   │   │   │   ├── CLDDefaultUploadRequest.swift
│       │   │   │   │   ├── CLDUploadRequest.swift
│       │   │   │   │   └── CLDUploadRequestWrapper.swift
│       │   │   │   └── Results/
│       │   │   │       └── CLDUploadResult.swift
│       │   │   └── Url/
│       │   │       └── CLDUrl.swift
│       │   ├── Network/
│       │   │   ├── Adapter/
│       │   │   │   └── CLDNetworkAdapter.swift
│       │   │   ├── CLDDefaultNetworkAdapter.swift
│       │   │   ├── CLDNetworkCoordinator.swift
│       │   │   ├── NetworkRequest/
│       │   │   │   ├── CLDAsyncNetworkUploadRequest.swift
│       │   │   │   ├── CLDErrorRequest.swift
│       │   │   │   ├── CLDGenericNetworkRequest.swift
│       │   │   │   ├── CLDNetworkDataRequestImpl.swift
│       │   │   │   ├── CLDNetworkDownloadRequest.swift
│       │   │   │   └── CLDNetworkUploadRequest.swift
│       │   │   └── PrivacyInfo.xcprivacy
│       │   └── Utils/
│       │       ├── CLDBuildParamsUtils.swift
│       │       ├── CLDCryptoUtils.swift
│       │       ├── CLDDictionaryUtils.swift
│       │       ├── CLDError.swift
│       │       ├── CLDFileUtils.swift
│       │       ├── CLDImageGenerator/
│       │       │   └── CLDImageGenerator.swift
│       │       ├── CLDImageUtils.swift
│       │       ├── CLDJsonUtils.swift
│       │       ├── CLDLogManager.swift
│       │       └── CLDStringUtils.swift
│       └── ios/
│           ├── Extensions/
│           │   ├── CLDTransformation+Ios.swift
│           │   ├── ExtensionCLDDownloader.swift
│           │   ├── UIButton+Cloudinary.swift
│           │   ├── UIImageView+Cloudinary.swift
│           │   └── UIView+Cloudinary.swift
│           ├── NetworkRequest/
│           │   ├── CLDFetchAssetRequestImpl.swift
│           │   └── CLDFetchImageRequestImpl.swift
│           ├── UIViews/
│           │   ├── CLDResponsiveViewHelper.swift
│           │   └── CLDUIImageView.swift
│           ├── Uploader/
│           │   ├── CLDImagePreprocessChain.swift
│           │   ├── CLDPreprocessHelpers.swift
│           │   ├── CLDVideoPreprocessChain.swift
│           │   ├── CLDVideoPreprocessHelpers.swift
│           │   └── CLDVideoTranscode.swift
│           └── Video/
│               ├── Analytics/
│               │   ├── VideoAnalytics.swift
│               │   └── VideoEventsManager.swift
│               └── CLDVideoPlayer.swift
├── Cloudinary.podspec
├── Cloudinary.xcodeproj/
│   ├── Cloudinary_Info.plist
│   ├── project.pbxproj
│   ├── project.xcworkspace/
│   │   ├── contents.xcworkspacedata
│   │   └── xcshareddata/
│   │       ├── IDEWorkspaceChecks.plist
│   │       └── WorkspaceSettings.xcsettings
│   └── xcshareddata/
│       └── xcschemes/
│           └── Cloudinary-Package.xcscheme
├── Example/
│   ├── Cloudinary/
│   │   ├── AppDelegate.swift
│   │   ├── Assets.xcassets/
│   │   │   ├── AppIcon.appiconset/
│   │   │   │   ├── Contents.json
│   │   │   │   └── app_icon 1.heic
│   │   │   ├── Contents.json
│   │   │   ├── Delivery/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── Inner/
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   ├── ski.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   └── sofa.imageset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── Video/
│   │   │   │   │   ├── Contents.json
│   │   │   │   │   ├── instagram.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   ├── tiktok.imageset/
│   │   │   │   │   │   └── Contents.json
│   │   │   │   │   └── youtube.imageset/
│   │   │   │   │       └── Contents.json
│   │   │   │   ├── background_normalizing.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── color_alternation.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── delivery-city.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   ├── localization_branding.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── smart_cropping.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Upload/
│   │   │   │   ├── Contents.json
│   │   │   │   ├── question_mark.imageset/
│   │   │   │   │   └── Contents.json
│   │   │   │   └── upload_missing.imageset/
│   │   │   │       └── Contents.json
│   │   │   ├── Video/
│   │   │   │   ├── Contents.json
│   │   │   │   └── TikTok/
│   │   │   │       ├── Contents.json
│   │   │   │       ├── tiktok_bar_icon.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_comments.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_discover.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_home.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_inbox.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_like.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_more.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_music.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_note.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       ├── tiktok_share.imageset/
│   │   │   │       │   └── Contents.json
│   │   │   │       └── tiktok_social_icon.imageset/
│   │   │   │           └── Contents.json
│   │   │   ├── Widgets/
│   │   │   │   ├── Contents.json
│   │   │   │   └── house.imageset/
│   │   │   │       ├── Contents.json
│   │   │   │       └── Frame_871_ao5o4r_2_viaeyi.heic
│   │   │   ├── back_arrow.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── car-speed-limiter.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── cloudinary_logo.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── info_icon.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── splash.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── splash_without_logo.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── upload.imageset/
│   │   │   │   └── Contents.json
│   │   │   ├── video.imageset/
│   │   │   │   └── Contents.json
│   │   │   └── widgets.imageset/
│   │   │       └── Contents.json
│   │   ├── CldModel.xcdatamodeld/
│   │   │   └── CldModel.xcdatamodel/
│   │   │       └── contents
│   │   ├── Cloudinary_Example-Bridging-Header.h
│   │   ├── Colors.xcassets/
│   │   │   ├── Contents.json
│   │   │   ├── gradient-first.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── gradient-second.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── primary.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── secondary.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── size_green.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── size_red.colorset/
│   │   │   │   └── Contents.json
│   │   │   ├── surface.colorset/
│   │   │   │   └── Contents.json
│   │   │   └── text_not_selected.colorset/
│   │   │       └── Contents.json
│   │   ├── Controllers/
│   │   │   ├── Base/
│   │   │   │   └── BaseViewController.swift
│   │   │   ├── Inner Views/
│   │   │   │   ├── Transform/
│   │   │   │   │   ├── DeliveryViewController.swift
│   │   │   │   │   ├── Optimization/
│   │   │   │   │   │   └── OptimizationViewController.swift
│   │   │   │   │   ├── Transformation/
│   │   │   │   │   │   ├── DeliveryTransformCollectionController.swift
│   │   │   │   │   │   ├── Reveal Image/
│   │   │   │   │   │   │   └── RevealImageController.swift
│   │   │   │   │   │   ├── Smart Cropping/
│   │   │   │   │   │   │   └── SmartCroppingController.swift
│   │   │   │   │   │   ├── TransformCollectionCell.swift
│   │   │   │   │   │   ├── TransformCollectionController.swift
│   │   │   │   │   │   ├── TransformViewController.swift
│   │   │   │   │   │   └── TransformationCell.swift
│   │   │   │   │   └── Use Cases/
│   │   │   │   │       ├── DeliveryUseCasesCollectionController.swift
│   │   │   │   │       ├── Screens/
│   │   │   │   │       │   ├── ConditionalProductViewController.swift
│   │   │   │   │       │   ├── IntegrateAIViewController.swift
│   │   │   │   │       │   └── NormalizingProductAssetsViewController.swift
│   │   │   │   │       ├── UseCaseCell.swift
│   │   │   │   │       ├── UseCaseCollectionCell.swift
│   │   │   │   │       ├── UseCasesCollectionController.swift
│   │   │   │   │       └── UseCasesViewController.swift
│   │   │   │   ├── Upload/
│   │   │   │   │   ├── InnerUploadFrame.swift
│   │   │   │   │   ├── Single Upload/
│   │   │   │   │   │   ├── SingleUploadCell.swift
│   │   │   │   │   │   ├── SingleUploadCollectionController.swift
│   │   │   │   │   │   ├── SingleUploadCollectionLayout.swift
│   │   │   │   │   │   ├── SingleUploadPreview.swift
│   │   │   │   │   │   └── SingleUploadViewController.swift
│   │   │   │   │   ├── Upload Does Not Exist/
│   │   │   │   │   │   └── UploadDoesNotExistController.swift
│   │   │   │   │   ├── UploadChoiceController.swift
│   │   │   │   │   ├── UploadNoCloudController.swift
│   │   │   │   │   └── UploadViewController.swift
│   │   │   │   └── Video/
│   │   │   │       ├── Video Feed/
│   │   │   │       │   └── VideoFeedCell.swift
│   │   │   │       └── VideoFeedCollectionController.swift
│   │   │   ├── MainViewController.swift
│   │   │   ├── SplashViewController.swift
│   │   │   ├── Video Feed/
│   │   │   │   ├── Controllers/
│   │   │   │   │   ├── MainPageController.swift
│   │   │   │   │   ├── VideoFeedContainerController.swift
│   │   │   │   │   ├── VideoFeedController.swift
│   │   │   │   │   ├── VideoFeedViewController.swift
│   │   │   │   │   ├── VideoSocialOverlaysController.swift
│   │   │   │   │   └── VideoViewController.swift
│   │   │   │   ├── Helpers/
│   │   │   │   │   └── VideoHelper.swift
│   │   │   │   └── Resources/
│   │   │   │       └── video_links.plist
│   │   │   └── Widgets/
│   │   │       ├── ImageWidgetViewController.swift
│   │   │       ├── UploadWidgetViewController.swift
│   │   │       └── WidgetsViewController.swift
│   │   ├── Core Data/
│   │   │   ├── AssetItems+CoreDataClass.swift
│   │   │   ├── AssetItems+CoreDataProperties.swift
│   │   │   ├── AssetModel.swift
│   │   │   ├── AssetModel.xcdatamodeld/
│   │   │   │   └── Model.xcdatamodel/
│   │   │   │       └── contents
│   │   │   └── CoreDataHelper.swift
│   │   ├── Custom Views/
│   │   │   ├── GradientView.swift
│   │   │   ├── RevealImageView.swift
│   │   │   ├── ToolBar/
│   │   │   │   ├── Item/
│   │   │   │   │   ├── ToolbarItem.swift
│   │   │   │   │   └── ToolbarItem.xib
│   │   │   │   ├── Toolbar.swift
│   │   │   │   └── Toolbar.xib
│   │   │   └── Upload Loading View/
│   │   │       ├── UploadLoadingView.swift
│   │   │       └── UploadLoadingView.xib
│   │   ├── Extensions/
│   │   │   └── Double+Extension.swift
│   │   ├── GoogleService-Info.plist
│   │   ├── Helpers/
│   │   │   ├── AnimationHelper.swift
│   │   │   ├── Events/
│   │   │   │   ├── EventObject.swift
│   │   │   │   ├── EventsHandler.swift
│   │   │   │   ├── EventsProvider.swift
│   │   │   │   └── FirebaseEventsHandler.swift
│   │   │   └── ImageHelper.swift
│   │   ├── Info.plist
│   │   ├── SceneDelegate.swift
│   │   ├── Utils/
│   │   │   ├── CloudinaryHelper.swift
│   │   │   └── FileUtils.swift
│   │   └── Views/
│   │       ├── Base.lproj/
│   │       │   ├── LaunchScreen.storyboard
│   │       │   └── Main.storyboard
│   │       ├── Inner Views/
│   │       │   ├── Base/
│   │       │   │   └── Base.storyboard
│   │       │   ├── Transform/
│   │       │   │   ├── Delivery.storyboard
│   │       │   │   ├── Optimization.storyboard
│   │       │   │   ├── Use Cases/
│   │       │   │   │   ├── ConditionalProduct.storyboard
│   │       │   │   │   ├── IntegrateAI.storyboard
│   │       │   │   │   └── NormalizingProductAssets.storyboard
│   │       │   │   └── UseCases.storyboard
│   │       │   ├── Upload/
│   │       │   │   ├── InnerUploadFrame.xib
│   │       │   │   ├── Upload.storyboard
│   │       │   │   └── UploadNoCloud.storyboard
│   │       │   ├── Video/
│   │       │   │   ├── Social/
│   │       │   │   │   └── VideoSocialOverlays.storyboard
│   │       │   │   ├── Video.storyboard
│   │       │   │   └── VideoFeed.storyboard
│   │       │   └── Widgets/
│   │       │       ├── ImageWidget.storyboard
│   │       │       ├── UploadWidget.storyboard
│   │       │       └── Widgets.storyboard
│   │       ├── Splash.storyboard
│   │       ├── Transform/
│   │       │   ├── RevealImage.storyboard
│   │       │   ├── SmartCropping.storyboard
│   │       │   └── Transform.storyboard
│   │       └── Upload/
│   │           ├── SingleUpload.storyboard
│   │           ├── SingleUploadPreview.storyboard
│   │           ├── UploadChoice.storyboard
│   │           └── UploadDoesNotExist.storyboard
│   ├── Cloudinary.xcodeproj/
│   │   ├── project.pbxproj
│   │   ├── project.xcworkspace/
│   │   │   ├── contents.xcworkspacedata
│   │   │   └── xcshareddata/
│   │   │       └── IDEWorkspaceChecks.plist
│   │   └── xcshareddata/
│   │       └── xcschemes/
│   │           ├── Cloudinary-Example.xcscheme
│   │           └── travis_public_scheme.xcscheme
│   ├── Podfile
│   ├── README.md
│   ├── Tests/
│   │   ├── BaseNetwork/
│   │   │   ├── BaseTestCase.swift
│   │   │   ├── Core/
│   │   │   │   ├── AuthenticationTests.swift
│   │   │   │   ├── CLDCloudinaryTests.m
│   │   │   │   ├── CLDCloudinaryTests.swift
│   │   │   │   ├── ParameterEncodingTests.swift
│   │   │   │   ├── RequestTests.swift
│   │   │   │   ├── ResponseTests.swift
│   │   │   │   ├── ResultTests.swift
│   │   │   │   ├── SessionDelegateTests.swift
│   │   │   │   ├── SessionManagerTests.swift
│   │   │   │   └── UploadTests.swift
│   │   │   ├── Extensions/
│   │   │   │   ├── CLDNDataResponse+CloudinaryTests.swift
│   │   │   │   ├── CLDNError+CloudinaryTests.swift
│   │   │   │   ├── CLDNResult+CloudinaryTests.swift
│   │   │   │   └── FileManager+CloudinaryTests.swift
│   │   │   ├── Features/
│   │   │   │   ├── CacheTests.swift
│   │   │   │   ├── MultipartFormDataTests.swift
│   │   │   │   ├── ResponseSerializationTests.swift
│   │   │   │   ├── URLProtocolTests.swift
│   │   │   │   └── ValidationTests.swift
│   │   │   ├── ObjcBaseTestCase.h
│   │   │   ├── ObjcBaseTestCase.m
│   │   │   └── Resources/
│   │   │       └── Responses/
│   │   │           ├── JSON/
│   │   │           │   ├── empty_data.json
│   │   │           │   ├── invalid_data.json
│   │   │           │   └── valid_data.json
│   │   │           ├── Property List/
│   │   │           │   ├── empty.data
│   │   │           │   ├── invalid.data
│   │   │           │   └── valid.data
│   │   │           └── String/
│   │   │               ├── empty_string.txt
│   │   │               ├── utf32_string.txt
│   │   │               └── utf8_string.txt
│   │   ├── ConfigurationTests/
│   │   │   ├── CLDAnalyticsTests.swift
│   │   │   ├── CLDConfigurationTests.m
│   │   │   └── CLDConfigurationTests.swift
│   │   ├── CryptoUtilsTests/
│   │   │   ├── CryptoUtilsTests.m
│   │   │   └── CryptoUtilsTests.swift
│   │   ├── FileUtilsTests.swift
│   │   ├── GenerateUrlTests/
│   │   │   ├── UrlTests.m
│   │   │   └── UrlTests.swift
│   │   ├── Info.plist
│   │   ├── NetworkTests/
│   │   │   ├── AccessibilityUploderTests/
│   │   │   │   ├── UploaderAccessibilityTests.m
│   │   │   │   └── UploaderAccessibilityTests.swift
│   │   │   ├── DownloaderAssetTests.swift
│   │   │   ├── DownloaderTests.swift
│   │   │   ├── ManagementApiTests.swift
│   │   │   ├── NetworkBaseTests/
│   │   │   │   ├── NetworkBaseTest.swift
│   │   │   │   ├── NetworkBaseTestObjc.h
│   │   │   │   └── NetworkBaseTestObjc.m
│   │   │   ├── NetworkTestUtils.swift
│   │   │   └── UploaderTests/
│   │   │       ├── MockProvider/
│   │   │       │   └── BaseMockProvider.swift
│   │   │       ├── OcrUploaderTests/
│   │   │       │   ├── ExplicitMockOcrTests.m
│   │   │       │   ├── ExplicitMockOcrTests.swift
│   │   │       │   ├── OcrMockProvider.swift
│   │   │       │   ├── UploaderMockOcrTests.m
│   │   │       │   ├── UploaderMockOcrTests.swift
│   │   │       │   └── UploaderOcrTests.swift
│   │   │       ├── PreprocessUploaderTests/
│   │   │       │   └── PreprocessUploaderTests.swift
│   │   │       ├── QualityAnalysisUploaderTests/
│   │   │       │   ├── MockProviderQualityAnalysis.swift
│   │   │       │   ├── ObjcQualityAnalysisExplicitResultParserTests.m
│   │   │       │   ├── ObjcQualityAnalysisUploadResultParserTests.m
│   │   │       │   ├── ObjcUploaderQualityAnalysisTests.m
│   │   │       │   ├── QualityAnalysisExplicitResultParserTests.swift
│   │   │       │   ├── QualityAnalysisUploadResultParserTests.swift
│   │   │       │   └── UploaderQualityAnalysisTests.swift
│   │   │       └── UploaderTests.swift
│   │   ├── ParamTests/
│   │   │   └── UploadRequestParamsTests.swift
│   │   ├── Preprocess/
│   │   │   ├── PreprocessTests.swift
│   │   │   └── VideoPreprocessTests.swift
│   │   ├── Resources/
│   │   │   └── Docs/
│   │   │       └── docx.docx
│   │   ├── StringUtilsTest.swift
│   │   ├── TestableCloudinary.swift
│   │   ├── TransformationTests/
│   │   │   ├── CLDConditionExpressionTests/
│   │   │   │   ├── CLDConditionExpressionHelpersTests.swift
│   │   │   │   ├── CLDConditionExpressionTests.m
│   │   │   │   └── CLDConditionExpressionTests.swift
│   │   │   ├── CLDExpressionTests/
│   │   │   │   ├── CLDExpressionTests.m
│   │   │   │   └── CLDExpressionTests.swift
│   │   │   ├── CLDTransformationTests/
│   │   │   │   ├── CLDTransformationBaselineTests.swift
│   │   │   │   ├── CLDTransformationConditionsTests.swift
│   │   │   │   ├── CLDTransformationExpressionsTests.swift
│   │   │   │   ├── CLDTransformationTests.m
│   │   │   │   ├── CLDTransformationTests.swift
│   │   │   │   └── CLDTransformationVariablesTests.swift
│   │   │   └── CLDVariableTests/
│   │   │       ├── CLDVariableTests.m
│   │   │       └── CLDVariableTests.swift
│   │   ├── UIExtensions/
│   │   │   ├── CLDVideoPlayerTests.swift
│   │   │   ├── UIBaseTest.swift
│   │   │   ├── UIButtonTests.swift
│   │   │   ├── UIImageViewTests.swift
│   │   │   └── Video Analytics/
│   │   │       ├── VideoEventsManagerTests.swift
│   │   │       └── VideoEventsTests.swift
│   │   └── UploadWidgetTests/
│   │       ├── UploadWidgetHelpersTests/
│   │       │   ├── UploaderWidgetAssetContainerTests.swift
│   │       │   ├── UploaderWidgetConfigurationTests.m
│   │       │   └── UploaderWidgetConfigurationTests.swift
│   │       ├── UploadWidgetVideoTests/
│   │       │   ├── UploaderWidgetVideoControlsTests.swift
│   │       │   ├── UploaderWidgetVideoDisplayLinkTests.swift
│   │       │   ├── UploaderWidgetVideoPlayerTests.swift
│   │       │   └── UploaderWidgetVideoViewTests.swift
│   │       ├── UploaderWidgetEditTests/
│   │       │   └── UploaderWidgetEditViewControllerTests.swift
│   │       ├── UploaderWidgetPreviewTests/
│   │       │   ├── UploaderWidgetCollectionCellTests.swift
│   │       │   └── UploaderWidgetPreviewViewControllerTests.swift
│   │       ├── UploaderWidgetTests/
│   │       │   ├── UploaderWidgetTests.m
│   │       │   └── UploaderWidgetTests.swift
│   │       ├── UploaderWidgetViewControllerTests/
│   │       │   └── UploaderWidgetViewControllerTests.swift
│   │       └── WidgetBaseTest.swift
│   └── WidgetUITests/
│       ├── Info.plist
│       └── WidgetUITests.swift
├── LICENSE
├── Package.swift
├── Package@swift-5.3.swift
├── README.md
└── tools/
    └── get_test_cloud.sh
Download .txt
SYMBOL INDEX (1 symbols across 1 files)

FILE: Example/Tests/NetworkTests/NetworkBaseTests/NetworkBaseTestObjc.h
  type TestResourceType (line 31) | typedef enum TestResourceType: NSUInteger {
Condensed preview — 444 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,526K chars).
[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "chars": 1117,
    "preview": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Bug report fo"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "chars": 528,
    "preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n## Feature re"
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 960,
    "preview": "### Brief Summary of Changes\n<!-- Provide some context as to what was changed, from an implementation standpoint. -->\n\n#"
  },
  {
    "path": ".gitignore",
    "chars": 1619,
    "preview": "# Xcode\n#\n# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore\n\n"
  },
  {
    "path": ".gitmodules",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": ".swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata",
    "chars": 135,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": ".travis.yml",
    "chars": 1205,
    "preview": "language: objective-c\nos: osx\nbefore_script: >\n  export CLOUDINARY_URL=$(bash tools/get_test_cloud.sh);\n  echo cloud_nam"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 11119,
    "preview": "5.2.5 / 2026-01-08\n==================\n\n* Remove _Pods.xcodeproj\n\n5.2.4 / 2025-11-30\n==================\n\nOther changes\n--"
  },
  {
    "path": "Cloudinary/Assets/.gitkeep",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNConvertible.swift",
    "chars": 4218,
    "preview": "//\n//  CLDNConvertible.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  "
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNError.swift",
    "chars": 19744,
    "preview": "//\n//  CLDNError.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of this s"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNMultipartFormData.swift",
    "chars": 23782,
    "preview": "//\n//  CLDNMultipartFormData.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNParameterEncoding.swift",
    "chars": 16860,
    "preview": "//\n//  CLDNParameterEncoding.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a cop"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNRequest.swift",
    "chars": 17371,
    "preview": "//\n//  CLDNRequest.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of t"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNResponse.swift",
    "chars": 6893,
    "preview": "//\n//  CLDNResponse.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of "
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNResponseSerialization.swift",
    "chars": 15348,
    "preview": "//\n//  CLDNResponseSerialization.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNResult.swift",
    "chars": 11208,
    "preview": "//\n//  CLDNResult.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of th"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNSessionDelegate.swift",
    "chars": 28160,
    "preview": "//\n//  CLDNSessionDelegate.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n// "
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNSessionManager.swift",
    "chars": 30548,
    "preview": "//\n//  CLDNSessionManager.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  "
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNTaskDelegate.swift",
    "chars": 11083,
    "preview": "//\n//  CLDNTaskDelegate.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNTimeline.swift",
    "chars": 6897,
    "preview": "//\n//  CLDNTimeline.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of thi"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/CLDNValidation.swift",
    "chars": 8154,
    "preview": "//\n//  CLDNValidation.swift\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a copy\n//  of t"
  },
  {
    "path": "Cloudinary/Classes/Core/BaseNetwork/DispatchQueue+Cloudinary.swift",
    "chars": 1742,
    "preview": "//\n//  DispatchQueue+Cloudinary.swift\n//\n//\n//  Permission is hereby granted, free of charge, to any person obtaining a "
  },
  {
    "path": "Cloudinary/Classes/Core/CLDAnalytics.swift",
    "chars": 5244,
    "preview": "//\n//  CLDAnalytics.swift\n//  Cloudinary\n//\n//  Created by Adi Mizrahi on 13/07/2022.\n//\n\nimport Foundation\nimport UIKit"
  },
  {
    "path": "Cloudinary/Classes/Core/CLDCloudinary.swift",
    "chars": 8739,
    "preview": "//\n//  CLDCloudinary.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/CLDCompatibility.swift",
    "chars": 261,
    "preview": "//\n// Created by Amir on 05/11/2017.\n// Copyright (c) 2017 Cloudinary. All rights reserved.\n//\n\nimport Foundation\n\n#if s"
  },
  {
    "path": "Cloudinary/Classes/Core/CLDConfiguration.swift",
    "chars": 18195,
    "preview": "//\n//  CLDConfiguration.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/CLDEagerTransformation.swift",
    "chars": 845,
    "preview": "//\n// Created by Nitzan Jaitman on 09/05/2018.\n// Copyright (c) 2018 Cloudinary. All rights reserved.\n//\n\nimport Foundat"
  },
  {
    "path": "Cloudinary/Classes/Core/CLDResponsiveParams.swift",
    "chars": 3156,
    "preview": "//\n//  CLDResponsiveParams.swift\n//\n//  Copyright (c) 2018 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereb"
  },
  {
    "path": "Cloudinary/Classes/Core/Cloudinary.h",
    "chars": 1541,
    "preview": "//\n//  Cloudinary.h\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, fr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/CacheSystem/Enum/HTTPStatusCode.swift",
    "chars": 5859,
    "preview": "//\n//  HTTPStatusCode.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Downloader/CLDDownloader.swift",
    "chars": 1787,
    "preview": "//\n//  CLDDownloader.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDBaseNetworkObject.swift",
    "chars": 1561,
    "preview": "//\n//  CLDBaseNetworkObject.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDConditionExpression.swift",
    "chars": 31915,
    "preview": "//\n//  CLDConditionExpression.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDDefinitions.swift",
    "chars": 3658,
    "preview": "//\n//  CLDDefinitions.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDExpression.swift",
    "chars": 14043,
    "preview": "//\n//  CLDExpression.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDOperators.swift",
    "chars": 2363,
    "preview": "//\n//  CLDOperators.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDTransformation.swift",
    "chars": 80162,
    "preview": "//\n//  CLDTransformation.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/CLDVariable.swift",
    "chars": 4705,
    "preview": "//\n//  CLDVariable.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grante"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Layers/CLDFetchLayer.swift",
    "chars": 609,
    "preview": "//\n//  CLDFetchLayer.swift\n//  Cloudinary\n//\n//  Created by Nitzan Jaitman on 19/02/2019.\n//  Copyright © 2019 Cloudinar"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Layers/CLDLayer.swift",
    "chars": 6310,
    "preview": "//\n//  CLDLayer.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Layers/CLDSubtitlesLayer.swift",
    "chars": 1523,
    "preview": "//\n//  CLDSubtitlesLayer.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Layers/CLDTextLayer.swift",
    "chars": 15005,
    "preview": "//\n//  CLDTextLayer.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/RequestParams/CLDRequestParams.swift",
    "chars": 4410,
    "preview": "//\n//  CLDRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/RequestParams/Helpers/CLDRequestParamsHelpers.swift",
    "chars": 11078,
    "preview": "//\n//  CLDRequestParamsHelpers.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Requests/CLDRequest.swift",
    "chars": 2666,
    "preview": "//\n//  CLDRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/CLDBaseResult.swift",
    "chars": 1610,
    "preview": "//\n//  CLDBaseResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/AccessibilityAnalysis/CLDAccessibilityAnalysisResult.swift",
    "chars": 2459,
    "preview": "//\n//  CLDAccessibilityAnalysisResult.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permissi"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/AccessibilityAnalysis/CLDColorblindAccessibilityAnalysisResult.swift",
    "chars": 2508,
    "preview": "//\n//  CLDColorblindAccessibilityAnalysisResult.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDBoundingBox.swift",
    "chars": 2849,
    "preview": "//\n//  CLDBoundingBox.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDDetection.swift",
    "chars": 1944,
    "preview": "//\n//  CLDDetection.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDFace.swift",
    "chars": 8000,
    "preview": "//\n//  CLDFace.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, f"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDInfo.swift",
    "chars": 2094,
    "preview": "//\n//  CLDInfo.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, f"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDAdvOcrResult.swift",
    "chars": 2065,
    "preview": "//\n//  CLDAdvOcrResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrBlockResult.swift",
    "chars": 2712,
    "preview": "//\n//  CLDOcrBlockResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrBoundindBlockResult.swift",
    "chars": 2135,
    "preview": "//\n//  CLDOcrBoundindBlockResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrDataResult.swift",
    "chars": 2329,
    "preview": "//\n//  CLDOcrDataResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrDetectedLanguagesResult.swift",
    "chars": 2130,
    "preview": "//\n//  CLDOcrDetectedLanguagesResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permissio"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrFullTextAnnotationResult.swift",
    "chars": 2119,
    "preview": "//\n//  CLDOcrFullTextAnnotationResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permissi"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrPageResult.swift",
    "chars": 2555,
    "preview": "//\n//  CLDOcrPageResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrParagraphResult.swift",
    "chars": 2471,
    "preview": "//\n//  CLDOcrParagraphResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrPropertyResult.swift",
    "chars": 2012,
    "preview": "//\n//  CLDOcrPropertyResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrResult.swift",
    "chars": 1849,
    "preview": "//\n//  CLDOcrResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrSymbolResult.swift",
    "chars": 2382,
    "preview": "//\n//  CLDOcrSymbolResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrTextAnnotationResult.swift",
    "chars": 2406,
    "preview": "//\n//  CLDOcrTextAnnotationResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission i"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDOcr/CLDOcrWordResult.swift",
    "chars": 2467,
    "preview": "//\n//  CLDOcrWordResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDQualityAnalysis/CLDQualityAnalysis.swift",
    "chars": 4696,
    "preview": "//\n//  CLDQualityAnalysis.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CLDRekognitionFace.swift",
    "chars": 2138,
    "preview": "//\n//  CLDRekognitionFace.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Helpers/Results/Helpers/CommonResultKeys.swift",
    "chars": 3439,
    "preview": "//\n//  CommonResultKeys.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/CLDManagementApi.swift",
    "chars": 27399,
    "preview": "//\n//  CLDManagementApi.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDDeleteRequest.swift",
    "chars": 2438,
    "preview": "//\n//  CLDDeleteRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDExplicitRequest.swift",
    "chars": 2450,
    "preview": "//\n//  CLDExplicitRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDExplodeRequest.swift",
    "chars": 2444,
    "preview": "//\n//  CLDExplodeRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDMultiRequest.swift",
    "chars": 2427,
    "preview": "//\n//  CLDMultiRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDRenameRequest.swift",
    "chars": 2431,
    "preview": "//\n//  CLDRenameRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDSpriteRequest.swift",
    "chars": 2435,
    "preview": "//\n//  CLDSpriteRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDTagRequest.swift",
    "chars": 2411,
    "preview": "//\n//  CLDTagRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Requests/CLDTextRequest.swift",
    "chars": 2417,
    "preview": "//\n//  CLDTextRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDDeleteByTokenRequestParams.swift",
    "chars": 2400,
    "preview": "//\n//  CLDDeleteByTokenRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permissio"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDDestroyRequestParams.swift",
    "chars": 3659,
    "preview": "//\n//  CLDDestroyRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDExplicitRequestParams.swift",
    "chars": 2756,
    "preview": "//\n//  CLDExplicitRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDExplodeRequestParams.swift",
    "chars": 5476,
    "preview": "//\n//  CLDExplodeRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDMultiRequestParams.swift",
    "chars": 4376,
    "preview": "//\n//  CLDMultiRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDRenameRequestParams.swift",
    "chars": 4247,
    "preview": "//\n//  CLDRenameRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDSpriteRequestParams.swift",
    "chars": 4399,
    "preview": "//\n//  CLDSpriteRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDTagsRequestParams.swift",
    "chars": 3497,
    "preview": "//\n//  CLDTagsRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/RequestsParams/CLDTextRequestParams.swift",
    "chars": 8366,
    "preview": "//\n//  CLDTextRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDDeleteResult.swift",
    "chars": 1788,
    "preview": "//\n//  CLDDeleteResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDExplicitResult.swift",
    "chars": 1261,
    "preview": "//\n//  CLDExplicitResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDExplodeResult.swift",
    "chars": 1948,
    "preview": "//\n//  CLDExplodeResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDMultiResult.swift",
    "chars": 1732,
    "preview": "//\n//  CLDMultiResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDRenameResult.swift",
    "chars": 7060,
    "preview": "//\n//  CLDRenameResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDSpriteResult.swift",
    "chars": 3450,
    "preview": "//\n//  CLDSpriteResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDTagResult.swift",
    "chars": 1804,
    "preview": "//\n//  CLDTagResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/ManagementApi/Results/CLDTextResult.swift",
    "chars": 1454,
    "preview": "//\n//  CLDTextResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CLDUploaderWidget.swift",
    "chars": 15875,
    "preview": "//\n//  CLDUploaderWidget.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropOverlayView.swift",
    "chars": 10755,
    "preview": "//\n//  CLDCropOverlayView.swift\n//  ProjectCLDWidgetEditViewController\n//\n//  Created by Arkadi Yoskovitz on 8/17/20.\n//"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropScrollView.swift",
    "chars": 1837,
    "preview": "//\n//  CLDCropScrollView.swift\n//  ProjectCLDWidgetEditViewController\n//\n//  Created by Arkadi Yoskovitz on 8/17/20.\n// "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropScrollViewController.swift",
    "chars": 2885,
    "preview": "//\n//  CLDCropViewScrollViewDelegate.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permissio"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropView.swift",
    "chars": 36277,
    "preview": "//\n//  CLDCropView.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grante"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropViewCalculator.swift",
    "chars": 28286,
    "preview": "//\n//  CLDCropViewCalculator.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/CropView/CLDCropViewUIManager.swift",
    "chars": 18332,
    "preview": "//\n//  CLDCropViewUIManager.swift\n//  Cloudinary\n//\n//  Created by Oz Deutsch on 04/10/2020.\n//\n\nimport UIKit\n\ninternal "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetConfiguration/CLDWidgetConfiguration.swift",
    "chars": 3868,
    "preview": "//\n//  CLDWidgetConfiguration.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetElements/CLDWidgetAssetContainer.swift",
    "chars": 3309,
    "preview": "//\n//  CLDWidgetAssetContainer.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetElements/CLDWidgetPreviewCollectionCell.swift",
    "chars": 1852,
    "preview": "//\n//  CLDWidgetPreviewCollectionCell.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permissi"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/BackIconInstructions.swift",
    "chars": 3298,
    "preview": "//\n//  BackIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/CropIconInstructions.swift",
    "chars": 5650,
    "preview": "//\n//  CropIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/CropRotateIconInstructions.swift",
    "chars": 7584,
    "preview": "//\n//  CropRotateIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission i"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/DoneIconInstructions.swift",
    "chars": 3867,
    "preview": "//\n//  DoneIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/RatioLockedIconInstructions.swift",
    "chars": 8434,
    "preview": "//\n//  RatioLockedIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/RatioOpenedIconInstructions.swift",
    "chars": 9010,
    "preview": "//\n//  RatioOpenedIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetImages/RotateIconInstructions.swift",
    "chars": 7173,
    "preview": "//\n//  RotateIconInstructions.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDDisplayLinkObserver.swift",
    "chars": 3186,
    "preview": "//\n//  CLDDisplayLinkObservers.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoControlsState.swift",
    "chars": 1474,
    "preview": "//\n//  CLDVideoControlsState.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoControlsView.swift",
    "chars": 5754,
    "preview": "//\n//  CLDVideoControlsView.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is here"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoHiddenAndPausedState.swift",
    "chars": 2043,
    "preview": "//\n//  CLDVideoHiddenAndPausedState.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoHiddenAndPlayingState.swift",
    "chars": 2143,
    "preview": "//\n//  CLDVideoHiddenAndPlayingState.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permissio"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoPlayerView.swift",
    "chars": 1672,
    "preview": "//\n//  CLDVideoPlayerView.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoShownAndPausedState.swift",
    "chars": 2058,
    "preview": "//\n//  CLDVideoShownAndPausedState.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoShownAndPlayingState.swift",
    "chars": 2128,
    "preview": "//\n//  CLDVideoShownAndPlayingState.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetVideo/CLDVideoView.swift",
    "chars": 3722,
    "preview": "//\n//  CLDVideoView.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetViewControllers/CLDWidgetEditViewController.swift",
    "chars": 14511,
    "preview": "//\n//  CLDWidgetEditViewController.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission "
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetViewControllers/CLDWidgetPreviewViewController.swift",
    "chars": 13169,
    "preview": "//\n//  CLDWidgetPreviewViewController.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permissi"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/UploadWidget/WidgetViewControllers/CLDWidgetViewController.swift",
    "chars": 20325,
    "preview": "//\n//  CLDWidgetViewController.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/CLDUploader.swift",
    "chars": 24470,
    "preview": "//\n//  CLDUploader.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grante"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/Preupload/CLDPreprocessChain.swift",
    "chars": 3843,
    "preview": "//\n//\n//  CLDPreprocessChain.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/RequestParams/CLDUploadRequestParams.swift",
    "chars": 47928,
    "preview": "//\n//  CLDUploadRequestParams.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/Requests/CLDDefaultUploadRequest.swift",
    "chars": 4042,
    "preview": "//\n//  CLDDefaultUploadRequest.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/Requests/CLDUploadRequest.swift",
    "chars": 3380,
    "preview": "//\n//  CLDUploadRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby g"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/Requests/CLDUploadRequestWrapper.swift",
    "chars": 8224,
    "preview": "//\n//  CLDUploadRequestWrapper.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Uploader/Results/CLDUploadResult.swift",
    "chars": 12677,
    "preview": "//\n//  CLDUploadResult.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Features/Url/CLDUrl.swift",
    "chars": 18090,
    "preview": "//\n//  CLDUrl.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, fr"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/Adapter/CLDNetworkAdapter.swift",
    "chars": 8875,
    "preview": "//\n//  NetworkAdapter.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/CLDDefaultNetworkAdapter.swift",
    "chars": 6401,
    "preview": "//\n//  CLDDefaultNetworkAdapter.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is "
  },
  {
    "path": "Cloudinary/Classes/Core/Network/CLDNetworkCoordinator.swift",
    "chars": 9722,
    "preview": "//\n//  NetworkCoordinator.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDAsyncNetworkUploadRequest.swift",
    "chars": 2759,
    "preview": "//\n//  CLDAsyncNetworkUploadRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDErrorRequest.swift",
    "chars": 2120,
    "preview": "//\n//  CLDRequestError.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDGenericNetworkRequest.swift",
    "chars": 1774,
    "preview": "//\n//  CLDGenericNetworkRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is "
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDNetworkDataRequestImpl.swift",
    "chars": 2678,
    "preview": "//\n//  CLDDataNetworkRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is her"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDNetworkDownloadRequest.swift",
    "chars": 4128,
    "preview": "//\n//  CLDNetworkDownloadRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/NetworkRequest/CLDNetworkUploadRequest.swift",
    "chars": 1538,
    "preview": "//\n//  CLDNetworkUploadRequest.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/Core/Network/PrivacyInfo.xcprivacy",
    "chars": 1818,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDBuildParamsUtils.swift",
    "chars": 2051,
    "preview": "//\n//  CLDBuildParamsUtils.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereb"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDCryptoUtils.swift",
    "chars": 8935,
    "preview": "//\n//  CLDCryptoUtils.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDDictionaryUtils.swift",
    "chars": 1498,
    "preview": "//\n//  CLDDictionaryUtils.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDError.swift",
    "chars": 3079,
    "preview": "//\n//  CLDError.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby granted, "
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDFileUtils.swift",
    "chars": 5148,
    "preview": "//\n//\n//  CLDFileUtils.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gr"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDImageGenerator/CLDImageGenerator.swift",
    "chars": 3414,
    "preview": "//\n//  CLDImageGenerator.swift\n//\n//  Copyright (c) 2020 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDImageUtils.swift",
    "chars": 1428,
    "preview": "//\n//  CLDImageUtils.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDJsonUtils.swift",
    "chars": 2532,
    "preview": "//\n//  CLDJsonUtils.swift\n//\n//  Copyright (c) 2018 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby grant"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDLogManager.swift",
    "chars": 2278,
    "preview": "//\n//  CLDLogManager.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gran"
  },
  {
    "path": "Cloudinary/Classes/Core/Utils/CLDStringUtils.swift",
    "chars": 6194,
    "preview": "//\n//  CLDStringUtils.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/ios/Extensions/CLDTransformation+Ios.swift",
    "chars": 468,
    "preview": "//\n//  CLDTransformation+Ios.swift\n//  Cloudinary\n//\n//  Created on 23/02/2020.\n//\n\nimport Foundation\nimport UIKit\n\nexte"
  },
  {
    "path": "Cloudinary/Classes/ios/Extensions/ExtensionCLDDownloader.swift",
    "chars": 3464,
    "preview": "//\n//  ExtensionCLDDownloader.swift\n//\n//  Copyright (c) 2021 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/ios/Extensions/UIButton+Cloudinary.swift",
    "chars": 6677,
    "preview": "//\n//  UIButton+Cloudinary.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereb"
  },
  {
    "path": "Cloudinary/Classes/ios/Extensions/UIImageView+Cloudinary.swift",
    "chars": 4777,
    "preview": "//\n//  UIImageView+Cloudinary.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is he"
  },
  {
    "path": "Cloudinary/Classes/ios/Extensions/UIView+Cloudinary.swift",
    "chars": 2789,
    "preview": "//\n//  UIView+Cloudinary.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby "
  },
  {
    "path": "Cloudinary/Classes/ios/NetworkRequest/CLDFetchAssetRequestImpl.swift",
    "chars": 4724,
    "preview": "//\n//  CLDFetchAssetRequestImpl.swift\n//\n//  Copyright (c) 2021 Cloudinary (http://cloudinary.com)\n//\n//  Permission is "
  },
  {
    "path": "Cloudinary/Classes/ios/NetworkRequest/CLDFetchImageRequestImpl.swift",
    "chars": 4491,
    "preview": "//\n//  CLDFetchImageRequestImpl.swift\n//\n//  Copyright (c) 2016 Cloudinary (http://cloudinary.com)\n//\n//  Permission is "
  },
  {
    "path": "Cloudinary/Classes/ios/UIViews/CLDResponsiveViewHelper.swift",
    "chars": 5571,
    "preview": "//\n//  CLDResponsiveViewHelper.swift\n//\n//  Copyright (c) 2018 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/ios/UIViews/CLDUIImageView.swift",
    "chars": 3516,
    "preview": "//\n//  CLDUIImageView.swift\n//\n//  Copyright (c) 2018 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary/Classes/ios/Uploader/CLDImagePreprocessChain.swift",
    "chars": 2048,
    "preview": "//\n//\n//  CLDImagePreprocessChain.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission i"
  },
  {
    "path": "Cloudinary/Classes/ios/Uploader/CLDPreprocessHelpers.swift",
    "chars": 9130,
    "preview": "//\n//\n//  CLDPreprocessHelpers.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/ios/Uploader/CLDVideoPreprocessChain.swift",
    "chars": 5110,
    "preview": "//\n//  CLDVideoPreprocessChain.swift\n//\n//  Copyright (c) 2017 Cloudinary (http://cloudinary.com)\n//\n//  Permission is h"
  },
  {
    "path": "Cloudinary/Classes/ios/Uploader/CLDVideoPreprocessHelpers.swift",
    "chars": 1837,
    "preview": "import Foundation\nimport AVKit\n\npublic class CLDVideoPreprocessHelpers {\n\n    private var steps: [(CLDVideoTranscode) th"
  },
  {
    "path": "Cloudinary/Classes/ios/Uploader/CLDVideoTranscode.swift",
    "chars": 5012,
    "preview": "import Foundation\nimport AVFoundation\n\npublic class CLDVideoTranscode {\n    let sourceURL: URL\n    var outputURL: URL?\n "
  },
  {
    "path": "Cloudinary/Classes/ios/Video/Analytics/VideoAnalytics.swift",
    "chars": 5826,
    "preview": "//\n//  VideoAnalytics.swift\n//  Cloudinary\n//\n//  Created by Adi Mizrahi on 14/12/2023.\n//\n\nimport Foundation\n\npublic ty"
  },
  {
    "path": "Cloudinary/Classes/ios/Video/Analytics/VideoEventsManager.swift",
    "chars": 5316,
    "preview": "//\n//  VideoEventsManager.swift\n//  Cloudinary\n//\n//  Created by Adi Mizrahi on 14/12/2023.\n//\n\nimport Foundation\n@objcM"
  },
  {
    "path": "Cloudinary/Classes/ios/Video/CLDVideoPlayer.swift",
    "chars": 9678,
    "preview": "//\n//  CLDVideoPlayer.swift\n//\n//  Copyright (c) 2018 Cloudinary (http://cloudinary.com)\n//\n//  Permission is hereby gra"
  },
  {
    "path": "Cloudinary.podspec",
    "chars": 1915,
    "preview": "#\n# Be sure to run `pod lib lint Cloudinary.podspec' to ensure this is a\n# valid spec before submitting.\n#\n# Any lines s"
  },
  {
    "path": "Cloudinary.xcodeproj/Cloudinary_Info.plist",
    "chars": 723,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en<"
  },
  {
    "path": "Cloudinary.xcodeproj/project.pbxproj",
    "chars": 80530,
    "preview": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 46;\n\tobjects = {\n\n/* Begin PBXBuildFile section *"
  },
  {
    "path": "Cloudinary.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "chars": 134,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef"
  },
  {
    "path": "Cloudinary.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "chars": 238,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Cloudinary.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "chars": 269,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/P"
  },
  {
    "path": "Cloudinary.xcodeproj/xcshareddata/xcschemes/Cloudinary-Package.xcscheme",
    "chars": 2025,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"9999\"\n   version = \"1.3\">\n   <BuildAction\n      "
  },
  {
    "path": "Example/Cloudinary/AppDelegate.swift",
    "chars": 663,
    "preview": "//\n//  AppDelegate.swift\n//  iOS_Geekle_Conference\n//\n//  Created by Adi Mizrahi on 11/09/2023.\n//\n\nimport UIKit\n\n@main\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "chars": 215,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"app_icon 1.heic\",\n      \"idiom\" : \"universal\",\n      \"platform\" : \"ios\",\n    "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Inner/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Inner/ski.imageset/Contents.json",
    "chars": 168,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"smart-crop-4 (4) 1.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Inner/sofa.imageset/Contents.json",
    "chars": 154,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"sofa.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" :"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Video/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Video/instagram.imageset/Contents.json",
    "chars": 224,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Group.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author\" "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Video/tiktok.imageset/Contents.json",
    "chars": 236,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"tiktok-white-icon.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/Video/youtube.imageset/Contents.json",
    "chars": 240,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Youtube_shorts_icon 1.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" :"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/background_normalizing.imageset/Contents.json",
    "chars": 172,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"background_normalizing.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/color_alternation.imageset/Contents.json",
    "chars": 167,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"color_alternation.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/delivery-city.imageset/Contents.json",
    "chars": 174,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"optimization_hero_gezokr.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/localization_branding.imageset/Contents.json",
    "chars": 171,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"localization_branding.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" :"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Delivery/smart_cropping.imageset/Contents.json",
    "chars": 165,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Rectangle 3 (1).png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n   "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Upload/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Upload/question_mark.imageset/Contents.json",
    "chars": 239,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"ic_help_outline_24px.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : "
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Upload/upload_missing.imageset/Contents.json",
    "chars": 163,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"picturepile-4.png\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \""
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Video/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Video/TikTok/Contents.json",
    "chars": 63,
    "preview": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Video/TikTok/tiktok_bar_icon.imageset/Contents.json",
    "chars": 226,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Group 5.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Video/TikTok/tiktok_comments.imageset/Contents.json",
    "chars": 226,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Group 2.svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"author"
  },
  {
    "path": "Example/Cloudinary/Assets.xcassets/Video/TikTok/tiktok_discover.imageset/Contents.json",
    "chars": 229,
    "preview": "{\n  \"images\" : [\n    {\n      \"filename\" : \"Vector (3).svg\",\n      \"idiom\" : \"universal\"\n    }\n  ],\n  \"info\" : {\n    \"aut"
  }
]

// ... and 244 more files (download for full content)

About this extraction

This page contains the full source code of the cloudinary/cloudinary_ios GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 444 files (3.2 MB), approximately 865.0k tokens, and a symbol index with 1 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!