This getting started guide discusses all concepts required to use the SRG Data Provider library.
At its core the SRG Data Provider library reduces to a single data provider class, SRGDataProvider
, which you instantiate for a service URL, for example in Swift:
let dataProvider = SRGDataProvider(serviceURL: SRGIntegrationLayerProductionServiceURL())
or in Objective-C:
SRGDataProvider *dataProvider = [[SRGDataProvider alloc] initWithServiceURL:SRGIntegrationLayerProductionServiceURL()];
A set of constants for common service URLs (production, staging and test) is provided. You can have several data providers in an application, though most applications should require only one. To make it easier to access the main data provider of an application, the SRGDataProvider
class provides a class property to set and retrive it as shared instance in in Swift:
SRGDataProvider.current = SRGDataProvider(serviceURL: SRGIntegrationLayerProductionServiceURL())
or in Objective-C:
SRGDataProvider.currentDataProvider = [[SRGDataProvider alloc] initWithServiceURL:SRGIntegrationLayerProductionServiceURL()];
For simplicity this getting started guide assumes that a shared data provider has been set. Note that all requests associated with a data provider will be cancelled if it gets deallocated. If you cannot or don't want to use the shared instance, you must therefore store the data providers you instantiated somewhere and provide access to them in some other way.
The SRGDataProviderCombine
library provides Combine publishers for each supported request.
When subscribing to an SRG Data Provider publisher the corresponding request is performed and results are delivered to the associated pipeline.
SRG Data Provider offers two kinds of publishers:
- Publishers without pagination support.
- Publishers optionally supporting pagination, characterized by a signature containing
paginatedBy
andpageSize
parameters.
For more information about Combine itself, please have a look at the official documentation. The Using Combine book is also a great reference but a steep introduction if you have no prior knowledge of functional and reactive programming.
Using publishers is straightforward. Just obtain the publisher for the request you need and write a corresponding pipeline, for example:
SRGDataProvider.current!.tvLivestreams(for: .SRF)
// Rest of the pipeline
The publisher completes once the results have been delivered or an error has been encountered.
Publishers optionally supporting pagination behave in a similar way when their paginatedBy
parameter is omitted or nil
:
SRGDataProvider.current!.latestMediasForShow(withUrn: "urn:rts:show:tv:532539", pageSize: 50)
// Rest of the pipeline
In this case only a single page of results is returned and the publisher completes. You can use the optional pageSize
parameter to control how much results must be returned at most.
Setting paginatedBy
enables pagination support and lets you control when a new page of results must be loaded. Note that a publisher for which pagination has been enabled will only complete once all pages of results have been exhausted.
Requesting further pages of results from a paginated publisher requires a trigger to be instantiated and stored separately:
let trigger = Trigger()
A trigger defines a local context for communication with a set of publishers. To be able to ask some paginated publisher for a next page of content you must associate it with some Trigger.Signal
, obtained from the trigger ans associated with some integer index:
SRGDataProvider.current!.latestMediasForShow(withUrn: "urn:rts:show:tv:532539", paginatedBy: trigger.signal(activatedBy: 1))
// Rest of the pipeline
The publisher emits the first page of results with the first subscription then waits for the signal. When you need the next page of results simply activate the trigger for the corresponding index:
trigger.activate(for: 1)
If a next page of results is available it will be retrieved and delivered to the same pipeline.
In general you should avoid assigning the same index to several publishers, except if you want to use them together as a group. Assigning indices manually is possible, especially if there is some natural ordering involved, but it is better to use a Hashable
type if possible.
For example if a list of results is displayed in some section
of type:
enum Section: Hashable { /* ... */ }
you can use the section itself as index:
SRGDataProvider.current!.latestMediasForShow(withUrn: "urn:rts:show:tv:532539", paginatedBy: trigger.signal(activatedBy: section))
// Rest of the pipeline
and request the next page of results accordingly:
trigger.activate(for: section)
In general you should have a Trigger
in each local context where you need to control pagination, e.g. in a view model instance. Application-wide triggers must be avoided so that you do not incorrectly assign the same index to unrelated signals throughout your application.
Subscribers receive results in pages, not as a consolidated list. The reason is that you might want to apply additional processing to each page of results, which would be inefficient if results were accumulated with each new page of results.
Fortunately accumulating results delivered by a pipeline is simple. You should use scan
to consolidate results as they are made available, for example:
SRGDataProvider.current!.latestMediasForShow(withUrn: "urn:rts:show:tv:532539", paginatedBy: trigger.signal(activatedBy: 2))
.scan([]) { $0 + $1 }
// Rest of the pipeline
The second example below shows how to search for medias. Search services deliver URN lists, which you can replace with media objects by additionally fetching them, accumulating the results each time a new page of medias has been retrieved:
SRGDataProvider.current!.medias(for: .RTS, matchingQuery: "jour", pageSize: 20, paginatedBy: trigger.signal(activatedBy: 3))
.map { result in
return SRGDataProvider.current!.medias(withUrns: result.mediaUrns, pageSize: 20)
}
.switchToLatest()
.scan([]) { $0 + $1 }
// Rest of the pipeline
Since the publisher only completes when all pages of results have been exhausted you usually want to avoid reducers, as they will prevent results from propagating down the pipeline until all upstream publishers have completed, i.e. until all pages of content have been exhausted.
Subscribers for which pagination has been enabled may fail when retrieving the first page of content, propagating the corresponding error downstream.
If the first page of results could be retrieved, though, and if attempting to load an additional page of content fails, for example because the network connection dropped, the operation will silently fail without delivering any additional page to the pipeline. The operation might be reatttempted using the same trigger and identifier as many times as needed.
The SRGDataProviderNetwork
library returns instances of requests from SRG Network, either simple SRGRequest
, or SRGFirstPageRequest
for services supporting pagination.
For example, retrieving SRF livestreams is achieved as follows in Swift:
let request = SRGDataProvider.current!.tvLivestreams(for: .RTS) { (medias, response, error) in
if let error = error {
// Deal with the error
return
}
// Proceed further, e.g. display the medias
}
request.resume()
or in Objective-C:
SRGRequest *request = [SRGDataProvider.currentDataProvider tvLivestreamsForVendor:SRGVendorSRF withCompletionBlock:^(NSArray<SRGMedia *> * _Nullable medias, NSHTTPURLResponse * _Nullable HTTPResponse, NSError * _Nullable error) {
if (error) {
// Deal with the error
return;
}
// Proceed further, e.g. display the medias
}];
[request resume];
Please carefully read the SRG Network getting started guide, which provides extensive information about request management and grouping via queues.
Images are returned as opaque SRGImage
objects, for which SRGDataProvider
offers instance methods to generate corresponding URLs, in Objective-C:
- (nullable NSURL *)URLForImage:(nullable SRGImage *)image withWidth:(SRGImageWidth)width;
- (nullable NSURL *)URLForImage:(nullable SRGImage *)image withSize:(SRGImageSize)size;
and in Swift:
func url(for image: SRGImage?, width: SRGImageWidth) -> URL?
func url(for image: SRGImage?, size: SRGImageSize) -> URL?
The image API allows you to either generate images based on a finite set of widths (arbitrary widths are not supported) or on a set of semantic sizes (small, medium, large) which should fulfill most needs. Should these semantic sizes not match your needs you are always free to either request images based on some desired widths or to define your own set of semantic sizes.
Dimensions for images of a given width can be retrieved using SRGRecommendedImageWidth
and SRGRecommendedImageCGSize
. This can sometimes be useful if your application precisely needs to adjust some frames based on the size somes images they may contain.
Formatted dates provided as request parameters, as well as data delivered by the SRG Data Provider library, are tightly bound to the Zurich time zone.
In general it makes little sense for applications using SRG SSR data to format dates and times in other time zones. This is why the data provider library provides standard time zone and calendar extensions to access the SRG SSR time zone in a consistent way:
NSTimeZone.srg_defaultTimeZone
in Objective-C andTimeZone.srgDefault
in Swift.NSCalendar.srg_defaultCalendar
in Objective-C andCalendar.srgDefault
in Swift.
Your application is free to format dates returned by the SRG Data Provider library in any time zone, though using the above helpers can deliver a more consistent user experience overall.
Request availability depends on the business unit. Refer to the provided service compatibility matrix for reference. This matrix also provides information about page constraints for services supporting pagination.
In the future, Apple will likely favor HTTPS over HTTP, and require applications to explicitly declare potentially insecure connections. These guidelines are referred to as App Transport Security (ATS).
For information about how you should configure your application to access our services, please refer to the dedicated wiki topic.