diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7dd4b89..a4e5040 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,29 +5,29 @@ on: [push] jobs: build-swift: name: Build with swift - runs-on: macos-12 + runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: xcode version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '13.4.1' + xcode-version: latest-stable - name: Build Package with swift run: swift build build-xcodebuild: name: Build with xcodebuild - runs-on: macos-12 + runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: xcode version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '13.4.1' + xcode-version: latest-stable - name: Set Default Scheme run: | scheme_list=$(xcodebuild -list -json | tr -d "\n") @@ -50,14 +50,14 @@ jobs: cocoapods: name: Verify cocopods podspec needs: [ build-xcodebuild ] - runs-on: macos-12 + runs-on: macos-latest steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v3 - name: xcode version uses: maxim-lobanov/setup-xcode@v1 with: - xcode-version: '13.4.1' + xcode-version: latest-stable - name: Verify cocoapods run: pod lib lint --allow-warnings diff --git a/.github/workflows/create-release-from-changelog.yml b/.github/workflows/create-release-from-changelog.yml index 7de02c0..a1ac0db 100644 --- a/.github/workflows/create-release-from-changelog.yml +++ b/.github/workflows/create-release-from-changelog.yml @@ -9,7 +9,7 @@ jobs: update-documentation: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Create draft release if needed uses: apivideo/api.video-release-from-changelog-action@main with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0f158ca..8305544 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,7 +6,7 @@ jobs: deploy: runs-on: macos-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install Cocoapods run: gem install cocoapods diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index cb48529..08d6e0d 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,27 +7,44 @@ objects = { /* Begin PBXBuildFile section */ + 212CE7122B03CABA001F70DD /* InAppSettingsKit in Frameworks */ = {isa = PBXBuildFile; productRef = 212CE7112B03CABA001F70DD /* InAppSettingsKit */; }; + 212CE7502B03CB21001F70DD /* Contents.json in Resources */ = {isa = PBXBuildFile; fileRef = 212CE7392B03CB20001F70DD /* Contents.json */; }; + 212CE76A2B03CB5B001F70DD /* AsyncApiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7662B03CB5A001F70DD /* AsyncApiUtils.swift */; }; + 212CE76B2B03CB5B001F70DD /* AlertUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7672B03CB5A001F70DD /* AlertUtils.swift */; }; + 212CE76C2B03CB5B001F70DD /* SettingsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7682B03CB5A001F70DD /* SettingsManager.swift */; }; + 212CE76D2B03CB5B001F70DD /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7692B03CB5A001F70DD /* UIColorExtensions.swift */; }; + 212CE7732B03CB6F001F70DD /* ClientAppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE76F2B03CB6E001F70DD /* ClientAppError.swift */; }; + 212CE7742B03CB6F001F70DD /* TaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7702B03CB6E001F70DD /* TaskManager.swift */; }; + 212CE7752B03CB6F001F70DD /* CancellableApiTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7712B03CB6E001F70DD /* CancellableApiTask.swift */; }; + 212CE7762B03CB6F001F70DD /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 212CE7722B03CB6E001F70DD /* Settings.bundle */; }; + 212CE7782B03CBCA001F70DD /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 212CE7772B03CBCA001F70DD /* MainViewController.swift */; }; 2136DD4A274B9E74007B9FC9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD49274B9E74007B9FC9 /* AppDelegate.swift */; }; 2136DD4C274B9E74007B9FC9 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD4B274B9E74007B9FC9 /* SceneDelegate.swift */; }; - 2136DD4E274B9E74007B9FC9 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD4D274B9E74007B9FC9 /* ViewController.swift */; }; 2136DD51274B9E74007B9FC9 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2136DD4F274B9E74007B9FC9 /* Main.storyboard */; }; 2136DD53274B9E77007B9FC9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2136DD52274B9E77007B9FC9 /* Assets.xcassets */; }; 2136DD56274B9E77007B9FC9 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2136DD54274B9E77007B9FC9 /* LaunchScreen.storyboard */; }; 21A2E574275E5D4A00ED880F /* ApiVideoUploader in Frameworks */ = {isa = PBXBuildFile; productRef = 21A2E573275E5D4A00ED880F /* ApiVideoUploader */; }; - C70E513C27A9719900B31115 /* UploaderManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = C70E513B27A9719900B31115 /* UploaderManager.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 212CE7392B03CB20001F70DD /* Contents.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Contents.json; path = Assets.xcassets/Contents.json; sourceTree = ""; }; + 212CE7662B03CB5A001F70DD /* AsyncApiUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncApiUtils.swift; sourceTree = ""; }; + 212CE7672B03CB5A001F70DD /* AlertUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertUtils.swift; sourceTree = ""; }; + 212CE7682B03CB5A001F70DD /* SettingsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsManager.swift; sourceTree = ""; }; + 212CE7692B03CB5A001F70DD /* UIColorExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = ""; }; + 212CE76F2B03CB6E001F70DD /* ClientAppError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientAppError.swift; sourceTree = ""; }; + 212CE7702B03CB6E001F70DD /* TaskManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskManager.swift; sourceTree = ""; }; + 212CE7712B03CB6E001F70DD /* CancellableApiTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CancellableApiTask.swift; sourceTree = ""; }; + 212CE7722B03CB6E001F70DD /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + 212CE7772B03CBCA001F70DD /* MainViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 2136DD46274B9E74007B9FC9 /* Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 2136DD49274B9E74007B9FC9 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 2136DD4B274B9E74007B9FC9 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; - 2136DD4D274B9E74007B9FC9 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 2136DD50274B9E74007B9FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 2136DD52274B9E77007B9FC9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2136DD55274B9E77007B9FC9 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 2136DD57274B9E77007B9FC9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 21A2E572275E5D3300ED880F /* swift5-uploader */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "swift5-uploader"; path = ..; sourceTree = ""; }; - C70E513B27A9719900B31115 /* UploaderManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UploaderManager.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -36,12 +53,34 @@ buildActionMask = 2147483647; files = ( 21A2E574275E5D4A00ED880F /* ApiVideoUploader in Frameworks */, + 212CE7122B03CABA001F70DD /* InAppSettingsKit in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 212CE7652B03CB5A001F70DD /* Utils */ = { + isa = PBXGroup; + children = ( + 212CE7662B03CB5A001F70DD /* AsyncApiUtils.swift */, + 212CE7672B03CB5A001F70DD /* AlertUtils.swift */, + 212CE7682B03CB5A001F70DD /* SettingsManager.swift */, + 212CE7692B03CB5A001F70DD /* UIColorExtensions.swift */, + ); + path = Utils; + sourceTree = ""; + }; + 212CE76E2B03CB6E001F70DD /* Models */ = { + isa = PBXGroup; + children = ( + 212CE76F2B03CB6E001F70DD /* ClientAppError.swift */, + 212CE7702B03CB6E001F70DD /* TaskManager.swift */, + 212CE7712B03CB6E001F70DD /* CancellableApiTask.swift */, + ); + path = Models; + sourceTree = ""; + }; 2136DD3D274B9E74007B9FC9 = { isa = PBXGroup; children = ( @@ -63,14 +102,17 @@ 2136DD48274B9E74007B9FC9 /* Example */ = { isa = PBXGroup; children = ( + 212CE7772B03CBCA001F70DD /* MainViewController.swift */, + 212CE76E2B03CB6E001F70DD /* Models */, + 212CE7722B03CB6E001F70DD /* Settings.bundle */, + 212CE7652B03CB5A001F70DD /* Utils */, + 212CE7392B03CB20001F70DD /* Contents.json */, 2136DD49274B9E74007B9FC9 /* AppDelegate.swift */, 2136DD4B274B9E74007B9FC9 /* SceneDelegate.swift */, - 2136DD4D274B9E74007B9FC9 /* ViewController.swift */, 2136DD4F274B9E74007B9FC9 /* Main.storyboard */, 2136DD52274B9E77007B9FC9 /* Assets.xcassets */, 2136DD54274B9E77007B9FC9 /* LaunchScreen.storyboard */, 2136DD57274B9E77007B9FC9 /* Info.plist */, - C70E513B27A9719900B31115 /* UploaderManager.swift */, ); path = Example; sourceTree = ""; @@ -108,6 +150,7 @@ name = Example; packageProductDependencies = ( 21A2E573275E5D4A00ED880F /* ApiVideoUploader */, + 212CE7112B03CABA001F70DD /* InAppSettingsKit */, ); productName = Example; productReference = 2136DD46274B9E74007B9FC9 /* Example.app */; @@ -137,6 +180,9 @@ Base, ); mainGroup = 2136DD3D274B9E74007B9FC9; + packageReferences = ( + 212CE7102B03CABA001F70DD /* XCRemoteSwiftPackageReference "InAppSettingsKit" */, + ); productRefGroup = 2136DD47274B9E74007B9FC9 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -153,7 +199,9 @@ files = ( 2136DD56274B9E77007B9FC9 /* LaunchScreen.storyboard in Resources */, 2136DD53274B9E77007B9FC9 /* Assets.xcassets in Resources */, + 212CE7762B03CB6F001F70DD /* Settings.bundle in Resources */, 2136DD51274B9E74007B9FC9 /* Main.storyboard in Resources */, + 212CE7502B03CB21001F70DD /* Contents.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -164,10 +212,16 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2136DD4E274B9E74007B9FC9 /* ViewController.swift in Sources */, + 212CE76B2B03CB5B001F70DD /* AlertUtils.swift in Sources */, + 212CE76C2B03CB5B001F70DD /* SettingsManager.swift in Sources */, + 212CE7742B03CB6F001F70DD /* TaskManager.swift in Sources */, 2136DD4A274B9E74007B9FC9 /* AppDelegate.swift in Sources */, + 212CE76D2B03CB5B001F70DD /* UIColorExtensions.swift in Sources */, + 212CE76A2B03CB5B001F70DD /* AsyncApiUtils.swift in Sources */, + 212CE7732B03CB6F001F70DD /* ClientAppError.swift in Sources */, + 212CE7752B03CB6F001F70DD /* CancellableApiTask.swift in Sources */, 2136DD4C274B9E74007B9FC9 /* SceneDelegate.swift in Sources */, - C70E513C27A9719900B31115 /* UploaderManager.swift in Sources */, + 212CE7782B03CBCA001F70DD /* MainViewController.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -316,7 +370,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = VY3VXRC7P4; + DEVELOPMENT_TEAM = GBC36KP98K; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Example/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -344,7 +398,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = VY3VXRC7P4; + DEVELOPMENT_TEAM = GBC36KP98K; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = Example/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -388,7 +442,23 @@ }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 212CE7102B03CABA001F70DD /* XCRemoteSwiftPackageReference "InAppSettingsKit" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/futuretap/InAppSettingsKit.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.4.2; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + /* Begin XCSwiftPackageProductDependency section */ + 212CE7112B03CABA001F70DD /* InAppSettingsKit */ = { + isa = XCSwiftPackageProductDependency; + package = 212CE7102B03CABA001F70DD /* XCRemoteSwiftPackageReference "InAppSettingsKit" */; + productName = InAppSettingsKit; + }; 21A2E573275E5D4A00ED880F /* ApiVideoUploader */ = { isa = XCSwiftPackageProductDependency; productName = ApiVideoUploader; diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index 65f7c57..969e0eb 100644 --- a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -1,25 +1,32 @@ { - "object": { - "pins": [ - { - "package": "Alamofire", - "repositoryURL": "https://github.com/Alamofire/Alamofire", - "state": { - "branch": null, - "revision": "d120af1e8638c7da36c8481fd61a66c0c08dc4fc", - "version": "5.4.4" - } - }, - { - "package": "AnyCodable", - "repositoryURL": "https://github.com/Flight-School/AnyCodable", - "state": { - "branch": null, - "revision": "b1a7a8a6186f2fcb28f7bda67cfc545de48b3c80", - "version": "0.6.2" - } + "pins" : [ + { + "identity" : "alamofire", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Alamofire/Alamofire", + "state" : { + "revision" : "d120af1e8638c7da36c8481fd61a66c0c08dc4fc", + "version" : "5.4.4" } - ] - }, - "version": 1 + }, + { + "identity" : "anycodable", + "kind" : "remoteSourceControl", + "location" : "https://github.com/Flight-School/AnyCodable", + "state" : { + "revision" : "b1a7a8a6186f2fcb28f7bda67cfc545de48b3c80", + "version" : "0.6.2" + } + }, + { + "identity" : "inappsettingskit", + "kind" : "remoteSourceControl", + "location" : "https://github.com/futuretap/InAppSettingsKit.git", + "state" : { + "revision" : "08ab93cd15759eed534b821c2ea789d97a0fdca0", + "version" : "3.4.2" + } + } + ], + "version" : 2 } diff --git a/Example/Example/AppDelegate.swift b/Example/Example/AppDelegate.swift index 92ed19d..c0eb3b3 100644 --- a/Example/Example/AppDelegate.swift +++ b/Example/Example/AppDelegate.swift @@ -7,28 +7,22 @@ import UIKit @main class AppDelegate: UIResponder, UIApplicationDelegate { - - - - func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + func application(_: UIApplication, didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. return true } // MARK: UISceneSession Lifecycle - func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options _: UIScene.ConnectionOptions) -> UISceneConfiguration { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + func application(_: UIApplication, didDiscardSceneSessions _: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. // Use this method to release any resources that were specific to the discarded scenes, as they will not return. } - - } - diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/1024.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/1024.png new file mode 100644 index 0000000..3fff53b Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/1024.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/120 1.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/120 1.png new file mode 100644 index 0000000..49405f4 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/120 1.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/120 2.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/120 2.png new file mode 100644 index 0000000..49405f4 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/120 2.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/152.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/152.png new file mode 100644 index 0000000..28adb6b Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/152.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/167.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/167.png new file mode 100644 index 0000000..298d0c6 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/167.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/180.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/180.png new file mode 100644 index 0000000..ff08a37 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/180.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/20.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/20.png new file mode 100644 index 0000000..b88c87b Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/20.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/29.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/29.png new file mode 100644 index 0000000..fbd42b9 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/29.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/40 1.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/40 1.png new file mode 100644 index 0000000..af2c141 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/40 1.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/40 2.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/40 2.png new file mode 100644 index 0000000..af2c141 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/40 2.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/40.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/40.png new file mode 100644 index 0000000..af2c141 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/40.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/58 1.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/58 1.png new file mode 100644 index 0000000..110dac7 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/58 1.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/58.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/58.png new file mode 100644 index 0000000..110dac7 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/58.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/60.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/60.png new file mode 100644 index 0000000..683adad Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/60.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/76.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/76.png new file mode 100644 index 0000000..60f8825 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/76.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/80 1.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/80 1.png new file mode 100644 index 0000000..9dfb993 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/80 1.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/80.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/80.png new file mode 100644 index 0000000..9dfb993 Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/80.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/87.png b/Example/Example/Assets.xcassets/AppIcon.appiconset/87.png new file mode 100644 index 0000000..d564d5d Binary files /dev/null and b/Example/Example/Assets.xcassets/AppIcon.appiconset/87.png differ diff --git a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json index 9221b9b..83e2c62 100644 --- a/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json +++ b/Example/Example/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -1,91 +1,109 @@ { "images" : [ { + "filename" : "40.png", "idiom" : "iphone", "scale" : "2x", "size" : "20x20" }, { + "filename" : "60.png", "idiom" : "iphone", "scale" : "3x", "size" : "20x20" }, { + "filename" : "58 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "29x29" }, { + "filename" : "87.png", "idiom" : "iphone", "scale" : "3x", "size" : "29x29" }, { + "filename" : "80 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "40x40" }, { + "filename" : "120 2.png", "idiom" : "iphone", "scale" : "3x", "size" : "40x40" }, { + "filename" : "120 1.png", "idiom" : "iphone", "scale" : "2x", "size" : "60x60" }, { + "filename" : "180.png", "idiom" : "iphone", "scale" : "3x", "size" : "60x60" }, { + "filename" : "20.png", "idiom" : "ipad", "scale" : "1x", "size" : "20x20" }, { + "filename" : "40 1.png", "idiom" : "ipad", "scale" : "2x", "size" : "20x20" }, { + "filename" : "29.png", "idiom" : "ipad", "scale" : "1x", "size" : "29x29" }, { + "filename" : "58.png", "idiom" : "ipad", "scale" : "2x", "size" : "29x29" }, { + "filename" : "40 2.png", "idiom" : "ipad", "scale" : "1x", "size" : "40x40" }, { + "filename" : "80.png", "idiom" : "ipad", "scale" : "2x", "size" : "40x40" }, { + "filename" : "76.png", "idiom" : "ipad", "scale" : "1x", "size" : "76x76" }, { + "filename" : "152.png", "idiom" : "ipad", "scale" : "2x", "size" : "76x76" }, { + "filename" : "167.png", "idiom" : "ipad", "scale" : "2x", "size" : "83.5x83.5" }, { + "filename" : "1024.png", "idiom" : "ios-marketing", "scale" : "1x", "size" : "1024x1024" diff --git a/Example/Example/Base.lproj/Assets.xcassets/AccentColor.colorset/Contents.json b/Example/Example/Base.lproj/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/Example/Example/Base.lproj/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Base.lproj/Assets.xcassets/AppIcon.appiconset/Contents.json b/Example/Example/Base.lproj/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/Example/Example/Base.lproj/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Base.lproj/Assets.xcassets/Contents.json b/Example/Example/Base.lproj/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Example/Example/Base.lproj/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index 25a7638..0f181a5 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -1,24 +1,35 @@ - + + - + + + - - + + - - - + + + - - + + + - + + + + + + + + diff --git a/Example/Example/MainViewController.swift b/Example/Example/MainViewController.swift new file mode 100644 index 0000000..0852214 --- /dev/null +++ b/Example/Example/MainViewController.swift @@ -0,0 +1,162 @@ +// +// MainViewController.swift +// Example +// + +import ApiVideoUploader +import AVKit +import InAppSettingsKit +import SwiftUI +import UIKit + +class MainViewController: UIViewController { + let imagePickerController = UIImagePickerController() + + /// Handle multiple upload tasks to cancel them easily + /// It also runs them sequentially. We upload the file sequentially to simplify the upload progress bar. + let taskManager = TaskManager() + + /// Manage configuration + let iaskViewController: IASKAppSettingsViewController = { + let iaskViewController = IASKAppSettingsViewController() + iaskViewController.showCreditsFooter = false + iaskViewController.view.tintColor = UIColor.orange + return iaskViewController + }() + + let videoPickerButton: UIButton = { + let btn = UIButton(type: .system) + btn.setTitle("Pick a video", for: .normal) + btn.tintColor = UIColor.orange + return btn + }() + + let progressView: UIProgressView = { + let view = UIProgressView() + view.progressTintColor = UIColor.orange + view.isHidden = true + return view + }() + + let cancelButton: UIButton = { + let btn = UIButton(type: .system) + btn.setTitle("Cancel all uploads", for: .normal) + btn.tintColor = UIColor.orange + btn.isHidden = true + return btn + }() + + override func viewDidLoad() { + super.viewDidLoad() + + title = "Uploader" + // Increasing timeout to 120s + ApiVideoUploader.timeout = 120 + + addChild(iaskViewController) + view.addSubview(iaskViewController.view) + + view.addSubview(videoPickerButton) + view.addSubview(progressView) + view.addSubview(cancelButton) + + videoPickerButton.addTarget(self, action: #selector(pickVideo), for: .touchUpInside) + cancelButton.addTarget(self, action: #selector(cleanUploadQueue), for: .touchUpInside) + + constraints() + } + + func constraints() { + iaskViewController.view.translatesAutoresizingMaskIntoConstraints = false + videoPickerButton.translatesAutoresizingMaskIntoConstraints = false + progressView.translatesAutoresizingMaskIntoConstraints = false + cancelButton.translatesAutoresizingMaskIntoConstraints = false + + iaskViewController.view.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + iaskViewController.view.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + iaskViewController.view.heightAnchor.constraint(equalToConstant: 300).isActive = true + iaskViewController.view.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + iaskViewController.view.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10).isActive = true + + videoPickerButton.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + videoPickerButton.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + videoPickerButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + videoPickerButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + videoPickerButton.topAnchor.constraint(equalTo: iaskViewController.view.bottomAnchor, constant: 20).isActive = true + + progressView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + progressView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + progressView.heightAnchor.constraint(equalToConstant: 10).isActive = true + progressView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + progressView.topAnchor.constraint(equalTo: videoPickerButton.bottomAnchor, constant: 10).isActive = true + + cancelButton.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true + cancelButton.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true + cancelButton.heightAnchor.constraint(equalToConstant: 40).isActive = true + cancelButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true + cancelButton.topAnchor.constraint(equalTo: progressView.bottomAnchor, constant: 10).isActive = true + } + + @objc func cleanUploadQueue() { + taskManager.cancelAll() + } + + @objc func pickVideo() { + imagePickerController.sourceType = .photoLibrary + imagePickerController.delegate = self + imagePickerController.mediaTypes = ["public.movie"] + imagePickerController.allowsEditing = false + imagePickerController.videoQuality = .typeHigh + + // for IOS 11 and more + if #available(iOS 11.0, *) { + // disable video compressing to get the highest video quality + imagePickerController.videoExportPreset = AVAssetExportPresetPassthrough + } + + present(imagePickerController, animated: true, completion: nil) + } + + /// Adds the upload task to the ``TaskManager`` + func addToUploadQueue(videoUrl: URL) { + taskManager.add { + do { + return try await self.upload(fileUrl: videoUrl, progress: { progress in + print("Progress: \(progress.fractionCompleted)") + self.progressView.progress = Float(progress.fractionCompleted) + }) + } catch { + print("Upload error: \(error)") + AlertUtils.show(error.localizedDescription, title: "Error", vc: self) + throw error + } + } + } + + /// Creates and uploads a video with async/await + private func upload(fileUrl: URL, progress: ((Progress) -> Void)? = nil) async throws { + let uploadedVideo = try await AsyncApiUtils.uploadVideoWithUploadToken(uploadToken: SettingsManager.uploadToken, url: fileUrl, progress: progress) + + print("Video uploaded: \(uploadedVideo)") + AlertUtils.show("File has been successfully uploaded at video ID: \(uploadedVideo.videoId)", title: "Success", vc: self) + } +} + +extension MainViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController(_: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + guard let url = info[.mediaURL] as? URL else { + AlertUtils.show("No video selected", vc: self) + return + } + + imagePickerController.dismiss(animated: true, completion: nil) + cancelButton.isHidden = false + progressView.isHidden = false + + // Set client configuration + ApiVideoUploader.basePath = SettingsManager.environment.rawValue + + // Upload + addToUploadQueue(videoUrl: url) + } +} diff --git a/Example/Example/Models/CancellableApiTask.swift b/Example/Example/Models/CancellableApiTask.swift new file mode 100644 index 0000000..5323af3 --- /dev/null +++ b/Example/Example/Models/CancellableApiTask.swift @@ -0,0 +1,54 @@ +import ApiVideoUploader +import Foundation + +/// A wrapper around ``RequestTask`` to make it cancellable +class CancellableTask { + internal var task: RequestTask? + + /// Execute the task + func execute() async throws -> T { + fatalError("Not implemented") + } + + /// Cancel the task + func cancel() { + task?.cancel() + } +} + +/// An async/await wrapper around ``VideosAPI.upload`` to make it cancellable +class CancellableUploadWithUploadTokenVideoTask: CancellableTask