diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 0ae6198..743e7bb 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -7,6 +7,13 @@ objects = { /* Begin PBXBuildFile section */ + 211834CE29CDBAB2002F2CCD /* AsyncApiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834CD29CDBAB2002F2CCD /* AsyncApiUtils.swift */; }; + 211834DB29CDEFF8002F2CCD /* Contents.json in Resources */ = {isa = PBXBuildFile; fileRef = 211834D829CDEFF8002F2CCD /* Contents.json */; }; + 211834E129CDF08F002F2CCD /* ClientAppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834DE29CDF08F002F2CCD /* ClientAppError.swift */; }; + 211834E229CDF08F002F2CCD /* CancellableApiTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834DF29CDF08F002F2CCD /* CancellableApiTask.swift */; }; + 211834E329CDF08F002F2CCD /* TaskManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834E029CDF08F002F2CCD /* TaskManager.swift */; }; + 211834E729CDF0A8002F2CCD /* AsyncApiUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834E529CDF0A8002F2CCD /* AsyncApiUtils.swift */; }; + 211834E829CDF0A8002F2CCD /* AlertUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 211834E629CDF0A8002F2CCD /* AlertUtils.swift */; }; 2136DD4A274B9E74007B9FC9 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD49274B9E74007B9FC9 /* AppDelegate.swift */; }; 2136DD4C274B9E74007B9FC9 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD4B274B9E74007B9FC9 /* SceneDelegate.swift */; }; 2136DD4E274B9E74007B9FC9 /* VideosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2136DD4D274B9E74007B9FC9 /* VideosViewController.swift */; }; @@ -21,6 +28,13 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 211834CD29CDBAB2002F2CCD /* AsyncApiUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = AsyncApiUtils.swift; path = Utils/AsyncApiUtils.swift; sourceTree = ""; }; + 211834D829CDEFF8002F2CCD /* Contents.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = Contents.json; path = Assets.xcassets/Contents.json; sourceTree = ""; }; + 211834DE29CDF08F002F2CCD /* ClientAppError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ClientAppError.swift; sourceTree = ""; }; + 211834DF29CDF08F002F2CCD /* CancellableApiTask.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CancellableApiTask.swift; sourceTree = ""; }; + 211834E029CDF08F002F2CCD /* TaskManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TaskManager.swift; sourceTree = ""; }; + 211834E529CDF0A8002F2CCD /* AsyncApiUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsyncApiUtils.swift; sourceTree = ""; }; + 211834E629CDF0A8002F2CCD /* AlertUtils.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlertUtils.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 = ""; }; @@ -49,6 +63,25 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 211834DD29CDF08F002F2CCD /* Models */ = { + isa = PBXGroup; + children = ( + 211834DE29CDF08F002F2CCD /* ClientAppError.swift */, + 211834DF29CDF08F002F2CCD /* CancellableApiTask.swift */, + 211834E029CDF08F002F2CCD /* TaskManager.swift */, + ); + path = Models; + sourceTree = ""; + }; + 211834E429CDF0A8002F2CCD /* Utils */ = { + isa = PBXGroup; + children = ( + 211834E529CDF0A8002F2CCD /* AsyncApiUtils.swift */, + 211834E629CDF0A8002F2CCD /* AlertUtils.swift */, + ); + path = Utils; + sourceTree = ""; + }; 2136DD3D274B9E74007B9FC9 = { isa = PBXGroup; children = ( @@ -70,7 +103,10 @@ 2136DD48274B9E74007B9FC9 /* Example */ = { isa = PBXGroup; children = ( - C7BC280827A18111007406AB /* Controller */, + 211834E429CDF0A8002F2CCD /* Utils */, + 211834DD29CDF08F002F2CCD /* Models */, + 211834D829CDEFF8002F2CCD /* Contents.json */, + C7BC280827A18111007406AB /* ViewControllers */, C7BC280527A17FD4007406AB /* Cell */, 2136DD49274B9E74007B9FC9 /* AppDelegate.swift */, 2136DD4B274B9E74007B9FC9 /* SceneDelegate.swift */, @@ -79,6 +115,7 @@ 2136DD54274B9E77007B9FC9 /* LaunchScreen.storyboard */, 2136DD57274B9E77007B9FC9 /* Info.plist */, C7BC280927A18CC3007406AB /* ClientManager.swift */, + 211834CD29CDBAB2002F2CCD /* AsyncApiUtils.swift */, ); path = Example; sourceTree = ""; @@ -107,13 +144,13 @@ path = Cell; sourceTree = ""; }; - C7BC280827A18111007406AB /* Controller */ = { + C7BC280827A18111007406AB /* ViewControllers */ = { isa = PBXGroup; children = ( 2136DD4D274B9E74007B9FC9 /* VideosViewController.swift */, C7BC280327A17CD2007406AB /* UploaderViewController.swift */, ); - path = Controller; + path = ViewControllers; sourceTree = ""; }; /* End PBXGroup section */ @@ -180,6 +217,7 @@ 2136DD56274B9E77007B9FC9 /* LaunchScreen.storyboard in Resources */, 2136DD53274B9E77007B9FC9 /* Assets.xcassets in Resources */, 2136DD51274B9E74007B9FC9 /* Main.storyboard in Resources */, + 211834DB29CDEFF8002F2CCD /* Contents.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -191,10 +229,16 @@ buildActionMask = 2147483647; files = ( 2136DD4E274B9E74007B9FC9 /* VideosViewController.swift in Sources */, + 211834E129CDF08F002F2CCD /* ClientAppError.swift in Sources */, + 211834CE29CDBAB2002F2CCD /* AsyncApiUtils.swift in Sources */, 2136DD4A274B9E74007B9FC9 /* AppDelegate.swift in Sources */, C7BC280A27A18CC3007406AB /* ClientManager.swift in Sources */, C7BC280427A17CD2007406AB /* UploaderViewController.swift in Sources */, + 211834E729CDF0A8002F2CCD /* AsyncApiUtils.swift in Sources */, + 211834E829CDF0A8002F2CCD /* AlertUtils.swift in Sources */, 2136DD4C274B9E74007B9FC9 /* SceneDelegate.swift in Sources */, + 211834E229CDF08F002F2CCD /* CancellableApiTask.swift in Sources */, + 211834E329CDF08F002F2CCD /* TaskManager.swift in Sources */, C7BC280727A17FEB007406AB /* VideoTableViewCell.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -344,9 +388,10 @@ 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_CFBundleDisplayName = ApiVideoClient; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; @@ -372,9 +417,10 @@ 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_CFBundleDisplayName = ApiVideoClient; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen; INFOPLIST_KEY_UIMainStoryboardFile = Main; diff --git a/Example/Example/Base.lproj/Main.storyboard b/Example/Example/Base.lproj/Main.storyboard index a6c7b99..ba2c177 100644 --- a/Example/Example/Base.lproj/Main.storyboard +++ b/Example/Example/Base.lproj/Main.storyboard @@ -1,8 +1,9 @@ - + - + + @@ -37,8 +38,8 @@ - + @@ -59,7 +60,7 @@ - + @@ -68,7 +69,7 @@ - + @@ -85,7 +86,7 @@ - + diff --git a/Example/Example/Cell/VideoTableViewCell.swift b/Example/Example/Cell/VideoTableViewCell.swift index 58ff270..b1eed5a 100644 --- a/Example/Example/Cell/VideoTableViewCell.swift +++ b/Example/Example/Cell/VideoTableViewCell.swift @@ -4,11 +4,12 @@ // import UIKit +import ApiVideoClient class VideoTableViewCell: UITableViewCell { static let identifier = "VideoTableViewCell" - + private let title: UILabel = { let label = UILabel() label.numberOfLines = 1 @@ -19,66 +20,83 @@ class VideoTableViewCell: UITableViewCell { label.numberOfLines = 1 return label }() - - private let myImageView: UIImageView = { + + private let thumbnailView: UIImageView = { let imageView = UIImageView() return imageView }() - + override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { super.init(style: style, reuseIdentifier: reuseIdentifier) contentView.addSubview(title) contentView.addSubview(videoId) - contentView.addSubview(myImageView) - myImageView.contentMode = .scaleAspectFit - myImageView.backgroundColor = .black + contentView.addSubview(thumbnailView) + thumbnailView.contentMode = .scaleAspectFit + thumbnailView.backgroundColor = .black contentView.clipsToBounds = true accessoryType = .none } - - required init?(coder: NSCoder){ + + required init?(coder: NSCoder) { fatalError() } - + override func layoutSubviews() { super.layoutSubviews() - myImageView.frame = CGRect( + thumbnailView.frame = CGRect( x: 10, y: 5, width: contentView.frame.size.width - 20, height: contentView.frame.size.height * 0.7 ) - + title.frame = CGRect( x: 20, - y: (myImageView.frame.height * 0.6) + 55, + y: (thumbnailView.frame.height * 0.6) + 55, width: contentView.frame.size.width, height: contentView.frame.size.height * 0.4 ) videoId.frame = CGRect( x: 20, - y: (myImageView.frame.height * 0.6) + 80, + y: (thumbnailView.frame.height * 0.6) + 80, width: contentView.frame.size.width, height: contentView.frame.size.height * 0.4 ) videoId.textColor = .lightGray videoId.font = videoId.font.withSize(10) } - + override func prepareForReuse() { super.prepareForReuse() title.text = nil videoId.text = nil } - - public func configure(with model: VideosOption){ - title.text = model.title - videoId.text = model.videoId - - let myUrl = URL(string: model.thumbnail!) - let myData = try? Data(contentsOf: myUrl!) - myImageView.image = UIImage(data: myData!) - + + public func bind(with video: Video) { + title.text = video.title ?? "Unknown title" + videoId.text = video.videoId + + if let thumbnail = video.assets?.thumbnail { + do { + guard let thumbnailUrl = URL(string: thumbnail) else { + print("Invalid thumbnail url: \(thumbnail)") + return + } + fetchThumbnails(url: thumbnailUrl) + } catch { + print("Error while loading thumbnail: \(error)") + } + } } + private func fetchThumbnails(url: URL) { + Task { + do { + let (data, _) = try await URLSession.shared.data(from: url) + thumbnailView.image = UIImage(data: data) + } catch { + print("Error while loading thumbnail: \(error)") + } + } + } } diff --git a/Example/Example/ClientManager.swift b/Example/Example/ClientManager.swift index 97ac907..6f7a856 100644 --- a/Example/Example/ClientManager.swift +++ b/Example/Example/ClientManager.swift @@ -6,16 +6,17 @@ import Foundation import ApiVideoClient -class ClientManager{ - public static var apiKey: String = "" +class ClientManager { + public static var apiKey: String = "YOUR_API_KEY" public static var environment: Environment = Environment.sandbox - - public static func environmentToBool()-> Bool{ - var isOn = false - if(environment == Environment.production){ - isOn = true + + public static var isProduction: Bool { + get { + if (environment == Environment.production) { + return true + } else { + return false + } } - return isOn } - } diff --git a/Example/Example/Controller/UploaderViewController.swift b/Example/Example/Controller/UploaderViewController.swift deleted file mode 100644 index 99864af..0000000 --- a/Example/Example/Controller/UploaderViewController.swift +++ /dev/null @@ -1,260 +0,0 @@ -// -// UploaderViewController.swift -// Example -// - -import UIKit -import AVKit -import ApiVideoClient -import SwiftUI - -class UploaderViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, UITextFieldDelegate { - let imagePickerController = UIImagePickerController() - - let paramBackgroundView: UIView = { - let paramView = UIView() - return paramView - }() - let environmentTitleLabel: UILabel = { - let title = UILabel() - title.text = "Environment" - title.font = UIFont.systemFont(ofSize: 22, weight: .bold) - return title - }() - - let selectedEnvironmentLabel: UILabel = { - let title = UILabel() - title.text = "Production" - return title - }() - - let selectEnvironmentSwitch: UISwitch = { - let selectSwitch = UISwitch() - return selectSwitch - }() - - let uplaodButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("Upload", for: .normal) - return btn - }() - - let apiKeyLabel: UILabel = { - let api = UILabel() - api.text = "Your Api key :" - return api - }() - - let apiKeyTextField: UITextField = { - let api = UITextField() - return api - }() - - let titleSelectVideoLabel: UILabel = { - let titleLabel = UILabel() - titleLabel.text = "Choose your video :" - return titleLabel - }() - - let selectVideoButton: UIButton = { - let btn = UIButton(type: .system) - btn.setTitle("Select Video", for: .normal) - return btn - }() - - let thumbnailImageView: UIImageView = { - let thumbnail = UIImageView() - thumbnail.backgroundColor = .gray - return thumbnail - }() - - private var videoUrl: URL? - - override func viewDidLoad() { - super.viewDidLoad() - title = "Uploader" - view.addSubview(paramBackgroundView) - paramBackgroundView.backgroundColor = .darkGray - paramBackgroundView.layer.cornerRadius = 10 - paramBackgroundView.addSubview(environmentTitleLabel) - paramBackgroundView.addSubview(selectedEnvironmentLabel) - paramBackgroundView.addSubview(selectEnvironmentSwitch) - paramBackgroundView.addSubview(apiKeyLabel) - - paramBackgroundView.addSubview(apiKeyTextField) - apiKeyLabel.frame = CGRect(x: 0, y: 0, width: 105, height: 30) - view.addSubview(titleSelectVideoLabel) - view.addSubview(thumbnailImageView) - view.addSubview(selectVideoButton) - view.addSubview(uplaodButton) - - self.selectVideoButton.addTarget(self, action: #selector(selectVideo), for: .touchUpInside) - self.uplaodButton.addTarget(self, action: #selector(uploadVideo), for: .touchUpInside) - self.selectEnvironmentSwitch.addTarget(self, action: #selector(toggleSwitch), for: .touchUpInside) - - selectEnvironmentSwitch.isOn = ClientManager.environmentToBool() - if(!selectEnvironmentSwitch.isOn){ - selectedEnvironmentLabel.text = "Sandbox" - } - - apiKeyTextField.addTarget(self, action: #selector(UploaderViewController.textFieldDidChange(_:)), for: .editingChanged) - apiKeyTextField.delegate = self - apiKeyTextField.text = ClientManager.apiKey - - constraints() - } - - func constraints(){ - paramBackgroundView.translatesAutoresizingMaskIntoConstraints = false - uplaodButton.translatesAutoresizingMaskIntoConstraints = false - environmentTitleLabel.translatesAutoresizingMaskIntoConstraints = false - selectedEnvironmentLabel.translatesAutoresizingMaskIntoConstraints = false - selectEnvironmentSwitch.translatesAutoresizingMaskIntoConstraints = false - apiKeyLabel.translatesAutoresizingMaskIntoConstraints = false - apiKeyTextField.translatesAutoresizingMaskIntoConstraints = false - titleSelectVideoLabel.translatesAutoresizingMaskIntoConstraints = false - selectVideoButton.translatesAutoresizingMaskIntoConstraints = false - thumbnailImageView.translatesAutoresizingMaskIntoConstraints = false - - paramBackgroundView.widthAnchor.constraint(equalToConstant: view.frame.width - 20).isActive = true - paramBackgroundView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30).isActive = true - paramBackgroundView.heightAnchor.constraint(equalToConstant: 150).isActive = true - paramBackgroundView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - - environmentTitleLabel.centerXAnchor.constraint(equalTo: paramBackgroundView.centerXAnchor).isActive = true - environmentTitleLabel.topAnchor.constraint(equalTo: paramBackgroundView.topAnchor, constant: 10).isActive = true - - - selectedEnvironmentLabel.topAnchor.constraint(equalTo: paramBackgroundView.topAnchor, constant: 50).isActive = true - selectedEnvironmentLabel.leftAnchor.constraint(equalTo: paramBackgroundView.leftAnchor, constant: 20).isActive = true - selectedEnvironmentLabel.widthAnchor.constraint(equalToConstant: (view.frame.width - 20 )/2).isActive = true - - selectEnvironmentSwitch.centerYAnchor.constraint(equalTo: selectedEnvironmentLabel.centerYAnchor).isActive = true - selectEnvironmentSwitch.rightAnchor.constraint(equalTo: paramBackgroundView.rightAnchor, constant: -20).isActive = true - - - apiKeyLabel.topAnchor.constraint(equalTo: paramBackgroundView.topAnchor, constant: 80).isActive = true - apiKeyLabel.bottomAnchor.constraint(equalTo: paramBackgroundView.bottomAnchor).isActive = true - apiKeyLabel.leftAnchor.constraint(equalTo: paramBackgroundView.leftAnchor, constant: 20).isActive = true - - apiKeyTextField.centerYAnchor.constraint(equalTo: apiKeyLabel.centerYAnchor).isActive = true - apiKeyTextField.topAnchor.constraint(equalTo: paramBackgroundView.topAnchor, constant: 80).isActive = true - apiKeyTextField.bottomAnchor.constraint(equalTo: paramBackgroundView.bottomAnchor).isActive = true - apiKeyTextField.rightAnchor.constraint(equalTo: paramBackgroundView.rightAnchor, constant: -20).isActive = true - apiKeyTextField.widthAnchor.constraint(equalToConstant: 250).isActive = true - - titleSelectVideoLabel.topAnchor.constraint(equalTo: paramBackgroundView.bottomAnchor, constant: 30).isActive = true - titleSelectVideoLabel.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 30).isActive = true - - thumbnailImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - thumbnailImageView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true - thumbnailImageView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true - thumbnailImageView.heightAnchor.constraint(equalToConstant: 220).isActive = true - thumbnailImageView.topAnchor.constraint(equalTo: titleSelectVideoLabel.bottomAnchor, constant: 20).isActive = true - - - selectVideoButton.widthAnchor.constraint(equalToConstant: 110).isActive = true - selectVideoButton.heightAnchor.constraint(equalToConstant: 40).isActive = true - - selectVideoButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - selectVideoButton.topAnchor.constraint(equalTo: thumbnailImageView.bottomAnchor, constant: 10).isActive = true - - uplaodButton.widthAnchor.constraint(equalToConstant: 70).isActive = true - uplaodButton.heightAnchor.constraint(equalToConstant: 40).isActive = true - uplaodButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true - uplaodButton.topAnchor.constraint(equalTo: selectVideoButton.bottomAnchor, constant: 20).isActive = true - - // uplaodButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 40).isActive = true - - } - - @objc func selectVideo() { - 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, *) { - //desable video compressing to get the highest video quality - imagePickerController.videoExportPreset = AVAssetExportPresetPassthrough - } - - present(imagePickerController, animated: true, completion: nil) - } - - func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { - let url = info[.mediaURL] as! URL - videoUrl = url - thumbnailImageView.image = getThumbnailImage(forUrl: url) - imagePickerController.dismiss(animated: true, completion: nil) - } - - func upload(url: URL) { - VideosAPI.create(videoCreationPayload: VideoCreationPayload(title: "my video")) { video, error in - if let video = video { - do { - try VideosAPI.upload( - videoId: video.videoId, - file: url, - onProgressReady: { progress in - print("Progress: \(progress)") - }) { video, error in - if video != nil { - self.thumbnailImageView.image = nil - } - if let error = error { - print("Upload error: \(error)") - } - } - } catch { - print("Upload error: \(error)") - } - } - if let error = error { - print("Create error: \(error)") - } - } - } - - @objc func uploadVideo(){ - if(videoUrl != nil){ - upload(url: videoUrl!) - }else{ - print("error video") - } - - } - - @objc func toggleSwitch(){ - if(selectEnvironmentSwitch.isOn){ - ClientManager.environment = Environment.production - selectedEnvironmentLabel.text = "Production" - - }else{ - ClientManager.environment = Environment.sandbox - selectedEnvironmentLabel.text = "Sandbox" - } - } - - - func getThumbnailImage(forUrl url: URL) -> UIImage? { - let asset: AVAsset = AVAsset(url: url) - let imageGenerator = AVAssetImageGenerator(asset: asset) - - do { - let thumbnailImage = try imageGenerator.copyCGImage(at: CMTimeMake(value: 1, timescale: 60), actualTime: nil) - return UIImage(cgImage: thumbnailImage) - } catch let error { - print(error) - } - - return nil - } - - @objc func textFieldDidChange(_ textField: UITextField) { - ClientManager.apiKey = textField.text! - } - -} diff --git a/Example/Example/Models/CancellableApiTask.swift b/Example/Example/Models/CancellableApiTask.swift new file mode 100644 index 0000000..115c2a3 --- /dev/null +++ b/Example/Example/Models/CancellableApiTask.swift @@ -0,0 +1,98 @@ +import Foundation +import ApiVideoClient + +/// 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 CancellableUploadVideoTask: CancellableTask