Station Search
Overview
As of iOS SDK 5.8.0 and Android SDK 7.1.0, the SDKs can search for a station, set it as the
active station, and optionally begin loading audio for immediate playback — all in a single
network request. This is equivalent to retrieving the station list, picking a station,
calling setActiveStation, and then preparing the player, but without the extra round-trips.
This is the recommended way to start playback of First Play and Replay stations, and is especially useful when you want music to start as quickly as possible after a user action.
A search request contains one or more search queries. Each query may specify:
- type - restrict the match to
radio,first_play, orreplaystations (or leave unset to match any type) - advance (iOS) / at (Android) - the number of seconds into the station at which playback should begin. Only applies to First Play and Replay stations. If a First Play station has already been played past this offset on the device, it will not match. This value is ignored for radio stations.
- filter - a MongoDB-style filter object tested against station attributes
Queries are evaluated in order until a playable station is found, so you can list your preferred match first and fall back to something more general.
Filter syntax
The filter is a map of logical tests against station attributes:
- Equality:
{ "name": "HIIT Workout #1" } - Wildcard substring match:
{ "name": "*Workout*" } - Operators
$in,$nin,$ne,$exists,$and, and$or:{ "class": { "$in": ["hiit", "cardio"] } }
Searching and starting playback
The example below searches for a First Play station whose name contains "Workout", starting
playback 8 minutes (480 seconds) in, and falls back to any radio station if there is no match.
Because prepareToPlay is true, the player begins buffering audio before the callback fires,
so a subsequent play() starts almost instantly.
- Kotlin
- Swift
- Objective-C
val searches = listOf(
StationSearch(
type = StationSearchType.FIRST_PLAY,
at = 480, // begin playback 8 minutes in
filter = mapOf("name" to "*Workout*")
),
// fall back to any radio station
StationSearch(type = StationSearchType.RADIO)
)
player.searchForAndSetActiveStation(
stationSearches = searches,
searchTimeout = 5000, // milliseconds, defaults to 5000
prepareToPlay = true,
prepareTimeout = 5000, // milliseconds, defaults to 5000
callback = object : SearchAndSetActiveStationCallback {
override fun onSuccess(station: Station, play: Play) {
// the station is now active and audio is buffered
player.play()
}
override fun onError(error: FeedFMError) {
// no station matched, the search or prepare timed out,
// or a station change was already in progress
}
}
)
The callback is invoked exactly once, on the main thread. Possible errors include
SEARCH_TIMEOUT (1005), PREPARE_TIMEOUT (1006), and STATION_CHANGE_IN_PROGRESS (1007) —
see the FeedFMError reference.
let queries: [FMStationSearchQuery] = [
.firstPlay(withAdvance: 480, // begin playback 8 minutes in
filter: ["name": "*Workout*"]),
// fall back to any radio station
.radio(withAdvance: 0, filter: nil),
]
let player = FMAudioPlayer.shared()
let dispatched = player.search(
forAndSetActiveStation: queries,
searchTimeoutMs: 2000, // defaults to 2000ms when nil
prepareToPlay: true,
prepareTimeoutMs: 5000 // defaults to 5000ms when nil
) { audioItem, error in
if let error {
// no station matched, or the search or prepare timed out
print("station search failed: \(error)")
return
}
// the station is now active and audio is buffered
player.play()
}
if !dispatched {
// the network is unreachable or the search list was empty;
// the completion block will NOT be invoked
}
The completion block is invoked exactly once, on the main queue. Possible errors include
FeedFMErrorCodeRequestTimeout, FeedFMErrorCodePlayerCouldNotSetStation, and
FeedFMErrorCodePlayerPrepareTimeout — see SDK Error Handling.
If the prepare timeout is exceeded, the station and audio item are still set as active and
audio continues loading in the background.
FMStationSearchQuery *firstPlay =
[FMStationSearchQuery firstPlayWithAdvance:480 // begin playback 8 minutes in
filter:@{ @"name": @"*Workout*" }];
// fall back to any radio station
FMStationSearchQuery *fallback = [FMStationSearchQuery radioWithAdvance:0 filter:nil];
FMAudioPlayer *player = [FMAudioPlayer sharedPlayer];
BOOL dispatched =
[player searchForAndSetActiveStation:@[ firstPlay, fallback ]
searchTimeoutMs:@2000 // defaults to 2000ms when nil
prepareToPlay:YES
prepareTimeoutMs:@5000 // defaults to 5000ms when nil
completion:^(FMAudioItem *_Nullable audioItem,
NSError *_Nullable error) {
if (error) {
// no station matched, or the search or prepare timed out
NSLog(@"station search failed: %@", error);
return;
}
// the station is now active and audio is buffered
[player play];
}];
if (!dispatched) {
// the network is unreachable or the search list was empty;
// the completion block will NOT be invoked
}
The completion block is invoked exactly once, on the main queue. Possible errors include
FeedFMErrorCodeRequestTimeout, FeedFMErrorCodePlayerCouldNotSetStation, and
FeedFMErrorCodePlayerPrepareTimeout — see SDK Error Handling.
If the prepare timeout is exceeded, the station and audio item are still set as active and
audio continues loading in the background.