diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 3196c67..c6719e5 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -22,12 +22,12 @@ jobs:
- name: Build Example Project
run: |
cd Example
- xcodebuild -workspace ImageKit.xcworkspace -scheme ImageKit-Example -destination 'platform=iOS Simulator,name=iPhone 15' clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
+ xcodebuild -workspace ImageKit.xcworkspace -scheme ImageKit-Example -destination 'platform=iOS Simulator,name=iPhone 17' clean build CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
- name: Run Tests
run: |
cd Example
- xcodebuild -workspace ImageKit.xcworkspace -scheme ImageKit-Example -destination 'platform=iOS Simulator,name=iPhone 15' -enableCodeCoverage YES clean test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
+ xcodebuild -workspace ImageKit.xcworkspace -scheme ImageKit-Example -destination 'platform=iOS Simulator,name=iPhone 17' -enableCodeCoverage YES clean test CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO
- name: Upload Coverage to codecov
run: bash <(curl -s https://codecov.io/bash) -J '^ImageKitIO$' -X coveragepy
diff --git a/.gitignore b/.gitignore
index eb137ee..3cc75e1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -37,4 +37,8 @@ Carthage/Build
Pods/*
Example/Pods/*
Server/node_modules
-Server/*.lock
\ No newline at end of file
+Server/*.lock
+
+#Swift package build outputs
+.swiftpm
+.build/
diff --git a/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000..919434a
--- /dev/null
+++ b/.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/Example/ImageKit.xcodeproj/project.pbxproj b/Example/ImageKit.xcodeproj/project.pbxproj
index 6a97bd0..fcca67c 100644
--- a/Example/ImageKit.xcodeproj/project.pbxproj
+++ b/Example/ImageKit.xcodeproj/project.pbxproj
@@ -160,6 +160,7 @@
TargetAttributes = {
607FACCF1AFB9204008FA782 = {
CreatedOnToolsVersion = 6.3.1;
+ DevelopmentTeam = FY66VYM646;
LastSwiftMigration = 0900;
};
};
@@ -385,11 +386,12 @@
baseConfigurationReference = 7661EF116E06444B352B18F0 /* Pods-ImageKit_Example.debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = FY66VYM646;
INFOPLIST_FILE = ImageKit/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
- PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.--PRODUCT-NAME-";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
@@ -401,11 +403,12 @@
baseConfigurationReference = BFEF935EF71672E4888B2B32 /* Pods-ImageKit_Example.release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ DEVELOPMENT_TEAM = FY66VYM646;
INFOPLIST_FILE = ImageKit/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 12.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
MODULE_NAME = ExampleApp;
- PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.$(PRODUCT_NAME:rfc1034identifier)";
+ PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.demo.--PRODUCT-NAME-";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_SWIFT3_OBJC_INFERENCE = Default;
SWIFT_VERSION = 4.0;
diff --git a/Example/Podfile b/Example/Podfile
index 573d4b9..e094ec8 100644
--- a/Example/Podfile
+++ b/Example/Podfile
@@ -7,7 +7,7 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
target.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '13.0'
end
end
installer.pods_project.targets.each do |target|
diff --git a/Example/Podfile.lock b/Example/Podfile.lock
index 30691a3..3c4c07e 100644
--- a/Example/Podfile.lock
+++ b/Example/Podfile.lock
@@ -1,6 +1,6 @@
PODS:
- - ImageKitIO (3.0.0)
- - ImageKitIO/Tests (3.0.0):
+ - ImageKitIO (3.1.0)
+ - ImageKitIO/Tests (3.1.0):
- Mocker (~> 2.5)
- Nimble (~> 10.0.0)
- Quick (~> 5.0.1)
@@ -23,11 +23,11 @@ EXTERNAL SOURCES:
:path: "../"
SPEC CHECKSUMS:
- ImageKitIO: 804da6f03a903c4f540d6a3c0dd8ee87e9f29040
+ ImageKitIO: 9cb4bb64a800a248f0f60ed5070458bd3e6ac61a
Mocker: 8c731a8104962f246cadf2b02556218e9edc1390
Nimble: 5316ef81a170ce87baf72dd961f22f89a602ff84
Quick: 749aa754fd1e7d984f2000fe051e18a3a9809179
-PODFILE CHECKSUM: 406170311ac5dd5482330203b884d5db8ef323c7
+PODFILE CHECKSUM: 8224e8c767b2e111bf4f338e9aaa55d7cd447b9e
-COCOAPODS: 1.15.2
+COCOAPODS: 1.16.2
diff --git a/ImageKitIO.podspec b/ImageKitIO.podspec
index 7200e75..411fa79 100644
--- a/ImageKitIO.podspec
+++ b/ImageKitIO.podspec
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'ImageKitIO'
- s.version = '3.0.0'
+ s.version = '3.1.0'
s.summary = 'iOS SDK for ImageKit.io'
@@ -14,13 +14,16 @@ ImageKit is a complete image optimization and transformation solution that comes
s.author = { 'ImageKit Developer' => 'developer@imagekit.io', 'ahnv' => 'abhinav@imagekit.io' }
s.source = { :git => 'https://github.com/imagekit-developer/imagekit-ios.git', :tag => s.version.to_s }
- s.swift_version = '4.0'
- s.ios.deployment_target = '12.0'
+ s.swift_version = '6.0'
+ s.ios.deployment_target = '13.0'
- s.source_files = 'ImageKit/**/*'
+ s.source_files = 'Sources/ImageKit/**/*'
s.test_spec 'Tests' do |test_spec|
test_spec.source_files = 'Tests/**/*'
+ test_spec.resource_bundles = {
+ "ImageKitIO_ImageKitIO-Tests" => ["Tests/fixtures/**"]
+ }
test_spec.dependency 'Quick', '~> 5.0.1'
test_spec.dependency 'Nimble', '~> 10.0.0'
test_spec.dependency 'Mocker', '~> 2.5'
diff --git a/Package.resolved b/Package.resolved
new file mode 100644
index 0000000..2eaf445
--- /dev/null
+++ b/Package.resolved
@@ -0,0 +1,60 @@
+{
+ "originHash" : "60afdccc3655ee729de5d4e7daa470f850930a652f5b2e7235ced4ef83f077f9",
+ "pins" : [
+ {
+ "identity" : "cwlcatchexception",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/mattgallagher/CwlCatchException.git",
+ "state" : {
+ "revision" : "07b2ba21d361c223e25e3c1e924288742923f08c",
+ "version" : "2.2.1"
+ }
+ },
+ {
+ "identity" : "cwlpreconditiontesting",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/mattgallagher/CwlPreconditionTesting.git",
+ "state" : {
+ "revision" : "0139c665ebb45e6a9fbdb68aabfd7c39f3fe0071",
+ "version" : "2.2.2"
+ }
+ },
+ {
+ "identity" : "mocker",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/WeTransfer/Mocker.git",
+ "state" : {
+ "revision" : "5d86f27a8f80d4ba388bc1a379a3c2289a1f3d18",
+ "version" : "2.6.0"
+ }
+ },
+ {
+ "identity" : "nimble",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Quick/Nimble.git",
+ "state" : {
+ "revision" : "1f3bde57bde12f5e7b07909848c071e9b73d6edc",
+ "version" : "10.0.0"
+ }
+ },
+ {
+ "identity" : "quick",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/Quick/Quick.git",
+ "state" : {
+ "revision" : "f9d519828bb03dfc8125467d8f7b93131951124c",
+ "version" : "5.0.1"
+ }
+ },
+ {
+ "identity" : "swifter",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/httpswift/swifter.git",
+ "state" : {
+ "revision" : "9483a5d459b45c3ffd059f7b55f9638e268632fd",
+ "version" : "1.5.0"
+ }
+ }
+ ],
+ "version" : 3
+}
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..dbeb2c2
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,35 @@
+// swift-tools-version: 6.1
+// The swift-tools-version declares the minimum version of Swift required to build this package.
+
+import PackageDescription
+
+let package = Package(
+ name: "ImageKitIO",
+ platforms: [.iOS(.v13)],
+ products: [
+ // Products define the executables and libraries a package produces, making them visible to other packages.
+ .library(
+ name: "ImageKitIO",
+ targets: ["ImageKitIO"]),
+ ],
+ dependencies: [
+ .package(url: "https://github.com/Quick/Quick.git", from: "5.0.1"),
+ .package(url: "https://github.com/Quick/Nimble.git", from: "10.0.0"),
+ .package(url: "https://github.com/WeTransfer/Mocker.git", from: "2.5.0"),
+ .package(url: "https://github.com/httpswift/swifter.git", from: "1.5.0")
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package, defining a module or a test suite.
+ // Targets can depend on other targets in this package and products from dependencies.
+ .target(
+ name: "ImageKitIO",
+ path: "Sources",
+ ),
+ .testTarget(
+ name: "ImageKitIO-Tests",
+ dependencies: ["ImageKitIO", "Quick", "Nimble", "Mocker", .product(name: "Swifter", package: "swifter")],
+ path: "Tests",
+ resources: [.process("fixtures")]
+ ),
+ ]
+)
diff --git a/README.md b/README.md
index f27421b..1ba3f54 100644
--- a/README.md
+++ b/README.md
@@ -32,9 +32,58 @@ ImageKit iOS Pod allows you to use real-time [image resizing](https://docs.image
## Installation
### Requirements
-The library requires Swift 4.0 or above.
+The library requires Swift 6.0 and minimum iOS version for deployment be 13.0 or above.
-#### CocoaPods
+### Swift Package Manager
+
+You can integrate ImageKit into your iOS project using the Swift Package Manager (SPM).
+
+After completing the Swift Package setup in your project, you can add ImageKit in one of the following ways:
+
+#### Using Xcode
+
+Open your project in Xcode, then go to File → Add Packages…
+
+```
+https://github.com/imagekit-developer/imagekit-ios.git
+```
+
+Select the Up to Next Major version rule, starting from version 3.1.0, and add the package to your app target’s Package Dependencies list.
+
+#### Using `Package.swift`
+
+If you prefer managing dependencies manually, add ImageKit to the dependencies array in your `Package.swift` file:
+
+```swift
+// swift-tools-version: 6.0
+import PackageDescription
+
+let package = Package(
+ name: "Example",
+ platforms: [
+ .iOS(.v13)
+ ],
+ dependencies: [
+ .package(url: "https://github.com/imagekit-developer/imagekit-ios.git", .upToNextMajor(from: "3.1.0"))
+ ],
+ targets: [
+ .target(
+ name: "Example",
+ dependencies: [
+ "ImageKitIO"
+ ]
+ )
+ ]
+)
+```
+
+Once added, you can import ImageKitIO into your Swift files:
+
+```
+import ImageKitIO
+```
+
+### CocoaPods
You can use CocoaPods to install ImageKit by adding it to your Podfile:
diff --git a/ImageKit/Entity/CropMode.swift b/Sources/ImageKit/Entity/CropMode.swift
similarity index 100%
rename from ImageKit/Entity/CropMode.swift
rename to Sources/ImageKit/Entity/CropMode.swift
diff --git a/ImageKit/Entity/CropType.swift b/Sources/ImageKit/Entity/CropType.swift
similarity index 100%
rename from ImageKit/Entity/CropType.swift
rename to Sources/ImageKit/Entity/CropType.swift
diff --git a/ImageKit/Entity/FocusType.swift b/Sources/ImageKit/Entity/FocusType.swift
similarity index 100%
rename from ImageKit/Entity/FocusType.swift
rename to Sources/ImageKit/Entity/FocusType.swift
diff --git a/ImageKit/Entity/Format.swift b/Sources/ImageKit/Entity/Format.swift
similarity index 100%
rename from ImageKit/Entity/Format.swift
rename to Sources/ImageKit/Entity/Format.swift
diff --git a/ImageKit/Entity/IKError.swift b/Sources/ImageKit/Entity/IKError.swift
similarity index 88%
rename from ImageKit/Entity/IKError.swift
rename to Sources/ImageKit/Entity/IKError.swift
index ff93a01..ad3f3cb 100644
--- a/ImageKit/Entity/IKError.swift
+++ b/Sources/ImageKit/Entity/IKError.swift
@@ -17,7 +17,7 @@ public enum IKError: Error {
case serverSideError(Int)
}
- public enum MultipartEncodingFailureReason {
+ public enum MultipartEncodingFailureReason : Sendable {
case inputStreamReadFailed(error: Error)
}
diff --git a/ImageKit/Entity/InvalidArgumentError.swift b/Sources/ImageKit/Entity/InvalidArgumentError.swift
similarity index 100%
rename from ImageKit/Entity/InvalidArgumentError.swift
rename to Sources/ImageKit/Entity/InvalidArgumentError.swift
diff --git a/ImageKit/Entity/Rotation.swift b/Sources/ImageKit/Entity/Rotation.swift
similarity index 100%
rename from ImageKit/Entity/Rotation.swift
rename to Sources/ImageKit/Entity/Rotation.swift
diff --git a/ImageKit/Entity/StreamingFormat.swift b/Sources/ImageKit/Entity/StreamingFormat.swift
similarity index 100%
rename from ImageKit/Entity/StreamingFormat.swift
rename to Sources/ImageKit/Entity/StreamingFormat.swift
diff --git a/ImageKit/Entity/TransformationMapping.swift b/Sources/ImageKit/Entity/TransformationMapping.swift
similarity index 100%
rename from ImageKit/Entity/TransformationMapping.swift
rename to Sources/ImageKit/Entity/TransformationMapping.swift
diff --git a/ImageKit/Entity/TransformationPosition.swift b/Sources/ImageKit/Entity/TransformationPosition.swift
similarity index 100%
rename from ImageKit/Entity/TransformationPosition.swift
rename to Sources/ImageKit/Entity/TransformationPosition.swift
diff --git a/ImageKit/Entity/UploadAPIError.swift b/Sources/ImageKit/Entity/UploadAPIError.swift
similarity index 100%
rename from ImageKit/Entity/UploadAPIError.swift
rename to Sources/ImageKit/Entity/UploadAPIError.swift
diff --git a/ImageKit/Entity/UploadAPIResponse.swift b/Sources/ImageKit/Entity/UploadAPIResponse.swift
similarity index 100%
rename from ImageKit/Entity/UploadAPIResponse.swift
rename to Sources/ImageKit/Entity/UploadAPIResponse.swift
diff --git a/ImageKit/Entity/UploadPolicy.swift b/Sources/ImageKit/Entity/UploadPolicy.swift
similarity index 95%
rename from ImageKit/Entity/UploadPolicy.swift
rename to Sources/ImageKit/Entity/UploadPolicy.swift
index 1e0ebfd..a261348 100644
--- a/ImageKit/Entity/UploadPolicy.swift
+++ b/Sources/ImageKit/Entity/UploadPolicy.swift
@@ -7,7 +7,7 @@
import Foundation
-public class UploadPolicy {
+public final class UploadPolicy : Sendable {
private static let DEFAULT_MAX_ERROR_RETRIES = 5
private static let DEFAULT_BACKOFF_MILLIS = 1000
private static let DEFAULT_BACKOFF_POLICY = BackoffPolicy.LINEAR
@@ -26,12 +26,12 @@ public class UploadPolicy {
self.backoffPolicy = backoffPolicy
}
- public enum NetworkType {
+ public enum NetworkType : Sendable {
case ANY
case UNMETERED
}
- public enum BackoffPolicy {
+ public enum BackoffPolicy : Sendable {
case LINEAR
case EXPONENTIAL
}
diff --git a/ImageKit/ImageKit.swift b/Sources/ImageKit/ImageKit.swift
similarity index 95%
rename from ImageKit/ImageKit.swift
rename to Sources/ImageKit/ImageKit.swift
index 5f9bfb1..6d76d4e 100644
--- a/ImageKit/ImageKit.swift
+++ b/Sources/ImageKit/ImageKit.swift
@@ -6,7 +6,6 @@
//
import Foundation
-public var TESTING: Bool = true
public struct UserDefaultKeys {
public static let KEY_CLIENT_PUBLIC_KEY = "IKClientKey"
@@ -14,7 +13,7 @@ public struct UserDefaultKeys {
public static let KEY_IMAGEKIT_TRANSFORMATION_POSITION = "IKTransformationPosition"
}
-open class ImageKit: NSObject {
+open class ImageKit: NSObject, @unchecked Sendable {
open fileprivate(set) var clientPublicKey: String! = ""
open fileprivate(set) var imageKitEndpoint: String! = ""
@@ -26,7 +25,8 @@ open class ImageKit: NSObject {
public static let shared = ImageKit()
- private let sharedUploader = ImageKitUploader()
+ @MainActor
+ private lazy var sharedUploader = ImageKitUploader()
public override init() {
@@ -43,11 +43,13 @@ open class ImageKit: NSObject {
}
@available(*, deprecated, message: "clientPublicKey Renamed to publicKey")
+ @MainActor
public convenience init(clientPublicKey: String = "", imageKitEndpoint: String, transformationPosition: TransformationPosition = TransformationPosition.PATH) {
self.init(publicKey: clientPublicKey, imageKitEndpoint: imageKitEndpoint, transformationPosition: transformationPosition)
}
@available(*, deprecated, message: "imageKitEndpoint Renamed to urlEndpoint")
+ @MainActor
public convenience init(publicKey: String = "", imageKitEndpoint: String, transformationPosition: TransformationPosition = TransformationPosition.PATH) {
self.init(publicKey: publicKey, urlEndpoint: imageKitEndpoint, transformationPosition: transformationPosition)
}
@@ -80,6 +82,7 @@ open class ImageKit: NSObject {
return ImagekitUrlConstructor(src: src, transformationPosition: transformationPosition)
}
+ @MainActor
public func uploader() -> ImageKitUploader {
return sharedUploader
}
diff --git a/ImageKit/ImageKitURLConstructor.swift b/Sources/ImageKit/ImageKitURLConstructor.swift
similarity index 99%
rename from ImageKit/ImageKitURLConstructor.swift
rename to Sources/ImageKit/ImageKitURLConstructor.swift
index 2971942..cf66f05 100644
--- a/ImageKit/ImageKitURLConstructor.swift
+++ b/Sources/ImageKit/ImageKitURLConstructor.swift
@@ -5,6 +5,7 @@
// Created by Abhinav Dhiman on 16/07/20.
//
import Foundation
+import UIKit
public class ImagekitUrlConstructor {
@@ -528,6 +529,7 @@ public class ImagekitUrlConstructor {
* @param focus Possible values include the values defined in enum FocusType.
* @return the current ImagekitUrlConstructor object.
*/
+ @MainActor
public func setResponsive(
view: UIView,
minSize: Int = 0,
diff --git a/ImageKit/ImageKitUploader.swift b/Sources/ImageKit/ImageKitUploader.swift
similarity index 91%
rename from ImageKit/ImageKitUploader.swift
rename to Sources/ImageKit/ImageKitUploader.swift
index 91ef6bf..c52028a 100644
--- a/ImageKit/ImageKitUploader.swift
+++ b/Sources/ImageKit/ImageKitUploader.swift
@@ -7,11 +7,13 @@
import Foundation
import Network
+import UIKit
-public class ImageKitUploader {
+public class ImageKitUploader : @unchecked Sendable {
var currentNetworkPath: NWPath
+ @MainActor
public init() {
let monitor = NWPathMonitor()
currentNetworkPath = monitor.currentPath
@@ -22,6 +24,7 @@ public class ImageKitUploader {
UIDevice.current.isBatteryMonitoringEnabled = true
}
+ @MainActor
public func upload(
file: Data,
token: String,
@@ -39,17 +42,19 @@ public class ImageKitUploader {
overwriteTags: Bool? = nil,
overwriteCustomMetadata: Bool? = nil,
customMetadata: [String : Any]? = nil,
- progress: ((Progress) -> Void)? = nil,
+ progress: (@Sendable (Progress) -> Void)? = nil,
urlConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
policy: UploadPolicy = ImageKit.shared.defaultUploadPolicy,
preprocessor: UploadPreprocessor? = nil,
- completion: @escaping (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
+ completion: @escaping @Sendable (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
if checkUploadPolicy(policy, completion) {
DispatchQueue.global(qos: .default).async {
var fileData = file
if let imageProcessor = preprocessor as? ImageUploadPreprocessor {
fileData = imageProcessor.outputFile(input: file, fileName: fileName)
} else if let videoProcessor = preprocessor as? VideoUploadPreprocessor {
+ let ext = extensions
+ let metadata = customMetadata
videoProcessor.listener = { data in
UploadAPI.upload(
file: data,
@@ -61,13 +66,13 @@ public class ImageKitUploader {
isPrivateFile: isPrivateFile,
customCoordinates: customCoordinates,
responseFields: responseFields,
- extensions: extensions,
+ extensions: ext,
webhookUrl: webhookUrl,
overwriteFile: overwriteFile,
overwriteAITags: overwriteAITags,
overwriteTags: overwriteTags,
overwriteCustomMetadata: overwriteCustomMetadata,
- customMetadata: customMetadata,
+ customMetadata: metadata,
progressClosure: progress,
urlConfiguration: urlConfiguration,
uploadPolicy: policy,
@@ -107,6 +112,7 @@ public class ImageKitUploader {
}
}
+ @MainActor
public func upload(
file: UIImage,
token: String,
@@ -124,14 +130,14 @@ public class ImageKitUploader {
overwriteTags: Bool? = nil,
overwriteCustomMetadata: Bool? = nil,
customMetadata: [String : Any]? = nil,
- progress: ((Progress) -> Void)? = nil,
+ progress: (@Sendable (Progress) -> Void)? = nil,
urlConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
policy: UploadPolicy = ImageKit.shared.defaultUploadPolicy,
preprocessor: ImageUploadPreprocessor? = nil,
- completion: @escaping (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
+ completion: @escaping @Sendable (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
if checkUploadPolicy(policy, completion) {
DispatchQueue.global(qos: .default).async {
- let image = preprocessor != nil ? preprocessor!.outputFile(input: file, fileName: fileName) : UIImagePNGRepresentation(file)!
+ let image = preprocessor != nil ? preprocessor!.outputFile(input: file, fileName: fileName) : file.pngData()!
UploadAPI.upload(
file: image,
token: token,
@@ -177,10 +183,10 @@ public class ImageKitUploader {
overwriteTags: Bool? = nil,
overwriteCustomMetadata: Bool? = nil,
customMetadata: [String : Any]? = nil,
- progress: ((Progress) -> Void)? = nil,
+ progress: (@Sendable (Progress) -> Void)? = nil,
urlConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
policy: UploadPolicy = ImageKit.shared.defaultUploadPolicy,
- completion: @escaping (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
+ completion: @escaping @Sendable (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) {
UploadAPI.upload(
file: file.data(using: .utf8)!,
token: token,
@@ -207,6 +213,7 @@ public class ImageKitUploader {
)
}
+ @MainActor
internal func checkUploadPolicy(_ policy: UploadPolicy, _ completion: @escaping (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void) -> Bool {
if policy.networkType == .UNMETERED && currentNetworkPath.isExpensive {
completion(Result.failure(UploadAPIError(message: "POLICY_ERROR_METERED_NETWORK", help: nil)))
diff --git a/ImageKit/Preprocess/ImageCrop.swift b/Sources/ImageKit/Preprocess/ImageCrop.swift
similarity index 98%
rename from ImageKit/Preprocess/ImageCrop.swift
rename to Sources/ImageKit/Preprocess/ImageCrop.swift
index 672e234..9aefe46 100644
--- a/ImageKit/Preprocess/ImageCrop.swift
+++ b/Sources/ImageKit/Preprocess/ImageCrop.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import UIKit
internal class ImageCrop : Preprocess {
typealias T = UIImage
diff --git a/ImageKit/Preprocess/ImageDimensionsLimiter.swift b/Sources/ImageKit/Preprocess/ImageDimensionsLimiter.swift
similarity index 98%
rename from ImageKit/Preprocess/ImageDimensionsLimiter.swift
rename to Sources/ImageKit/Preprocess/ImageDimensionsLimiter.swift
index 96d528c..dd07b8b 100644
--- a/ImageKit/Preprocess/ImageDimensionsLimiter.swift
+++ b/Sources/ImageKit/Preprocess/ImageDimensionsLimiter.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import UIKit
internal class ImageDimensionsLimiter : Preprocess {
typealias T = UIImage
diff --git a/ImageKit/Preprocess/ImageRotation.swift b/Sources/ImageKit/Preprocess/ImageRotation.swift
similarity index 98%
rename from ImageKit/Preprocess/ImageRotation.swift
rename to Sources/ImageKit/Preprocess/ImageRotation.swift
index aa6b1ee..b0092e3 100644
--- a/ImageKit/Preprocess/ImageRotation.swift
+++ b/Sources/ImageKit/Preprocess/ImageRotation.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import UIKit
internal class ImageRotation : Preprocess {
typealias T = UIImage
diff --git a/ImageKit/Preprocess/ImageUploadPreprocessor.swift b/Sources/ImageKit/Preprocess/ImageUploadPreprocessor.swift
similarity index 95%
rename from ImageKit/Preprocess/ImageUploadPreprocessor.swift
rename to Sources/ImageKit/Preprocess/ImageUploadPreprocessor.swift
index 805341e..19bf697 100644
--- a/ImageKit/Preprocess/ImageUploadPreprocessor.swift
+++ b/Sources/ImageKit/Preprocess/ImageUploadPreprocessor.swift
@@ -6,6 +6,7 @@
//
import Foundation
+import UIKit
public final class ImageUploadPreprocessor : UploadPreprocessor {
private let limit: ImageDimensionsLimiter
@@ -30,7 +31,7 @@ public final class ImageUploadPreprocessor : UploadPreprocessor {
image = limit.process(source: image)
image = cropPoints?.process(source: image) ?? image
image = rotation.process(source: image)
- return format == .JPEG ? UIImageJPEGRepresentation(image, 1.0)! : UIImagePNGRepresentation(image)!
+ return format == .JPEG ? image.jpegData(compressionQuality: 1.0)! : image.pngData()!
}
public enum OutputFormat {
diff --git a/ImageKit/Preprocess/Preprocess.swift b/Sources/ImageKit/Preprocess/Preprocess.swift
similarity index 100%
rename from ImageKit/Preprocess/Preprocess.swift
rename to Sources/ImageKit/Preprocess/Preprocess.swift
diff --git a/ImageKit/Preprocess/UploadPreprocessor.swift b/Sources/ImageKit/Preprocess/UploadPreprocessor.swift
similarity index 100%
rename from ImageKit/Preprocess/UploadPreprocessor.swift
rename to Sources/ImageKit/Preprocess/UploadPreprocessor.swift
diff --git a/ImageKit/Preprocess/VideoUploadPreprocessor.swift b/Sources/ImageKit/Preprocess/VideoUploadPreprocessor.swift
similarity index 79%
rename from ImageKit/Preprocess/VideoUploadPreprocessor.swift
rename to Sources/ImageKit/Preprocess/VideoUploadPreprocessor.swift
index 871d9a9..9395de3 100644
--- a/ImageKit/Preprocess/VideoUploadPreprocessor.swift
+++ b/Sources/ImageKit/Preprocess/VideoUploadPreprocessor.swift
@@ -6,7 +6,8 @@
//
import Foundation
-import AVFoundation
+@preconcurrency import AVFoundation
+import AudioToolbox
public final class VideoUploadPreprocessor : UploadPreprocessor {
@@ -43,23 +44,17 @@ public final class VideoUploadPreprocessor : UploadPreprocessor {
let videoTrack = asset.tracks(withMediaType: .video).first!
let audioTrack = asset.tracks(withMediaType: .audio).first
let dimensions = videoTrack.naturalSize.applying(videoTrack.preferredTransform)
- let size = CGSize(width: fabs(dimensions.width), height: fabs(dimensions.height))
- print("dimensions: width: \(fabs(dimensions.width)), height: \(fabs(dimensions.height))")
+ let size = CGSize(width: abs(dimensions.width), height: abs(dimensions.height))
+ print("dimensions: width: \(abs(dimensions.width)), height: \(abs(dimensions.height))")
print("fps: \(videoTrack.minFrameDuration.seconds)")
-
- var audioWriteFinished = false
- var videoWriteFinished = false
-
let reader = try! AVAssetReader(asset: asset)
let assetReaderVideoTrackOutput = AVAssetReaderTrackOutput(track: videoTrack, outputSettings: [kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32ARGB])
var assetReaderAudioTrackOutput: AVAssetReaderTrackOutput?
var audioWriterInput: AVAssetWriterInput?
if audioTrack != nil {
- assetReaderAudioTrackOutput = AVAssetReaderTrackOutput(track: audioTrack!, outputSettings: nil)
+ assetReaderAudioTrackOutput = AVAssetReaderTrackOutput(track: audioTrack!, outputSettings: [AVFormatIDKey: kAudioFormatLinearPCM])
reader.add(assetReaderAudioTrackOutput!)
- } else {
- audioWriteFinished = true
}
reader.add(assetReaderVideoTrackOutput)
@@ -89,52 +84,42 @@ public final class VideoUploadPreprocessor : UploadPreprocessor {
outputSettings: [
AVFormatIDKey: pointee.mFormatID,
AVEncoderBitRateKey: targetAudioBitrate,
- AVSampleRateKey: pointee.mSampleRate
+ AVSampleRateKey: pointee.mSampleRate,
+ AVNumberOfChannelsKey: pointee.mChannelsPerFrame
]
)
writer.add(audioWriterInput!)
}
writer.startWriting()
reader.startReading()
- writer.startSession(atSourceTime: kCMTimeZero)
+ writer.startSession(atSourceTime: CMTime.zero)
videoWriterInput.requestMediaDataWhenReady(on: videoDispatchQueue, using: {
while videoWriterInput.isReadyForMoreMediaData {
guard let sample = assetReaderVideoTrackOutput.copyNextSampleBuffer() else {
guard writer.inputs.contains(videoWriterInput) == true else { return }
videoWriterInput.markAsFinished()
- videoWriteFinished = true
- if videoWriteFinished && audioWriteFinished {
- writer.finishWriting(completionHandler: {
- reader.cancelReading()
- self.completionListener(try! Data(contentsOf: processedVideoUrl))
- })
- }
break
}
videoWriterInput.append(sample)
}
- })
-
- if audioTrack != nil {
- audioWriterInput?.requestMediaDataWhenReady(on: audioDispatchQueue, using: {
- while audioWriterInput!.isReadyForMoreMediaData {
- guard let sample = assetReaderAudioTrackOutput?.copyNextSampleBuffer() else {
- guard writer.inputs.contains(audioWriterInput!) == true else { return }
- audioWriterInput!.markAsFinished()
- audioWriteFinished = true
- if videoWriteFinished && audioWriteFinished {
+ if audioTrack != nil {
+ audioWriterInput?.requestMediaDataWhenReady(on: audioDispatchQueue, using: {
+ while audioWriterInput!.isReadyForMoreMediaData {
+ guard let sample = assetReaderAudioTrackOutput?.copyNextSampleBuffer() else {
+ guard writer.inputs.contains(audioWriterInput!) == true else { return }
+ audioWriterInput!.markAsFinished()
writer.finishWriting(completionHandler: {
reader.cancelReading()
self.completionListener(try! Data(contentsOf: processedVideoUrl))
})
+ break
}
- break
+ audioWriterInput!.append(sample)
}
- audioWriterInput!.append(sample)
- }
- })
- }
+ })
+ }
+ })
return Data()
}
diff --git a/ImageKit/Util/MimeDetector.swift b/Sources/ImageKit/Util/MimeDetector.swift
similarity index 100%
rename from ImageKit/Util/MimeDetector.swift
rename to Sources/ImageKit/Util/MimeDetector.swift
diff --git a/ImageKit/Util/MimeType.swift b/Sources/ImageKit/Util/MimeType.swift
similarity index 99%
rename from ImageKit/Util/MimeType.swift
rename to Sources/ImageKit/Util/MimeType.swift
index d7b9eac..2fde2c8 100644
--- a/ImageKit/Util/MimeType.swift
+++ b/Sources/ImageKit/Util/MimeType.swift
@@ -7,7 +7,7 @@
import Foundation
-public enum FileType {
+public enum FileType : Sendable{
case amr
case ar
case avi
@@ -69,7 +69,7 @@ public enum FileType {
case zip
}
-public struct MimeType {
+public struct MimeType : Sendable {
/// Mime type string representation. For example "application/pdf"
public let mime: String
@@ -84,7 +84,7 @@ public struct MimeType {
fileprivate let bytesCount: Int
/// A function to check if the bytes match the `MimeType` specifications.
- fileprivate let matches: ([UInt8], MimeDetector) -> Bool
+ fileprivate let matches: @Sendable ([UInt8], MimeDetector) -> Bool
/// Check if the given bytes matches with `MimeType`
/// it will check for the `bytes.count` first before delegating the
diff --git a/ImageKit/Util/UploadApi.swift b/Sources/ImageKit/Util/UploadApi.swift
similarity index 77%
rename from ImageKit/Util/UploadApi.swift
rename to Sources/ImageKit/Util/UploadApi.swift
index 7d0a2cc..ac74e18 100644
--- a/ImageKit/Util/UploadApi.swift
+++ b/Sources/ImageKit/Util/UploadApi.swift
@@ -8,12 +8,20 @@
import Foundation
import OSLog
-class UploadAPI: NSObject, URLSessionTaskDelegate {
- internal static var baseUrl = "https://upload.imagekit.io"
+fileprivate class SendableAny : @unchecked Sendable {
+ let value: Any
+
+ init(value: Any) {
+ self.value = value
+ }
+}
+
+class UploadAPI: NSObject, URLSessionTaskDelegate, @unchecked Sendable {
+ nonisolated(unsafe) internal static var baseUrl = "https://upload.imagekit.io"
internal static let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "ImageKitIO")
internal static func upload(
- file: Any,
+ file: Sendable,
token: String,
fileName: String,
useUniqueFileName: Bool? = nil,
@@ -29,10 +37,58 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
overwriteTags: Bool? = nil,
overwriteCustomMetadata: Bool? = nil,
customMetadata: [String : Any]? = nil,
- progressClosure: ((Progress) -> Void)? = nil,
+ progressClosure: (@Sendable (Progress) -> Void)? = nil,
+ urlConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
+ uploadPolicy: UploadPolicy,
+ completion: @escaping @Sendable (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void,
+ retryCount: Int = 0
+ ) {
+ uploadWithSendableValues(
+ file: file,
+ token: token,
+ fileName: fileName,
+ useUniqueFileName: useUniqueFileName,
+ tags: tags,
+ folder: folder,
+ isPrivateFile: isPrivateFile,
+ customCoordinates: customCoordinates,
+ responseFields: responseFields,
+ extensions: extensions?.map { $0.mapValues { SendableAny(value: $0) } },
+ webhookUrl: webhookUrl,
+ overwriteFile: overwriteFile,
+ overwriteAITags: overwriteAITags,
+ overwriteTags: overwriteTags,
+ overwriteCustomMetadata: overwriteCustomMetadata,
+ customMetadata: customMetadata?.mapValues { SendableAny(value: $0) },
+ progressClosure: progressClosure,
+ urlConfiguration: urlConfiguration,
+ uploadPolicy: uploadPolicy,
+ completion: completion,
+ retryCount: retryCount + 1
+ )
+ }
+
+ private static func uploadWithSendableValues(
+ file: Sendable,
+ token: String,
+ fileName: String,
+ useUniqueFileName: Bool? = nil,
+ tags: String? = nil,
+ folder: String? = nil,
+ isPrivateFile: Bool?,
+ customCoordinates: String? = nil,
+ responseFields: String? = nil,
+ extensions: [[String : SendableAny]]? = nil,
+ webhookUrl: String? = nil,
+ overwriteFile: Bool? = nil,
+ overwriteAITags: Bool? = nil,
+ overwriteTags: Bool? = nil,
+ overwriteCustomMetadata: Bool? = nil,
+ customMetadata: [String : SendableAny]? = nil,
+ progressClosure: (@Sendable (Progress) -> Void)? = nil,
urlConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
uploadPolicy: UploadPolicy,
- completion: @escaping (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void,
+ completion: @escaping @Sendable (Result<(HTTPURLResponse?, UploadAPIResponse?), Error>) -> Void,
retryCount: Int = 0
) {
var request = URLRequest(url: URL(string: "\(baseUrl)/api/v2/files/upload")!)
@@ -50,10 +106,12 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
var extData: Data? = nil
var metaData: Data? = nil
if let extensions = extensions {
- extData = try? JSONSerialization.data(withJSONObject: extensions)
+ let extsObject = extensions.map { $0.mapValues { $0.value } }
+ extData = try? JSONSerialization.data(withJSONObject: extsObject)
}
if let customMetadata = customMetadata {
- metaData = try? JSONSerialization.data(withJSONObject: customMetadata)
+ let metaObject = customMetadata.mapValues { $0.value }
+ metaData = try? JSONSerialization.data(withJSONObject: metaObject)
}
formData.append(fileData, withName: "file", fileName: fileName, mimeType: file is Data ? mimeType! : "text/plain")
formData.append(token.data(using: String.Encoding.utf8)!, withName: "token")
@@ -112,6 +170,15 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
tags: tags,
folder: folder,
isPrivateFile: isPrivateFile,
+ customCoordinates: customCoordinates,
+ responseFields: responseFields,
+ extensions: extensions,
+ webhookUrl: webhookUrl,
+ overwriteFile: overwriteFile,
+ overwriteAITags: overwriteAITags,
+ overwriteTags: overwriteTags,
+ overwriteCustomMetadata: overwriteCustomMetadata,
+ customMetadata: customMetadata,
progressClosure: progressClosure,
urlConfiguration: urlConfiguration,
uploadPolicy: uploadPolicy,
@@ -140,6 +207,15 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
tags: tags,
folder: folder,
isPrivateFile: isPrivateFile,
+ customCoordinates: customCoordinates,
+ responseFields: responseFields,
+ extensions: extensions,
+ webhookUrl: webhookUrl,
+ overwriteFile: overwriteFile,
+ overwriteAITags: overwriteAITags,
+ overwriteTags: overwriteTags,
+ overwriteCustomMetadata: overwriteCustomMetadata,
+ customMetadata: customMetadata,
progressClosure: progressClosure,
urlConfiguration: urlConfiguration,
uploadPolicy: uploadPolicy,
@@ -164,6 +240,15 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
tags: tags,
folder: folder,
isPrivateFile: isPrivateFile,
+ customCoordinates: customCoordinates,
+ responseFields: responseFields,
+ extensions: extensions,
+ webhookUrl: webhookUrl,
+ overwriteFile: overwriteFile,
+ overwriteAITags: overwriteAITags,
+ overwriteTags: overwriteTags,
+ overwriteCustomMetadata: overwriteCustomMetadata,
+ customMetadata: customMetadata,
progressClosure: progressClosure,
urlConfiguration: urlConfiguration,
uploadPolicy: uploadPolicy,
@@ -332,7 +417,7 @@ class UploadAPI: NSObject, URLSessionTaskDelegate {
}
}
-class UploadTaskDelegate: NSObject, URLSessionDataDelegate {
+class UploadTaskDelegate: NSObject, URLSessionDataDelegate, @unchecked Sendable {
var uploadProgress: Progress
var uploadProgressHandler: ((Progress) -> Void)?
diff --git a/Tests/Tests.swift b/Tests/Tests.swift
index d8a8f0b..b68b9ad 100644
--- a/Tests/Tests.swift
+++ b/Tests/Tests.swift
@@ -1,4 +1,6 @@
import Quick
+import UIKit
+import Foundation
import Nimble
@testable import ImageKitIO
import Mocker
@@ -480,7 +482,7 @@ class UnitTestSpec: QuickSpec {
it("Background: UIColor.black") {
let actual = ImageKit.shared
.url(path: "medium_cafe_B1iTdD0C.jpg")
- .background(backgroundColor: UIColor.black)
+ .background(backgroundColor: "000000")
.create()
expect(actual).to(equal(String(format: "https://ik.imagekit.io/demo/tr:bg-000000/medium_cafe_B1iTdD0C.jpg")))
}
@@ -495,7 +497,7 @@ class UnitTestSpec: QuickSpec {
it("Border: 5, UIColor.blue") {
let actual = ImageKit.shared
.url(path: "medium_cafe_B1iTdD0C.jpg")
- .border(borderWidth: 5, borderColor: UIColor.blue)
+ .border(borderWidth: 5, borderColor: "0000FF")
.create()
expect(actual).to(equal(String(format: "https://ik.imagekit.io/demo/tr:b-5_0000FF/medium_cafe_B1iTdD0C.jpg")))
}
@@ -639,14 +641,18 @@ class UnitTestSpec: QuickSpec {
describe("Responsive URL loading for UIViews") {
it("With default params") {
- let view = UIView()
- let dpr = String(format: "%.2f", Float(UIScreen.main.scale.rounded(.toNearestOrAwayFromZero)))
- view.frame = CGRect(x: 0, y: 0, width: 400, height: 300)
- let actual = try! ImageKit.shared
- .url(src: "https://ik.imagekit.io/demo/medium_cafe_B1iTdD0C.jpg")
- .setResponsive(view: view)
- .create()
- expect(actual).to(equal(String(format: "https://ik.imagekit.io/demo/medium_cafe_B1iTdD0C.jpg?tr=w-400,h-300,dpr-\(dpr),cm-resize,fo-center")))
+ Task.detached(operation: {
+ await MainActor.run(body: {
+ let view = UIView()
+ let dpr = String(format: "%.2f", Float(UIScreen.main.scale.rounded(.toNearestOrAwayFromZero)))
+ view.frame = CGRect(x: 0, y: 0, width: 400, height: 300)
+ let actual = try! ImageKit.shared
+ .url(src: "https://ik.imagekit.io/demo/medium_cafe_B1iTdD0C.jpg")
+ .setResponsive(view: view)
+ .create()
+ expect(actual).to(equal(String(format: "https://ik.imagekit.io/demo/medium_cafe_B1iTdD0C.jpg?tr=w-400,h-300,dpr-\(dpr),cm-resize,fo-center")))
+ })
+ })
}
}
}
@@ -745,7 +751,7 @@ class MimeDetectorSpec: QuickSpec {
for ext in extensions {
context("when extension is \(ext)") {
it("shoud guess the correct mime type") {
- let data = loadFileData(path: "/Tests/fixtures/fixture.\(ext)")
+ let data = self.loadFileData(path: "/fixture.\(ext)")
let mimeType = MimeDetector.mimeType(data: data)
if let mime = mimeTypeByExtension[ext] {
@@ -781,7 +787,7 @@ class MimeDetectorSpec: QuickSpec {
describe("MimeDetector.mimeType(bytes:).type") {
context("when file type is image/jpeg") {
it("should return true") {
- let data: Data = loadFileData(path: "/Tests/fixtures/fixture.jpg")
+ let data: Data = self.loadFileData(path: "/fixture.jpg")
let mimeType = MimeDetector.mimeType(data: data)
expect(mimeType?.type) == .jpg
@@ -790,7 +796,7 @@ class MimeDetectorSpec: QuickSpec {
context("when file type is application/pdf") {
it("should return true") {
- let data: Data = loadFileData(path: "/Tests/fixtures/fixture.pdf")
+ let data: Data = self.loadFileData(path: "/fixture.pdf")
let mimeType = MimeDetector.mimeType(data: data)
expect(mimeType?.type) == .pdf
@@ -799,7 +805,7 @@ class MimeDetectorSpec: QuickSpec {
context("when file type is not image/jpeg") {
it("should return true") {
- let data: Data = loadFileData(path: "/Tests/fixtures/fixture.png")
+ let data: Data = self.loadFileData(path: "/fixture.png")
let mimeType = MimeDetector.mimeType(data: data)
expect(mimeType?.type) != .jpg
@@ -807,15 +813,15 @@ class MimeDetectorSpec: QuickSpec {
}
}
}
-}
-
-func loadFileData(path: String) -> Data {
- let projectDir = URL(fileURLWithPath: #file).pathComponents.prefix(while: { $0 != "Tests" }).joined(separator: "/").dropFirst()
- print(projectDir)
- let absolutePath = "\(projectDir)\(path)"
- print(absolutePath)
- let url = URL(fileURLWithPath: absolutePath, isDirectory: false)
- return try! Data(contentsOf: url)
+
+ func loadFileData(path: String) -> Data {
+ let projectDir = Bundle(for: MimeDetectorSpec.self).resourceURL?.appendingPathComponent("ImageKitIO_ImageKitIO-Tests.bundle").path ?? ""
+ print(projectDir)
+ let absolutePath = "\(projectDir)\(path)"
+ print(absolutePath)
+ let url = URL(fileURLWithPath: absolutePath, isDirectory: false)
+ return try! Data(contentsOf: url)
+ }
}
class UploadSpec: QuickSpec {
@@ -836,22 +842,30 @@ class UploadSpec: QuickSpec {
let sampleMetadata = ["device_name": "Emulator", "uid": 167434]
it("Upload From Url") {
Mock(url: URL(string: "https://upload.imagekit.io/api/v2/files/upload")!, dataType: .json, statusCode: 200, data: [
- .post : Data("""
- {
- "fileId": "5f881125ce8f14336dda25b6",
- "name": "default-image-test_1JO5mllWR.jpg",
- "size": 146974,
- "filePath": "/default-image-test_1JO5mllWR.jpg",
- "url": "https://ik.imagekit.io/demo/default-image-test_1JO5mllWR.jpg",
- "fileType": "image",
- "height": 1000,
- "width": 1000,
- "thumbnailUrl": "https://ik.imagekit.io/demo/tr:n-media_library_thumbnail/default-image-test_1JO5mllWR.jpg"
- }
- """.utf8)
- ]).register()
+ .post : Data("""
+ {
+ "fileId": "5f881125ce8f14336dda25b6",
+ "name": "default-image-test_1JO5mllWR.jpg",
+ "size": 146974,
+ "filePath": "/default-image-test_1JO5mllWR.jpg",
+ "url": "https://ik.imagekit.io/demo/default-image-test_1JO5mllWR.jpg",
+ "fileType": "image",
+ "height": 1000,
+ "width": 1000,
+ "thumbnailUrl": "https://ik.imagekit.io/demo/tr:n-media_library_thumbnail/default-image-test_1JO5mllWR.jpg"
+ }
+ """.utf8)
+ ]).register()
+
+ let urlConfiguration = self.urlConfiguration
- waitUntil(timeout: DispatchTimeInterval.seconds(60)){ done in
+ Task { @MainActor in
+
+ let sampleExtensions = [
+ ["name" : "remove-bg", "options" : ["add_shadow" : true]],
+ ["name": "google-auto-tagging", "minConfidence": 80, "maxTags": 5]
+ ]
+ let sampleMetadata = ["device_name": "Emulator", "uid": 167434]
ImageKit.shared.uploader().upload(
file: "https://ik.imagekit.io/demo/default-image.jpg",
token: "test1",
@@ -867,7 +881,7 @@ class UploadSpec: QuickSpec {
overwriteTags: true,
overwriteCustomMetadata: true,
customMetadata: sampleMetadata,
- urlConfiguration: self.urlConfiguration,
+ urlConfiguration: urlConfiguration,
completion: { result in
switch result{
case .success((_, let uploadAPIResponse)):
@@ -882,20 +896,16 @@ class UploadSpec: QuickSpec {
expect(uploadAPIResponse.width).to(equal(1000))
expect(uploadAPIResponse.thumbnailUrl).to(equal("https://ik.imagekit.io/demo/tr:n-media_library_thumbnail/default-image-test_1JO5mllWR.jpg"))
}
- break;
- case .failure( _ as UploadAPIError):
- fail("Should not throw Error")
- break;
- case .failure( _):
+ case .failure(_):
fail("Should not throw Error")
break;
}
- done()
})
}
}
it("Upload UIImage") {
+
Mock(url: URL(string: "https://upload.imagekit.io/api/v2/files/upload")!, dataType: .json, statusCode: 200, data: [
.post : Data("""
{
@@ -913,7 +923,13 @@ class UploadSpec: QuickSpec {
]).register()
let image = getImageWithColor(color: .red, size: .init(width: 200, height: 200))
- waitUntil(timeout: DispatchTimeInterval.seconds(60)){ done in
+ let urlConfiguration = self.urlConfiguration
+ Task { @MainActor in
+ let sampleExtensions = [
+ ["name" : "remove-bg", "options" : ["add_shadow" : true]],
+ ["name": "google-auto-tagging", "minConfidence": 80, "maxTags": 5]
+ ]
+ let sampleMetadata = ["device_name": "Emulator", "uid": 167434]
ImageKit.shared.uploader().upload(
file: image,
token: "test2",
@@ -929,7 +945,7 @@ class UploadSpec: QuickSpec {
overwriteTags: true,
overwriteCustomMetadata: true,
customMetadata: sampleMetadata,
- urlConfiguration: self.urlConfiguration,
+ urlConfiguration: urlConfiguration,
completion: { result in
switch result{
case .success((_, let uploadAPIResponse)):
@@ -952,8 +968,7 @@ class UploadSpec: QuickSpec {
fail("Should not throw Error")
break;
}
- done()
- })
+ })
}
}
it("Upload Data"){
@@ -974,9 +989,15 @@ class UploadSpec: QuickSpec {
]).register()
let image = getImageWithColor(color: .red, size: .init(width: 200, height: 200))
- waitUntil(timeout: DispatchTimeInterval.seconds(60)){ done in
+ let urlConfiguration = self.urlConfiguration
+ Task { @MainActor in
+ let sampleExtensions = [
+ ["name" : "remove-bg", "options" : ["add_shadow" : true]],
+ ["name": "google-auto-tagging", "minConfidence": 80, "maxTags": 5]
+ ]
+ let sampleMetadata = ["device_name": "Emulator", "uid": 167434]
ImageKit.shared.uploader().upload(
- file: UIImagePNGRepresentation(image)!,
+ file: image.pngData()!,
token: "test3",
fileName: "default-image-test.jpg",
tags: ["test", "image",],
@@ -990,7 +1011,7 @@ class UploadSpec: QuickSpec {
overwriteTags: true,
overwriteCustomMetadata: true,
customMetadata: sampleMetadata,
- urlConfiguration: self.urlConfiguration,
+ urlConfiguration: urlConfiguration,
completion: { result in
switch result{
case .success((_, let uploadAPIResponse)):
@@ -1013,7 +1034,6 @@ class UploadSpec: QuickSpec {
fail("Should not throw Error")
break;
}
- done()
})
}
}
@@ -1144,3 +1164,4 @@ extension UIImage {
return UIColor(red: r, green: g, blue: b, alpha: a)
}
}
+