Overview
Current Version: 5.8.0 (release notes) (GitHub)
You can go straight to the demo iOS app — a small, readable reference integration showing SDK setup, playback, and lock-screen support.
Our iOS SDK is written in Objective-C and works on recent versions of iOS and tvOS. The iOS SDK is kept up to date with all our latest features and handles integration with the iOS lock screen for background playback, if desired.
Installation
As of version 5.8.0, the library is distributed exclusively through Swift Package Manager (CocoaPods and Carthage are no longer supported). To add Feed Media to your project:
- Select File > Add Package Dependencies. Enter https://github.com/feedfm/iOS-SDK in the search field.
- In the next page, specify the version resolving rule as "Up to Next Major" with "5.8.0" as its earliest version.
- After Xcode checks out the source and resolves the version, you can choose the "FeedMedia" library and add it to your app target.
The iOS SDK contains MarqueeLabel library for a marquee effect on long UILabels that show metadata.
Concepts
The SDK centers around a singleton instance of this FMAudioPlayer class, which has simple methods to control music playback (play, pause, skip). The FMAudioPlayer holds a list of FMStation objects (stationList), one of which is always considered the active station (activeStation). Once music playback has begun, there is a current song (currentItem).
Getting started
Typical initialization and setup is as follows:
As early as you can in your app’s lifecycle (preferably in your AppDelegate or initial ViewController) call
- Swift
- Objective-C
FMAudioPlayer.setClientToken("demo", secret: "demo")
[FMAudioPlayer setClientToken:@"demo" secret:@"demo"];
to asynchronously contact the feed.fm servers, validate that the client is in a location that can legally play music, and then retrieve a list of available music stations.
Your token and secret values are provided to you when you sign up with Feed.fm, and will give your app access to your custom stations. Until you have them, you can use the "demo" string above, or one of the strings listed in the Testing credentials section below.
To receive notice that music is available or not available, use the whenAvailable:notAvailable: method call, which is guaranteed to call only one of its arguments as soon as music is deemed available or not:
- Swift
- Objective-C
let player = FMAudioPlayer.shared()
player.whenAvailable({
print("music is available!")
// .. do something, now that you know music is available
// set player settings
player.secondsOfCrossfade = 4
player.play()
}, notAvailable: {
print("music is not available!")
// .. do something, like leave music button hidden
})
// Set Notifications for ex to listen for player events
NotificationCenter.default.addObserver(self,
selector: #selector(stateDidChange(_:)),
name: .FMAudioPlayerPlaybackStateDidChange,
object: FMAudioPlayer.shared())
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
[player whenAvailable:^{
NSLog(@"music is available!");
// .. do something, now that you know music is available
// set player settings
player.secondsOfCrossfade = 4;
[player play];
} notAvailable: ^{
NSLog(@"music is not available!");
// .. do something, like leave music button hidden
}];
// Set Notifications for ex to listen for player events
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(stateDidChange:) name:FMAudioPlayerPlaybackStateDidChangeNotification object:[FMAudioPlayer sharedPlayer]];
Testing credentials
There are a number of sample credentials you can use to assist in testing your app out. Use one of the following strings as both your token and secret to get the desired behavior:
demo - 2 simple stations with no skip limits
badgeo - feed.fm will treat this client as if it were accessing from a location with no available music stations
counting - this will return a single a station that plays very short audio clips of a voice saying the numbers 0 through 9
Controlling playback
Once the player is available, playback is controlled with simple methods on the shared FMAudioPlayer:
- Swift
- Objective-C
let player = FMAudioPlayer.shared()
player.play() // begin or resume playback in the active station
player.pause() // pause playback
player.skip() // ask to skip the current song
player.like() // mark the current song as liked
player.dislike() // mark the current song as disliked
player.unlike() // remove a like or dislike
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
[player play]; // begin or resume playback in the active station
[player pause]; // pause playback
[player skip]; // ask to skip the current song
[player like]; // mark the current song as liked
[player dislike]; // mark the current song as disliked
[player unlike]; // remove a like or dislike
Skips must be granted by the Feed.fm servers (users are limited in the number of songs they may skip
per hour in some stations). Check the player's canSkip property to decide whether to enable a skip
button, and listen for the FMAudioPlayerSkipFailedNotification notification (below) to learn when
a skip request was denied.
While music is playing, the current song is exposed as player.currentItem, an FMAudioItem with
name, artist, album, and duration properties for display, along with liked and disliked
booleans that reflect any feedback the user has given. player.currentPlaybackTime returns the
elapsed playback time of the current song.
Player state and events
At any moment the player is in one of a fixed set of states, exposed as player.playbackState
(an FMAudioPlayerPlaybackState, bridged to dotted constants like .readyToPlay in Swift):
| State | Meaning |
|---|---|
Uninitialized | the server has not responded yet, so it is not yet known whether music is available |
Unavailable | the server has indicated that no streaming music is available to this client |
OfflineOnly | only locally downloaded offline music is available for playback |
ReadyToPlay | the player is idle and ready to begin playback |
WaitingForItem | the player is waiting for the server to provide the next song |
Playing | the player is actively playing a song |
Paused | playback is paused |
Stalled | audio data did not arrive in time and the player is buffering |
RequestingSkip | the user asked to skip the current song and the player is waiting for the server's permission |
Complete | there is no more music to play in the current station |
Rather than being polled, the player announces every change in its state by posting notifications to
the default NSNotificationCenter, with the player as the notification object. The notifications an
app will most commonly observe are:
| Notification | Posted when... |
|---|---|
FMAudioPlayerPlaybackStateDidChangeNotification | the player's playbackState changes (playing, paused, stalled, complete, etc.) |
FMAudioPlayerCurrentItemDidBeginPlaybackNotification | a new song begins playback — refresh any displayed song metadata here |
FMAudioPlayerTimeElapseNotification | playback time elapses (roughly every half second) — drive progress bars from this |
FMAudioPlayerSkipStatusNotification | the skippability of the current song changes — re-check player.canSkip |
FMAudioPlayerSkipFailedNotification | a skip request was denied by the server |
FMAudioPlayerLikeStatusChangeNotification | the like/dislike status of a song changes |
FMAudioPlayerActiveStationDidChangeNotification | the active station changes |
In Swift, the notification names are bridged to dotted constants on Notification.Name, e.g.
.FMAudioPlayerCurrentItemDidBeginPlayback:
- Swift
- Objective-C
NotificationCenter.default.addObserver(self,
selector: #selector(songChanged(_:)),
name: .FMAudioPlayerCurrentItemDidBeginPlayback,
object: FMAudioPlayer.shared())
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(songChanged:)
name:FMAudioPlayerCurrentItemDidBeginPlaybackNotification
object:[FMAudioPlayer sharedPlayer]];
Stations and station metadata
After the player becomes available, player.stationList holds FMStation instances representing a
subset of the music stations available to the client, and player.activeStation is the station music
is currently drawn from. To present a station picker, render the list and make the user's choice
active:
- Swift
- Objective-C
let player = FMAudioPlayer.shared()
for case let station as FMStation in player.stationList {
print("\(station.name) (\(station.identifier))")
}
// make a station active, then start playback in it
if let station = player.stationList.first as? FMStation {
player.setActiveStation(station, withCrossfade: false)
player.play()
}
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
for (FMStation *station in player.stationList) {
NSLog(@"%@ (%@)", station.name, station.identifier);
}
// make a station active, then start playback in it
FMStation *station = player.stationList.firstObject;
[player setActiveStation:station withCrossfade:NO];
[player play];
Every station has a name, a unique identifier, and an options dictionary containing any custom
station-level metadata configured for your account — for example, a subheader description line or a
background_image_url pointing to station artwork. Contact your Customer Success Manager to attach
custom metadata to your stations.
Because stationList holds only a subset of the stations available to your account, the SDK also
provides station search functions that query the full set of stations on the Feed.fm servers.
searchForAndSetActiveStation finds a station by type and attribute filters, makes it the active
station, and optionally begins buffering audio for immediate playback — all in a single network
request. See the Station Search recipe for details.
Station identifiers (such as tempId) are not guaranteed to be stable across application restarts,
so do not persist them (for instance, to remember the user's last station). You should use a station's
name or some player.option value, which can be searched for with
player.stationList.getStationWithOption("my-id", savedMyId).
Audio Session notes
The SDK is configured, by default, to create an AVAudioSession with the category AVAudioSessionCategoryPlayback, the mode
AVAudioSessionModeDefault, and the category option AVAudioSessionCategoryOptionDuckOthers (as of SDK 5.6.2; earlier versions used
AVAudioSessionCategoryOptionMixWithOthers). This allows the SDK to play audio in the background alongside other audio sources,
lowering its volume while Feed.fm music plays. If you are only playing Feed.fm music in your app, and you want the music to appear
in and be controlled via the lock screen, then the category option must be set to 0. This can be done by calling
setAVAudioSessionCategory:mode:options:. (The simulcast player is an exception: it defaults to AVAudioSessionCategoryOptionMixWithOthers.)
- Swift
- Objective-C
FMAudioPlayer.shared().setAVAudioSessionCategory(.playback, mode: .default, options: [])
[[FMAudioPlayer sharedPlayer] setAVAudioSessionCategory:AVAudioSessionCategoryPlayback
mode:AVAudioSessionModeDefault
options:0];
Background playback and the lock screen
To keep music playing when your app is backgrounded or the screen is locked, your app must declare
the audio background mode. In Xcode, select your app target, open Signing & Capabilities, add the
Background Modes capability, and check Audio, AirPlay, and Picture in Picture — or add the key to
your Info.plist directly:
<key>UIBackgroundModes</key>
<array>
<string>audio</string>
</array>
The SDK takes care of the rest of the lock-screen "Now Playing" experience: it populates the
MPNowPlayingInfoCenter with the current song's title, artist, and album, and registers with the
MPRemoteCommandCenter to handle play/pause/skip/like/dislike commands from the lock screen and
control center. (Remember that the audio session category options must be 0 for your app to appear
in the Now Playing interface, as described above.)
The one thing the SDK cannot provide is artwork. Assign an image to display on the lock screen with
setLockScreenImage::
- Swift
- Objective-C
FMAudioPlayer.shared().setLockScreenImage(stationArtwork)
[[FMAudioPlayer sharedPlayer] setLockScreenImage:stationArtwork];
If your app plays other audio besides Feed.fm music and you want to manage the Now Playing metadata
or remote commands yourself, set the player's doesHandleRemoteCommands property to NO and/or
assign your own lockScreenDelegate — see the
FMAudioPlayer reference for details.
Logging
While developing, you can ask the SDK to log its activity to the console, which is helpful when diagnosing why music isn't starting:
- Swift
- Objective-C
FMLogSetLevel(FMLogLevelDebug)
FMLogSetLevel(FMLogLevelDebug);
Available levels are FMLogLevelNone (the default), FMLogLevelError, FMLogLevelWarn, and
FMLogLevelDebug.
Demo apps
A fully functional demo app is available on GitHub: iOS SDK Demo, a reference integration — a small, readable SwiftUI app that lists the available stations, plays/pauses/skips tracks, likes/dislikes the current song, and shows how lock-screen controls, artwork, and background audio are wired up.
Reference docs
iOS Reference docs are available here, for the ten most recent releases.