Skip to main content

Overview

Current Version: 5.8.0 (release notes) (GitHub)

Prefer to start from working code?

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

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:

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())

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:

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

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):

StateMeaning
Uninitializedthe server has not responded yet, so it is not yet known whether music is available
Unavailablethe server has indicated that no streaming music is available to this client
OfflineOnlyonly locally downloaded offline music is available for playback
ReadyToPlaythe player is idle and ready to begin playback
WaitingForItemthe player is waiting for the server to provide the next song
Playingthe player is actively playing a song
Pausedplayback is paused
Stalledaudio data did not arrive in time and the player is buffering
RequestingSkipthe user asked to skip the current song and the player is waiting for the server's permission
Completethere 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:

NotificationPosted when...
FMAudioPlayerPlaybackStateDidChangeNotificationthe player's playbackState changes (playing, paused, stalled, complete, etc.)
FMAudioPlayerCurrentItemDidBeginPlaybackNotificationa new song begins playback — refresh any displayed song metadata here
FMAudioPlayerTimeElapseNotificationplayback time elapses (roughly every half second) — drive progress bars from this
FMAudioPlayerSkipStatusNotificationthe skippability of the current song changes — re-check player.canSkip
FMAudioPlayerSkipFailedNotificationa skip request was denied by the server
FMAudioPlayerLikeStatusChangeNotificationthe like/dislike status of a song changes
FMAudioPlayerActiveStationDidChangeNotificationthe active station changes

In Swift, the notification names are bridged to dotted constants on Notification.Name, e.g. .FMAudioPlayerCurrentItemDidBeginPlayback:

NotificationCenter.default.addObserver(self,
selector: #selector(songChanged(_:)),
name: .FMAudioPlayerCurrentItemDidBeginPlayback,
object: FMAudioPlayer.shared())

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:

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()
}

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.

warning

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.)

FMAudioPlayer.shared().setAVAudioSessionCategory(.playback, mode: .default, options: [])

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::

FMAudioPlayer.shared().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:

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.