diff --git a/Example/.DS_Store b/Example/.DS_Store new file mode 100644 index 0000000..591f077 Binary files /dev/null and b/Example/.DS_Store differ diff --git a/Example/Example.xcodeproj/project.pbxproj b/Example/Example.xcodeproj/project.pbxproj index 387e0d0..88e8acc 100644 --- a/Example/Example.xcodeproj/project.pbxproj +++ b/Example/Example.xcodeproj/project.pbxproj @@ -145,7 +145,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = teodorpatras; TargetAttributes = { 137DF0851D2043F400C15E86 = { @@ -157,7 +157,7 @@ }; buildConfigurationList = 137DF0811D2043F400C15E86 /* Build configuration list for PBXProject "Example" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, @@ -253,19 +253,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -301,19 +310,28 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -348,7 +366,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.jukebox.example; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -361,7 +379,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.jukebox.example; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/Example/Example.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme index dd14de0..5cb3266 100644 --- a/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme +++ b/Example/Example.xcodeproj/xcshareddata/xcschemes/Example.xcscheme @@ -1,6 +1,6 @@ Bool { + internal func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. application.isStatusBarHidden = true return true diff --git a/Example/Example/ViewController.swift b/Example/Example/ViewController.swift index b485355..fc4d758 100644 --- a/Example/Example/ViewController.swift +++ b/Example/Example/ViewController.swift @@ -58,7 +58,7 @@ class ViewController: UIViewController, JukeboxDelegate { let color = UIColor(red:0.84, green:0.09, blue:0.1, alpha:1) indicator.color = color - slider.setThumbImage(UIImage(named: "sliderThumb"), for: UIControlState()) + slider.setThumbImage(UIImage(named: "sliderThumb"), for: UIControl.State()) slider.minimumTrackTintColor = color slider.maximumTrackTintColor = UIColor.black @@ -99,9 +99,9 @@ class ViewController: UIViewController, JukeboxDelegate { }) if jukebox.state == .ready { - playPauseButton.setImage(UIImage(named: "playBtn"), for: UIControlState()) + playPauseButton.setImage(UIImage(named: "playBtn"), for: UIControl.State()) } else if jukebox.state == .loading { - playPauseButton.setImage(UIImage(named: "pauseBtn"), for: UIControlState()) + playPauseButton.setImage(UIImage(named: "pauseBtn"), for: UIControl.State()) } else { volumeSlider.value = jukebox.volume let imageName: String @@ -111,7 +111,7 @@ class ViewController: UIViewController, JukeboxDelegate { case .paused, .failed, .ready: imageName = "playBtn" } - playPauseButton.setImage(UIImage(named: imageName), for: UIControlState()) + playPauseButton.setImage(UIImage(named: imageName), for: UIControl.State()) } print("Jukebox state changed to \(jukebox.state)") diff --git a/Jukebox.xcodeproj/project.pbxproj b/Jukebox.xcodeproj/project.pbxproj index aa0ec86..532125d 100644 --- a/Jukebox.xcodeproj/project.pbxproj +++ b/Jukebox.xcodeproj/project.pbxproj @@ -171,7 +171,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1020; ORGANIZATIONNAME = teodorpatras; TargetAttributes = { 137DF0621D2041F500C15E86 = { @@ -186,10 +186,11 @@ }; buildConfigurationList = 137DF05D1D2041F500C15E86 /* Build configuration list for PBXProject "Jukebox" */; compatibilityVersion = "Xcode 3.2"; - developmentRegion = English; + developmentRegion = en; hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = 137DF0591D2041F500C15E86; productRefGroup = 137DF0641D2041F500C15E86 /* Products */; @@ -259,14 +260,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -310,14 +319,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; CLANG_WARN_EMPTY_BODY = YES; CLANG_WARN_ENUM_CONVERSION = YES; CLANG_WARN_INFINITE_RECURSION = YES; CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -357,12 +374,13 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.Jukebox; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -377,11 +395,12 @@ DYLIB_INSTALL_NAME_BASE = "@rpath"; INFOPLIST_FILE = Source/Info.plist; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 9.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.Jukebox; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; @@ -392,7 +411,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.JukeboxTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Debug; }; @@ -403,7 +422,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = com.teodorpatras.JukeboxTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/Jukebox.xcodeproj/xcshareddata/xcschemes/Jukebox.xcscheme b/Jukebox.xcodeproj/xcshareddata/xcschemes/Jukebox.xcscheme index 78aaa1f..17bb157 100644 --- a/Jukebox.xcodeproj/xcshareddata/xcschemes/Jukebox.xcscheme +++ b/Jukebox.xcodeproj/xcshareddata/xcschemes/Jukebox.xcscheme @@ -1,6 +1,6 @@ + + + + IDEDidComputeMac32BitWarning + + + diff --git a/README.md b/README.md index c723c52..67914c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ ![Jukebox: audio player in Swift](https://raw.githubusercontent.com/teodorpatras/Jukebox/master/assets/jukebox.png) -![Swift3](https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat") +![Swift5](https://img.shields.io/badge/Swift-3.0-orange.svg?style=flat") [![Platform](https://img.shields.io/cocoapods/p/Jukebox.svg?style=flat)](http://cocoapods.org/pods/Jukebox) [![Build Status](https://travis-ci.org/teodorpatras/Jukebox.svg)](https://travis-ci.org/teodorpatras/Jukebox) [![Version](https://img.shields.io/cocoapods/v/Jukebox.svg?style=flat)](http://cocoapods.org/pods/Jukebox) diff --git a/Source/Jukebox.swift b/Source/Jukebox.swift index b7d0200..6c1766d 100644 --- a/Source/Jukebox.swift +++ b/Source/Jukebox.swift @@ -90,7 +90,7 @@ extension Jukebox { invalidatePlayback() state = .ready UIApplication.shared.endBackgroundTask(backgroundIdentifier) - backgroundIdentifier = UIBackgroundTaskInvalid + backgroundIdentifier = UIBackgroundTaskIdentifier.invalid } /** @@ -135,11 +135,15 @@ extension Jukebox { */ public func seek(toSecond second: Int, shouldPlay: Bool = false) { guard let player = player, let item = currentItem else {return} - - player.seek(to: CMTimeMake(Int64(second), 1)) + player.seek(to: CMTimeMake(value: Int64(second), timescale: 1)) + player.play() item.update() if shouldPlay { - player.play() + if #available(iOS 10.0, *) { + player.playImmediately(atRate: 1.0) + } else { + player.play() + } if state != .playing { state = .playing } @@ -167,7 +171,7 @@ extension Jukebox { - parameter item: item to be removed */ public func remove(item: JukeboxItem) { - if let index = queuedItems.index(where: {$0.identifier == item.identifier}) { + if let index = queuedItems.firstIndex(where: {$0.identifier == item.identifier}) { queuedItems.remove(at: index) } } @@ -221,7 +225,7 @@ open class Jukebox: NSObject, JukeboxItemDelegate { fileprivate var player : AVPlayer? fileprivate var progressObserver : AnyObject! - fileprivate var backgroundIdentifier = UIBackgroundTaskInvalid + fileprivate var backgroundIdentifier = UIBackgroundTaskIdentifier.invalid fileprivate(set) open weak var delegate : JukeboxDelegate? fileprivate (set) open var playIndex = 0 @@ -297,7 +301,7 @@ open class Jukebox: NSObject, JukeboxItemDelegate { func jukeboxItemDidLoadPlayerItem(_ item: JukeboxItem) { delegate?.jukeboxDidLoadItem(self, item: item) - let index = queuedItems.index{$0 === item} + let index = queuedItems.firstIndex{$0 === item} guard let playItem = item.playerItem , state == .loading && playIndex == index else {return} @@ -418,9 +422,9 @@ open class Jukebox: NSObject, JukeboxItemDelegate { fileprivate func startProgressTimer(){ guard let player = player , player.currentItem?.duration.isValid == true else {return} - progressObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(0.05, Int32(NSEC_PER_SEC)), queue: nil, using: { [unowned self] (time : CMTime) -> Void in + progressObserver = player.addPeriodicTimeObserver(forInterval: CMTimeMakeWithSeconds(0.05, preferredTimescale: Int32(NSEC_PER_SEC)), queue: nil, using: { [unowned self] (time : CMTime) -> Void in self.timerAction() - }) as AnyObject! + }) as AnyObject? } fileprivate func stopProgressTimer() { @@ -436,47 +440,48 @@ open class Jukebox: NSObject, JukeboxItemDelegate { fileprivate func configureBackgroundAudioTask() { backgroundIdentifier = UIApplication.shared.beginBackgroundTask (expirationHandler: { () -> Void in UIApplication.shared.endBackgroundTask(self.backgroundIdentifier) - self.backgroundIdentifier = UIBackgroundTaskInvalid + self.backgroundIdentifier = UIBackgroundTaskIdentifier.invalid }) } fileprivate func configureAudioSession() throws { - try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback) - try AVAudioSession.sharedInstance().setMode(AVAudioSessionModeDefault) + try AVAudioSession.sharedInstance().setCategory(AVAudioSession.Category.playback) + try AVAudioSession.sharedInstance().setMode(AVAudioSession.Mode.default) try AVAudioSession.sharedInstance().setActive(true) } fileprivate func configureObservers() { NotificationCenter.default.addObserver(self, selector: #selector(Jukebox.handleStall), name: NSNotification.Name.AVPlayerItemPlaybackStalled, object: nil) - NotificationCenter.default.addObserver(self, selector: #selector(handleAudioSessionInterruption), name: NSNotification.Name.AVAudioSessionInterruption, object: AVAudioSession.sharedInstance()) + NotificationCenter.default.addObserver(self, selector: #selector(handleAudioSessionInterruption), name: AVAudioSession.interruptionNotification, object: AVAudioSession.sharedInstance()) } + // MARK:- Notifications - - func handleAudioSessionInterruption(_ notification : Notification) { + @objc func handleAudioSessionInterruption(_ notification : Notification) { guard let userInfo = notification.userInfo as? [String: AnyObject] else { return } guard let rawInterruptionType = userInfo[AVAudioSessionInterruptionTypeKey] as? NSNumber else { return } - guard let interruptionType = AVAudioSessionInterruptionType(rawValue: rawInterruptionType.uintValue) else { return } + guard let interruptionType = AVAudioSession.InterruptionType(rawValue: rawInterruptionType.uintValue) else { return } switch interruptionType { case .began: //interruption started self.pause() case .ended: //interruption ended if let rawInterruptionOption = userInfo[AVAudioSessionInterruptionOptionKey] as? NSNumber { - let interruptionOption = AVAudioSessionInterruptionOptions(rawValue: rawInterruptionOption.uintValue) - if interruptionOption == AVAudioSessionInterruptionOptions.shouldResume { + let interruptionOption = AVAudioSession.InterruptionOptions(rawValue: rawInterruptionOption.uintValue) + if interruptionOption == AVAudioSession.InterruptionOptions.shouldResume { self.resumePlayback() } } } } - func handleStall() { + @objc func handleStall() { player?.pause() player?.play() } - func playerItemDidPlayToEnd(_ notification : Notification){ + @objc func playerItemDidPlayToEnd(_ notification : Notification){ if playIndex >= queuedItems.count - 1 { stop() } else { diff --git a/Source/JukeboxItem.swift b/Source/JukeboxItem.swift index 0157bfa..5b0ad9c 100644 --- a/Source/JukeboxItem.swift +++ b/Source/JukeboxItem.swift @@ -47,7 +47,7 @@ open class JukeboxItem: NSObject { var delegate: JukeboxItemDelegate? fileprivate var didLoad = false open var localTitle: String? - open let URL: Foundation.URL + public let URL: Foundation.URL fileprivate(set) open var playerItem: AVPlayerItem? fileprivate (set) open var currentTime: Double? @@ -136,7 +136,7 @@ open class JukeboxItem: NSObject { } open override var description: String { - return "" + return "" } // MARK:- Private methods - @@ -161,7 +161,7 @@ open class JukeboxItem: NSObject { timer = Timer.scheduledTimer(timeInterval: 0.5, target: self, selector: #selector(JukeboxItem.notifyDelegate), userInfo: nil, repeats: false) } - func notifyDelegate() { + @objc func notifyDelegate() { timer?.invalidate() timer = nil self.delegate?.jukeboxItemDidUpdate(self) @@ -185,7 +185,7 @@ open class JukeboxItem: NSObject { for item in metadataArray { - item.loadValuesAsynchronously(forKeys: [AVMetadataKeySpaceCommon], completionHandler: { () -> Void in + item.loadValuesAsynchronously(forKeys: [AVMetadataKeySpace.common.rawValue], completionHandler: { () -> Void in self.meta.process(metaItem: item) DispatchQueue.main.async { self.scheduleNotification() @@ -197,17 +197,19 @@ open class JukeboxItem: NSObject { } private extension JukeboxItem.Meta { + mutating func process(metaItem item: AVMetadataItem) { - switch item.commonKey + guard let commonKey = item.commonKey else { return } + switch commonKey { - case "title"? : + case .commonKeyTitle : title = item.value as? String - case "albumName"? : + case .commonKeyAlbumName : album = item.value as? String - case "artist"? : + case .commonKeyArtist : artist = item.value as? String - case "artwork"? : + case .commonKeyArtwork : processArtwork(fromMetadataItem : item) default : break