Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!
Exploring MusicKit
I wrote this book to fill in the gap for the documentation, filled with examples so that you do not have to spend time experimenting yourself.
I hope this book provides value to you and your app, and you enjoy working with MusicKit and integrating Apple Music into your app!
Use the discount code “early” for a massive 75% discount!
In MusicKit for Swift, Apple provides us with a MusicPlayer
object that our app uses to play music. It has three kinds of enumerations -
PlaybackStatus
- The music player playback status modes.RepeatMode
- The repeat modes for the music player.ShuffleMode
- The shuffle modes for the music player.
In this post, we’ll explore RepeatMode
, and how we can implement it in our app.
RepeatMode is an enum that contains three cases -
- all - Repeating the currently playing collection.
- none - In disabled state, and playback stops after the current item.
- one - Repeating the currently playing entry.
The MusicPlayer
also exposes a State
class, which is an ObservableObject
. We observe the properties of the music player such as playbackStatus
, playbackRate
, repeatMode
and shuffleMode
.
If you take the example of Apple Music, there’s a repeat mode button that you can iterate through to repeat all the songs or a particular one. To create something similar in my app, Musadora, I extended MusicPlayer.RepeatMode
to conform to CaseIterable
. As I’m extending the enum outside the framework, it prevents automatic synthesis of ‘allCases’, a requirement of protocol CaseIterable
. To fix that, I manually add a static variable to the extension -
public static var allCases: [MusicPlayer.RepeatMode] = [.none, .all, .one]
Now, I add a mutating function next()
for circulation iteration of the options. It sets to the first element if it is the last one. Otherwise, increments to the next one.
mutating func next() {
let allCases = Self.allCases
let currentIndex = allCases.index(after: allCases.firstIndex(of: self)!)
self = allCases[currentIndex == allCases.endIndex ? allCases.startIndex : currentIndex]
}
The extension looks like -
extension MusicPlayer.RepeatMode: CaseIterable {
public static var allCases: [MusicPlayer.RepeatMode] = [.none, .all, .one]
mutating func next() {
let allCases = Self.allCases
let currentIndex = allCases.index(after: allCases.firstIndex(of: self)!)
self = allCases[currentIndex == allCases.endIndex ? allCases.startIndex : currentIndex]
}
}
In the NowPlayingPrimaryControlsView
, I observe the value of state -
@ObservedObject private var state = ApplicationMusicPlayer.shared.state
When the user taps on the repeat mode button, the next()
method of the enum is called and based on the response, the image is changed.
Button(action: { state.repeatMode?.next() }) {
Image(systemName: state.repeatMode == .one ? "repeat.1" : "repeat")
.font(.title3)
.padding(12)
.background(state.repeatMode == MusicPlayer.RepeatMode.none ? nil : Circle().opacity(0.1))
}
Here’s how it looks -
Based on the value of the repeatMode
, the MusicPlayer
decides if the song is repeated or not.
You can similarly create the shuffle button as well!
Button(action: { state.shuffleMode?.next() }) {
Image(systemName: "shuffle")
.font(.title3)
.padding(12)
.background(state.shuffleMode == .songs ? Circle().opacity(0.1) : nil)
}
Exploring Freelancing
Navigate freelancing as a developer; find clients, manage contracts, ensure timely payment, and learn from experiences!