Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remote configuration for Data Provider Service URL overrides #540

Merged
merged 9 commits into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tvSiteName": "rsi-player-tvos-apple",
"voiceOverLanguageCode": "it",
"appStoreProductIdentifier": 920753497,
"serviceURL": "https://il.srf.ch",
"playURLs": "{\"rsi\":\"https://www.rsi.ch/play/\",\"rtr\":\"https://www.rtr.ch/play/\",\"rts\":\"https://www.rts.ch/play/\",\"srf\":\"https://www.srf.ch/play/\",\"swi\":\"https://play.swissinfo.ch/play/\"}",
"playServiceURL": "https://www.rsi.ch/play/",
"middlewareURL": "https://playfff.herokuapp.com",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
"siteName": "rtr-player-ios-v",
"tvSiteName": "rtr-player-tvos-apple",
"appStoreProductIdentifier": 920754925,
"serviceURL": "https://il.srf.ch",
"playURLs": "{\"rsi\":\"https://www.rsi.ch/play/\",\"rtr\":\"https://www.rtr.ch/play/\",\"rts\":\"https://www.rts.ch/play/\",\"srf\":\"https://www.srf.ch/play/\",\"swi\":\"https://play.swissinfo.ch/play/\"}",
"playServiceURL": "https://www.rtr.ch/play/",
"middlewareURL": "https://playfff.herokuapp.com",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tvSiteName": "rts-player-tvos-apple",
"voiceOverLanguageCode": "fr",
"appStoreProductIdentifier": 920754415,
"serviceURL": "https://il.srf.ch",
"playURLs": "{\"rsi\":\"https://www.rsi.ch/play/\",\"rtr\":\"https://www.rtr.ch/play/\",\"rts\":\"https://www.rts.ch/play/\",\"srf\":\"https://www.srf.ch/play/\",\"swi\":\"https://play.swissinfo.ch/play/\"}",
"playServiceURL": "https://www.rts.ch/play/",
"middlewareURL": "https://playfff.herokuapp.com",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tvSiteName": "srf-player-tvos-apple",
"voiceOverLanguageCode": "de",
"appStoreProductIdentifier": 638194352,
"serviceURL": "https://il.srf.ch",
"playURLs": "{\"rsi\":\"https://www.rsi.ch/play/\",\"rtr\":\"https://www.rtr.ch/play/\",\"rts\":\"https://www.rts.ch/play/\",\"srf\":\"https://www.srf.ch/play/\",\"swi\":\"https://play.swissinfo.ch/play/\"}",
"playServiceURL": "https://www.srf.ch/play/",
"middlewareURL": "https://playfff.herokuapp.com",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"tvSiteName": "swi-player-tvos-apple",
"voiceOverLanguageCode": "en",
"appStoreProductIdentifier": 920785201,
"serviceURL": "https://il.srf.ch",
"playURLs": "{\"rsi\":\"https://www.rsi.ch/play/\",\"rtr\":\"https://www.rtr.ch/play/\",\"rts\":\"https://www.rts.ch/play/\",\"srf\":\"https://www.srf.ch/play/\",\"swi\":\"https://play.swissinfo.ch/play/\"}",
"playServiceURL": "https://play.swissinfo.ch/play/",
"middlewareURL": "https://playfff.herokuapp.com",
Expand Down
6 changes: 3 additions & 3 deletions Application/Sources/Application/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(

PlayApplicationRunOnce(^(void (^completionHandler)(BOOL success)) {
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
[userDefaults removeObjectForKey:PlaySRGSettingServiceIdentifier];
[userDefaults removeObjectForKey:PlaySRGSettingServiceEnvironment];
[userDefaults synchronize];
completionHandler(YES);
}, @"DataProviderServiceURLChange");
Expand All @@ -122,7 +122,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(

#if defined(DEBUG) || defined(NIGHTLY) || defined(BETA)
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
[defaults addObserver:self forKeyPath:PlaySRGSettingServiceIdentifier options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingServiceEnvironment options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingUserLocation options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
#endif

Expand Down Expand Up @@ -379,7 +379,7 @@ - (void)userDidLogout:(NSNotification *)notification
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (s_kvoContext == context) {
if ([keyPath isEqualToString:PlaySRGSettingServiceIdentifier] || [keyPath isEqualToString:PlaySRGSettingUserLocation]) {
if ([keyPath isEqualToString:PlaySRGSettingServiceEnvironment] || [keyPath isEqualToString:PlaySRGSettingUserLocation]) {
id oldValue = change[NSKeyValueChangeOldKey];
id newValue = change[NSKeyValueChangeNewKey];

Expand Down
8 changes: 4 additions & 4 deletions Application/Sources/Application/SceneDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session op

#if defined(DEBUG) || defined(NIGHTLY) || defined(BETA)
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
[defaults addObserver:self forKeyPath:PlaySRGSettingServiceIdentifier options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingServiceEnvironment options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingUserLocation options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingPosterImages options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
[defaults addObserver:self forKeyPath:PlaySRGSettingSquareImages options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:s_kvoContext];
Expand All @@ -70,7 +70,7 @@ - (void)sceneDidDisconnect:(UIScene *)scene
{
#if defined(DEBUG) || defined(NIGHTLY) || defined(BETA)
NSUserDefaults *defaults = NSUserDefaults.standardUserDefaults;
[defaults removeObserver:self forKeyPath:PlaySRGSettingServiceIdentifier];
[defaults removeObserver:self forKeyPath:PlaySRGSettingServiceEnvironment];
[defaults removeObserver:self forKeyPath:PlaySRGSettingUserLocation];
[defaults removeObserver:self forKeyPath:PlaySRGSettingPosterImages];
[defaults removeObserver:self forKeyPath:PlaySRGSettingSquareImages];
Expand Down Expand Up @@ -110,7 +110,7 @@ - (void)handleDeepLinkAction:(DeepLinkAction *)action
if (! [serviceIdentifier isEqual:ApplicationSettingServiceIdentifier()]) {
ApplicationSettingSetServiceIdentifier(serviceIdentifier);

NSString *serviceName = [ServiceObjC nameForServiceId:serviceIdentifier];
NSString *serviceName = [ServiceObjC nameForEnvironment:serviceIdentifier];
[Banner showWith:BannerStyleInfo
message:[NSString stringWithFormat:NSLocalizedString(@"Server changed to '%@'", @"Notification message when the server URL changed due to a custom URL."), serviceName]
image:[UIImage imageNamed:@"settings"]
Expand Down Expand Up @@ -560,7 +560,7 @@ - (void)openSectionUid:(NSString *)sectionUid
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (s_kvoContext == context) {
if ([keyPath isEqualToString:PlaySRGSettingServiceIdentifier] || [keyPath isEqualToString:PlaySRGSettingUserLocation] || [keyPath isEqualToString:PlaySRGSettingPosterImages] || [keyPath isEqualToString:PlaySRGSettingSquareImages] || [keyPath isEqualToString:PlaySRGSettingAudioHomepageOption]) {
if ([keyPath isEqualToString:PlaySRGSettingServiceEnvironment] || [keyPath isEqualToString:PlaySRGSettingUserLocation] || [keyPath isEqualToString:PlaySRGSettingPosterImages] || [keyPath isEqualToString:PlaySRGSettingSquareImages] || [keyPath isEqualToString:PlaySRGSettingAudioHomepageOption]) {
// Entirely reload the view controller hierarchy to ensure all configuration changes are reflected in the
// user interface. Scheduled for the next run loop to have the same code in the app delegate (updating the
// data provider) executed first.
Expand Down
4 changes: 3 additions & 1 deletion Application/Sources/Configuration/ApplicationConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,10 @@ OBJC_EXPORT NSString * const ApplicationConfigurationDidChangeNotification;

@property (nonatomic, readonly, copy) NSNumber *appStoreProductIdentifier;

@property (nonatomic, readonly, nullable) NSURL *serviceURL;
@property (nonatomic, readonly) NSURL *playServiceURL;
@property (nonatomic, readonly) NSURL *dataProviderProductionServiceURL;
@property (nonatomic, readonly) NSURL *dataProviderStageServiceURL;
@property (nonatomic, readonly) NSURL *dataProviderTestServiceURL;
@property (nonatomic, readonly) NSURL *middlewareURL;
@property (nonatomic, readonly, nullable) NSURL *identityWebserviceURL;
@property (nonatomic, readonly, nullable) NSURL *identityWebsiteURL;
Expand Down
33 changes: 28 additions & 5 deletions Application/Sources/Configuration/ApplicationConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,11 @@ @interface ApplicationConfiguration ()

@property (nonatomic, copy) NSNumber *appStoreProductIdentifier;

@property (nonatomic) NSURL *serviceURL;
@property (nonatomic) NSDictionary<NSNumber *, NSURL *> *playURLs;
@property (nonatomic) NSURL *playServiceURL;
@property (nonatomic) NSURL *dataProviderProductionServiceURL;
@property (nonatomic) NSURL *dataProviderStageServiceURL;
@property (nonatomic) NSURL *dataProviderTestServiceURL;
@property (nonatomic) NSURL *middlewareURL;
@property (nonatomic) NSURL *identityWebserviceURL;
@property (nonatomic) NSURL *identityWebsiteURL;
Expand Down Expand Up @@ -237,6 +239,21 @@ - (instancetype)init

#pragma mark Getters and setters

- (NSURL *)dataProviderProductionServiceURL
{
return _dataProviderProductionServiceURL ?: SRGIntegrationLayerProductionServiceURL();
}

- (NSURL *)dataProviderStageServiceURL
{
return _dataProviderStageServiceURL ?: SRGIntegrationLayerStagingServiceURL();
}

- (NSURL *)dataProviderTestServiceURL
{
return _dataProviderTestServiceURL ?: SRGIntegrationLayerTestServiceURL();
}

- (BOOL)isContinuousPlaybackAvailable
{
#if TARGET_OS_IOS
Expand Down Expand Up @@ -449,10 +466,16 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(PlayFirebaseConfiguration *)fireba
//

self.voiceOverLanguageCode = [firebaseConfiguration stringForKey:@"voiceOverLanguageCode"];

NSString *serviceURLString = [firebaseConfiguration stringForKey:@"serviceURL"];
self.serviceURL = serviceURLString ? [NSURL URLWithString:serviceURLString] : nil;


NSString *dataProviderProductionServiceURLString = [firebaseConfiguration stringForKey:@"dataProviderProductionServiceURL"];
self.dataProviderProductionServiceURL = dataProviderProductionServiceURLString ? [NSURL URLWithString:dataProviderProductionServiceURLString] : nil;

NSString *dataProviderStageServiceURLString = [firebaseConfiguration stringForKey:@"dataProviderStageServiceURL"];
self.dataProviderStageServiceURL = dataProviderStageServiceURLString ? [NSURL URLWithString:dataProviderStageServiceURLString] : nil;

NSString *dataProviderTestServiceURLString = [firebaseConfiguration stringForKey:@"dataProviderTestServiceURL"];
self.dataProviderTestServiceURL = dataProviderTestServiceURLString ? [NSURL URLWithString:dataProviderTestServiceURLString] : nil;

NSString *identityWebserviceURLString = [firebaseConfiguration stringForKey:@"identityWebserviceURL"];
self.identityWebserviceURL = identityWebserviceURLString ? [NSURL URLWithString:identityWebserviceURLString] : nil;

Expand Down
6 changes: 3 additions & 3 deletions Application/Sources/Settings/ApplicationSettings+Common.m
Original file line number Diff line number Diff line change
Expand Up @@ -197,19 +197,19 @@ void ApplicationSettingSetLastSelectedAudioLanguageCode(NSString *languageCode)

NSString *ApplicationSettingServiceIdentifier(void)
{
return [NSUserDefaults.standardUserDefaults stringForKey:PlaySRGSettingServiceIdentifier];
return [NSUserDefaults.standardUserDefaults stringForKey:PlaySRGSettingServiceEnvironment];
}

void ApplicationSettingSetServiceIdentifier(NSString *identifier)
{
NSUserDefaults *userDefaults = NSUserDefaults.standardUserDefaults;
[userDefaults setObject:identifier forKey:PlaySRGSettingServiceIdentifier];
[userDefaults setObject:identifier forKey:PlaySRGSettingServiceEnvironment];
[userDefaults synchronize];
}

NSURL *ApplicationSettingServiceURL(void)
{
return [ServiceObjC urlForServiceId:ApplicationSettingServiceIdentifier()];
return [ServiceObjC urlForEnvironment:ApplicationSettingServiceIdentifier()];
}

BOOL ApplicationSettingAutoplayEnabled(void)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ OBJC_EXPORT NSString * const PlaySRGSettingAudioDescriptionAvailabilityDisplayed
OBJC_EXPORT NSString * const PlaySRGSettingDiscoverySubtitleOptionLanguageRunOnce;
OBJC_EXPORT NSString * const PlaySRGSettingLastLoggedInEmailAddress;
OBJC_EXPORT NSString * const PlaySRGSettingSelectedLivestreamURNForChannels;
OBJC_EXPORT NSString * const PlaySRGSettingServiceIdentifier;
OBJC_EXPORT NSString * const PlaySRGSettingServiceEnvironment;
OBJC_EXPORT NSString * const PlaySRGSettingUserLocation;
OBJC_EXPORT NSString * const PlaySRGSettingUserConsentAcceptedServiceIds;
#if defined(DEBUG) || defined(NIGHTLY) || defined(BETA)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
NSString * const PlaySRGSettingDiscoverySubtitleOptionLanguageRunOnce = @"PlaySRGSettingDiscoverySubtitleOptionLanguageRunOnce";
NSString * const PlaySRGSettingLastLoggedInEmailAddress = @"PlaySRGSettingLastLoggedInEmailAddress";
NSString * const PlaySRGSettingSelectedLivestreamURNForChannels = @"PlaySRGSettingSelectedLivestreamURNForChannels";
NSString * const PlaySRGSettingServiceIdentifier = @"PlaySRGSettingServiceIdentifier";
NSString * const PlaySRGSettingServiceEnvironment = @"PlaySRGSettingServiceEnvironment";
NSString * const PlaySRGSettingUserLocation = @"PlaySRGSettingUserLocation";
NSString * const PlaySRGSettingUserConsentAcceptedServiceIds = @"PlaySRGSettingUserConsentAcceptedServiceIds";
#if defined(DEBUG) || defined(NIGHTLY) || defined(BETA)
Expand Down
115 changes: 61 additions & 54 deletions Application/Sources/Settings/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5,84 +5,91 @@
//

import Foundation
import SRGDataProvider

struct Service: Identifiable, Equatable {
let id: String
let name: String
let url: URL
enum Service: String, Identifiable, CaseIterable {
case production
case stage
case test
case mmf = "play mmf"
case samProduction = "sam production"
case samStage = "sam stage"
case samTest = "sam test"

static var production = Self(
id: "production",
name: NSLocalizedString("Production", comment: "Server setting name"),
url: SRGIntegrationLayerProductionServiceURL()
)

static var stage = Self(
id: "stage",
name: NSLocalizedString("Stage", comment: "Server setting name"),
url: SRGIntegrationLayerStagingServiceURL()
)

static var test = Self(
id: "test",
name: NSLocalizedString("Test", comment: "Server setting name"),
url: SRGIntegrationLayerTestServiceURL()
)

static var mmf = Self(
id: "play mmf",
name: "Play MMF",
url: mmfUrl
)

private static var mmfUrl: URL = {
private static var mmfUrl: URL {
guard let mmfUrlString = Bundle.main.object(forInfoDictionaryKey: "PlayMMFServiceURL") as? String,
!mmfUrlString.isEmpty
else {
return URL(string: "https://play-mmf.herokuapp.com")!
}
return URL(string: mmfUrlString)!
}()
}

static var samProduction = Self(
id: "sam production",
name: "SAM \(NSLocalizedString("Production", comment: "Server setting name"))",
url: SRGIntegrationLayerProductionServiceURL().appendingPathComponent("sam")
)
var id: Self {
self
}

static var samStage = Self(
id: "sam stage",
name: "SAM \(NSLocalizedString("Stage", comment: "Server setting name"))",
url: SRGIntegrationLayerStagingServiceURL().appendingPathComponent("sam")
)
var environment: String {
rawValue
}

static var samTest = Self(
id: "sam test",
name: "SAM \(NSLocalizedString("Test", comment: "Server setting name"))",
url: SRGIntegrationLayerTestServiceURL().appendingPathComponent("sam")
)
var name: String {
switch self {
case .production:
NSLocalizedString("Production", comment: "Server setting name")
case .stage:
NSLocalizedString("Stage", comment: "Server setting name")
case .test:
NSLocalizedString("Test", comment: "Server setting name")
case .mmf:
"Play MMF"
case .samProduction:
"SAM \(NSLocalizedString("Production", comment: "Server setting name"))"
case .samStage:
"SAM \(NSLocalizedString("Stage", comment: "Server setting name"))"
case .samTest:
"SAM \(NSLocalizedString("Test", comment: "Server setting name"))"
}
}

static var services: [Self] = [production, stage, test, mmf, samProduction, samStage, samTest]
var url: URL {
switch self {
case .production:
ApplicationConfiguration().dataProviderProductionServiceURL
case .stage:
ApplicationConfiguration().dataProviderStageServiceURL
case .test:
ApplicationConfiguration().dataProviderTestServiceURL
case .mmf:
Self.mmfUrl
case .samProduction:
ApplicationConfiguration().dataProviderProductionServiceURL.appendingPathComponent("sam")
case .samStage:
ApplicationConfiguration().dataProviderStageServiceURL.appendingPathComponent("sam")
case .samTest:
ApplicationConfiguration().dataProviderTestServiceURL.appendingPathComponent("sam")
}
}

static func service(forId id: String?) -> Self {
static func service(for environment: String?) -> Self {
#if DEBUG || NIGHTLY || BETA
guard let id, let server = services.first(where: { $0.id == id }) else {
guard let environment, let service = Self(rawValue: environment) else {
return .production
}
return server
return service
#else
return .production
#endif
}
}

@objc class ServiceObjC: NSObject {
@objc static func name(forServiceId serviceId: String) -> String {
Service.service(forId: serviceId).name
@objc static var environments = Service.allCases.map(\.environment)

@objc static func name(forEnvironment environment: String) -> String {
Service.service(for: environment).name
}

@objc static func url(forServiceId serviceId: String) -> URL {
ApplicationConfiguration().serviceURL ?? Service.service(forId: serviceId).url
@objc static func url(forEnvironment environment: String) -> URL {
Service.service(for: environment).url
}
}
Loading