diff --git a/Mux-Upload-SDK.podspec b/Mux-Upload-SDK.podspec index 1c9e7fce..429e0473 100644 --- a/Mux-Upload-SDK.podspec +++ b/Mux-Upload-SDK.podspec @@ -1,7 +1,7 @@ Pod::Spec.new do |s| s.name = 'Mux-Upload-SDK' s.module_name = 'MuxUploadSDK' - s.version = '0.3.1' + s.version = '0.6.0' s.summary = 'Upload video to Mux.' s.description = 'A library for uploading video to Mux. Similar to UpChunk, but for iOS.' diff --git a/Sources/MuxUploadSDK/InternalUtilities/Reporting/Reporter.swift b/Sources/MuxUploadSDK/InternalUtilities/Reporting/Reporter.swift index 99697543..d851b1f5 100644 --- a/Sources/MuxUploadSDK/InternalUtilities/Reporting/Reporter.swift +++ b/Sources/MuxUploadSDK/InternalUtilities/Reporting/Reporter.swift @@ -6,7 +6,36 @@ // import Foundation -import UIKit + +fileprivate func processInfoOperationSystemVersion() -> String { + let version = ProcessInfo().operatingSystemVersion + return "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)" +} + +fileprivate func posixModelName() -> String { + var systemName = utsname() + uname(&systemName) + return withUnsafePointer(to: &systemName.machine) { + $0.withMemoryRebound(to: CChar.self, capacity: 1) { + ptr in String.init(validatingUTF8: ptr) + } + } ?? "Unknown" +} + +fileprivate func inferredPlatformName() -> String { + let modelName = posixModelName().lowercased() + if modelName.contains("ipad") { + return "iPadOS" + } else if modelName.contains("iphone") { + return "iOS" + } else { + #if targetEnvironment(simulator) + return "Simulator" + #else + return "Unknown" + #endif + } +} class Reporter: NSObject { @@ -28,9 +57,11 @@ class Reporter: NSObject { var locale: Locale { Locale.current } - var device: UIDevice { - UIDevice.current - } + + let model: String + let platformName: String + let platformVersion: String + var regionCode: String? { if #available(iOS 16, *) { return locale.language.region?.identifier @@ -52,6 +83,10 @@ class Reporter: NSObject { string: "https://mobile.muxanalytics.com" )! + self.model = posixModelName() + self.platformName = inferredPlatformName() + self.platformVersion = processInfoOperationSystemVersion() + super.init() let sessionConfig: URLSessionConfiguration = URLSessionConfiguration.default @@ -105,14 +140,14 @@ extension Reporter { let data = UploadSucceededEvent.Data( appName: Bundle.main.appName, appVersion: Bundle.main.appVersion, - deviceModel: device.model, + deviceModel: model, inputDuration: inputDuration, inputSize: inputSize, inputStandardizationRequested: options.inputStandardization.isRequested, - platformName: device.systemName, - platformVersion: device.systemVersion, + platformName: platformName, + platformVersion: platformVersion, regionCode: regionCode, - sdkVersion: Version.versionString, + sdkVersion: SemanticVersion.versionString, uploadStartTime: uploadStartTime, uploadEndTime: uploadEndTime, uploadURL: uploadURL @@ -145,15 +180,15 @@ extension Reporter { let data = UploadFailedEvent.Data( appName: Bundle.main.appName, appVersion: Bundle.main.appVersion, - deviceModel: device.model, + deviceModel: model, errorDescription: errorDescription, inputDuration: inputDuration, inputSize: inputSize, inputStandardizationRequested: options.inputStandardization.isRequested, - platformName: device.systemName, - platformVersion: device.systemVersion, + platformName: platformName, + platformVersion: platformVersion, regionCode: regionCode, - sdkVersion: Version.versionString, + sdkVersion: SemanticVersion.versionString, uploadStartTime: uploadStartTime, uploadEndTime: uploadEndTime, uploadURL: url @@ -186,15 +221,15 @@ extension Reporter { let data = InputStandardizationSucceededEvent.Data( appName: Bundle.main.appName, appVersion: Bundle.main.appVersion, - deviceModel: device.model, + deviceModel: model, inputDuration: inputDuration, inputSize: inputSize, maximumResolution: options.inputStandardization.maximumResolution.description, nonStandardInputReasons: nonStandardInputReasons.map(\.description), - platformName: device.systemName, - platformVersion: device.systemVersion, + platformName: platformName, + platformVersion: platformVersion, regionCode: regionCode, - sdkVersion: Version.versionString, + sdkVersion: SemanticVersion.versionString, standardizationStartTime: standardizationStartTime, standardizationEndTime: standardizationEndTime, uploadURL: uploadURL @@ -229,16 +264,16 @@ extension Reporter { let data = InputStandardizationFailedEvent.Data( appName: Bundle.main.appName, appVersion: Bundle.main.appVersion, - deviceModel: device.model, + deviceModel: model, errorDescription: errorDescription, inputDuration: inputDuration, inputSize: inputSize, maximumResolution: options.inputStandardization.maximumResolution.description, nonStandardInputReasons: nonStandardInputReasons.map(\.description), - platformName: device.systemName, - platformVersion: device.systemVersion, + platformName: platformName, + platformVersion: platformVersion, regionCode: regionCode, - sdkVersion: Version.versionString, + sdkVersion: SemanticVersion.versionString, standardizationStartTime: standardizationStartTime, standardizationEndTime: standardizationEndTime, uploadCanceled: uploadCanceled, diff --git a/Sources/MuxUploadSDK/PublicAPI/DirectUpload.swift b/Sources/MuxUploadSDK/PublicAPI/DirectUpload.swift index 22064448..9736cdbf 100644 --- a/Sources/MuxUploadSDK/PublicAPI/DirectUpload.swift +++ b/Sources/MuxUploadSDK/PublicAPI/DirectUpload.swift @@ -8,6 +8,8 @@ import AVFoundation import Foundation +/// Indicates whether a finished upload failed due to an error +/// or succeeded along with details public typealias DirectUploadResult = Result /// @@ -341,6 +343,9 @@ public final class DirectUpload { */ public var progressHandler: StateHandler? + /** + Details about a ``DirectUpload`` after it successfully finished + */ public struct SuccessDetails : Sendable, Hashable { public let finalState: TransportStatus } diff --git a/Sources/MuxUploadSDK/PublicAPI/Options/DirectUploadOptions.swift b/Sources/MuxUploadSDK/PublicAPI/Options/DirectUploadOptions.swift index e152c805..61b4787c 100644 --- a/Sources/MuxUploadSDK/PublicAPI/Options/DirectUploadOptions.swift +++ b/Sources/MuxUploadSDK/PublicAPI/Options/DirectUploadOptions.swift @@ -4,24 +4,29 @@ import Foundation +// MARK: - Direct Upload Options + /// Options for the direct upload public struct DirectUploadOptions { // MARK: - Transport Options - /// Options to control the SDK network operations to - /// transport the direct upload input to Mux + /// Options for tuning network transport of direct upload + /// chunks to Mux. Using the ``default`` is recommended + /// for most applications. public struct Transport { - /// At least 8M is recommended + /// The size of each file chunk in bytes sent by the + /// SDK during an upload. At least 8MB is recommended. public var chunkSizeInBytes: Int - /// Number of retry attempts per chunk if the - /// associated request fails + /// Number of retry attempts per chunk if its upload + /// request is unsuccessful public var retryLimitPerChunk: Int - /// A default set of transport options: 8MB chunk - /// size and chunk request retry limit of 3 + /// Default options for ``DirectUpload`` chunk transport + /// over the network. The chunk size is 8MB and the + /// per-chunk retry limit is 3. public static var `default`: Transport { Transport( chunkSizeInBytes: 8 * 1024 * 1024, @@ -29,15 +34,35 @@ public struct DirectUploadOptions { ) } - /// Initializes options that govern network transport - /// by the SDK - /// + /// Initializes options for transport of upload chunks + /// over the network /// - Parameters: - /// - chunkSize: the size of each file chunk in - /// bytes the SDK sends when uploading, default - /// value is 8MB - /// - retriesPerChunk: number of retry attempts - /// if the chunk request fails, default value is 3 + /// - chunkSize: the size of each file chunk sent + /// by the SDK during an upload. + /// Defaults to 8MB. + /// - retryLimitPerChunk: number of times a failed + /// chunk request is retried. Default limit is + /// 3 retries. + public init( + chunkSize: Measurement = .defaultDirectUploadChunkSize, + retryLimitPerChunk: Int = 3 + ) { + self.chunkSizeInBytes = Int( + abs(chunkSize.converted(to: .bytes).value) + .rounded(.down) + ) + self.retryLimitPerChunk = retryLimitPerChunk + } + + /// Initializes options for transport of upload chunks + /// over the network + /// - Parameters: + /// - chunkSizeInBytes: the size of each file + /// chunk in bytes the SDK uploads in a single + /// request. Default chunk size is 8MB. + /// - retryLimitPerChunk: number of times a failed + /// chunk request is retried. Default limit is + /// 3 retries. public init( chunkSizeInBytes: Int = 8 * 1024 * 1024, retryLimitPerChunk: Int = 3 @@ -47,12 +72,14 @@ public struct DirectUploadOptions { } } - /// Transport options for the direct upload + /// Network transport options for direct upload chunks public var transport: Transport // MARK: - Input Standardization Options - /// Options controlling direct upload input standardization + /// Options for adjusments made by ``DirectUpload`` + /// to some inputs to minimize processing time during + /// ingestion public struct InputStandardization { /// If requested the SDK will attempt to detect @@ -124,8 +151,8 @@ public struct DirectUploadOptions { maximumResolution: .default ) - // Kept private to an invalid combination of parameters - // being used for initialization + // Kept private to avoid an invalid combination of + // parameters being used for initialization private init( isRequested: Bool, maximumResolution: MaximumResolution @@ -134,10 +161,8 @@ public struct DirectUploadOptions { self.maximumResolution = maximumResolution } - /// Used to initialize ``DirectUploadOptions.InputStandardization`` - /// with that enables input standardization with - /// a maximum resolution - /// + /// Initializes options that request input + /// standardization with a custom maximum resolution /// - Parameters: /// - maximumResolution: the maximum resolution /// of the standardized input @@ -186,12 +211,14 @@ public struct DirectUploadOptions { // MARK: Direct Upload Options Initializers + /// Initializes options that dictate how the direct upload + /// is carried out by the SDK /// - Parameters: - /// - inputStandardization: options to enable or - /// disable standardizing the format of the direct - /// upload inputs, it is requested by default. To - /// prevent the SDK from making any changes to the - /// format of the input use ``DirectUploadOptions.InputStandardization.skipped`` + /// - inputStandardization: options related to input + /// standardization. Input standardization is requested + /// by default. + /// To skip input standardization pass in + /// ``DirectUploadOptions.InputStandardization.skipped``. /// - transport: options for transporting the /// direct upload input to Mux /// - eventTracking: event tracking options for the @@ -206,19 +233,49 @@ public struct DirectUploadOptions { self.eventTracking = eventTracking } + /// Initializes options that dictate how the direct upload + /// is carried out by the SDK + /// - Parameters: + /// - eventTracking: event tracking options for the + /// direct upload + /// - inputStandardization: options related to input + /// standardization. Input standardization is requested + /// by default. + /// To skip input standardization pass in + /// ``DirectUploadOptions.InputStandardization.skipped``. + /// - chunkSize: The size of each file chunk sent by + /// the SDK during an upload. Defaults to 8MB. + /// - retryLimitPerChunk: number of retry attempts + /// if the chunk request fails. Defaults to 3. + public init( + eventTracking: EventTracking = .default, + inputStandardization: InputStandardization = .default, + chunkSize: Measurement = .defaultDirectUploadChunkSize, + retryLimitPerChunk: Int = 3 + ) { + self.eventTracking = eventTracking + self.inputStandardization = inputStandardization + self.transport = Transport( + chunkSize: chunkSize, + retryLimitPerChunk: retryLimitPerChunk + ) + } + + /// Initializes options that dictate how the direct upload + /// is carried out by the SDK /// - Parameters: /// - eventTracking: event tracking options for the /// direct upload - /// - inputStandardization: options to enable or - /// disable standardizing the format of the direct - /// upload inputs, it is requested by default. To - /// prevent the SDK from making any changes to the - /// format of the input use ``DirectUploadOptions.InputStandardization.skipped`` - /// - chunkSize: the size of each file chunk in - /// bytes the SDK sends when uploading, default - /// value is 8MB - /// - retriesPerChunk: number of retry attempts - /// if the chunk request fails, default value is 3 + /// - inputStandardization: options related to input + /// standardization. Input standardization is requested + /// by default. + /// To skip input standardization pass in + /// ``DirectUploadOptions.InputStandardization.skipped``. + /// - chunkSizeInBytes: The size of each file chunk + /// in bytes sent by the SDK during an upload. + /// Defaults to 8MB. + /// - retryLimitPerChunk: number of retry attempts + /// if the chunk request fails. Defaults to 3. public init( eventTracking: EventTracking = .default, inputStandardization: InputStandardization = .default, @@ -237,6 +294,16 @@ public struct DirectUploadOptions { // MARK: - Extensions +extension Measurement where UnitType == UnitInformationStorage { + /// Default direct upload chunk size + public static var defaultDirectUploadChunkSize: Self { + Measurement( + value: 8, + unit: .megabytes + ) + } +} + extension DirectUploadOptions.InputStandardization.MaximumResolution: CustomStringConvertible { public var description: String { switch self { diff --git a/Sources/MuxUploadSDK/PublicAPI/SemanticVersion.swift b/Sources/MuxUploadSDK/PublicAPI/SemanticVersion.swift new file mode 100644 index 00000000..9bff844f --- /dev/null +++ b/Sources/MuxUploadSDK/PublicAPI/SemanticVersion.swift @@ -0,0 +1,23 @@ +// +// SemanticVersion.swift +// +// +// Created by Emily Dixon on 2/21/23. +// + +import Foundation + +/// Version information about the SDK +public struct SemanticVersion { + /// Major version component. + public static let major = 0 + /// Minor version component. + public static let minor = 6 + /// Patch version component. + public static let patch = 0 + + /// String form of the version number in the format X.Y.Z + /// where X, Y, and Z are the major, minor, and patch + /// version components + public static let versionString = "\(major).\(minor).\(patch)" +} diff --git a/Sources/MuxUploadSDK/PublicAPI/Version.swift b/Sources/MuxUploadSDK/PublicAPI/Version.swift deleted file mode 100644 index 8e8e9154..00000000 --- a/Sources/MuxUploadSDK/PublicAPI/Version.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// Version.swift -// -// -// Created by Emily Dixon on 2/21/23. -// - -import Foundation - -public struct Version { - /// Major version. - public static let major = 0 - /// Minor version. - public static let minor = 5 - /// Revision number. - public static let revision = 0 - - /// String form of the version number. - public static let versionString = "\(major).\(minor).\(revision)" -}