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

Improve remote configuration for Service URL overrides #540

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion Application/Sources/Application/SceneDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ - (void)handleDeepLinkAction:(DeepLinkAction *)action
if (! [serviceIdentifier isEqual:ApplicationSettingServiceIdentifier()]) {
ApplicationSettingSetServiceIdentifier(serviceIdentifier);

NSString *serviceName = [ServiceObjC nameForServiceId:serviceIdentifier];
NSString *serviceName = [ServiceObjC nameFor: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
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@ 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 *middlewareURL;
@property (nonatomic, readonly, nullable) NSURL *identityWebserviceURL;
Expand Down Expand Up @@ -114,6 +113,7 @@ OBJC_EXPORT NSString * const ApplicationConfigurationDidChangeNotification;


- (nullable NSURL *)playURLForVendor:(SRGVendor)vendor;
- (nullable NSURL *)serviceURLForId:(NSString *)serviceId;

/**
* URLs to be used for sharing
Expand Down
10 changes: 7 additions & 3 deletions Application/Sources/Configuration/ApplicationConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ @interface ApplicationConfiguration ()

@property (nonatomic, copy) NSNumber *appStoreProductIdentifier;

@property (nonatomic) NSURL *serviceURL;
@property (nonatomic) NSDictionary<NSString *, NSURL *> *serviceURLs;
@property (nonatomic) NSDictionary<NSNumber *, NSURL *> *playURLs;
@property (nonatomic) NSURL *playServiceURL;
@property (nonatomic) NSURL *middlewareURL;
Expand Down Expand Up @@ -450,8 +450,7 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(PlayFirebaseConfiguration *)fireba

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

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

NSString *identityWebserviceURLString = [firebaseConfiguration stringForKey:@"identityWebserviceURL"];
self.identityWebserviceURL = identityWebserviceURLString ? [NSURL URLWithString:identityWebserviceURLString] : nil;
Expand Down Expand Up @@ -613,6 +612,11 @@ - (NSURL *)playURLForVendor:(SRGVendor)vendor
return playURLs[@(vendor)];
}

- (NSURL *)serviceURLForId:(NSString *)serviceId
{
return self.serviceURLs[serviceId];
}

- (NSURL *)sharingURLForMedia:(SRGMedia *)media atTime:(CMTime)time
{
if (! media || ! [self playURLForVendor:media.vendor]) {
Expand Down
6 changes: 6 additions & 0 deletions Application/Sources/Configuration/PlayFirebaseConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,12 @@ OBJC_EXPORT NSArray<NSNumber * /* HomeSection */> * _Nullable FirebaseConfigurat
*/
- (NSDictionary<NSNumber *, NSURL *> *)playURLsForKey:(NSString *)key;

/**
* Service URLs accessor. Return an empty dictionnary if no valid data is found under the specified key.
*/
- (NSDictionary<NSString *, NSURL *> *)serviceURLsForKey:(NSString *)key;


@end

NS_ASSUME_NONNULL_END
24 changes: 24 additions & 0 deletions Application/Sources/Configuration/PlayFirebaseConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#import "PlayFirebaseConfiguration.h"

#import "PlayLogger.h"
#import "PlaySRG-Swift.h"

@import Firebase;
@import SRGAppearance;
Expand Down Expand Up @@ -367,6 +368,29 @@ - (NSDictionary *)JSONDictionaryForKey:(NSString *)key
return playURLs.copy;
}

- (NSDictionary<NSString *, NSURL *> *)serviceURLsForKey:(NSString *)key
{
NSMutableDictionary<NSString *, NSURL *> *serviceURLs = [NSMutableDictionary dictionary];

NSDictionary *serviceURLsDictionary = [self JSONDictionaryForKey:key];
for (NSString *key in serviceURLsDictionary) {
if ([ServiceObjC.environments containsObject:key]) {
NSURL *URL = [NSURL URLWithString:serviceURLsDictionary[key]];
if (URL) {
serviceURLs[key] = URL;
}
else {
PlayLogWarning(@"configuration", @"Service URL configuration is not valid. The URL of %@ is not valid.", key);
}
}
else {
PlayLogWarning(@"configuration", @"Service URL configuration identifier is not valid. %@ is not valid.", key);
}
}

return serviceURLs.copy;
}

#pragma mark Update

- (void)update
Expand Down
2 changes: 1 addition & 1 deletion Application/Sources/Settings/ApplicationSettings+Common.m
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ void ApplicationSettingSetServiceIdentifier(NSString *identifier)

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

BOOL ApplicationSettingAutoplayEnabled(void)
Expand Down
114 changes: 61 additions & 53 deletions Application/Sources/Settings/Service.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,82 +7,90 @@
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:
SRGIntegrationLayerProductionServiceURL()
case .stage:
SRGIntegrationLayerStagingServiceURL()
case .test:
SRGIntegrationLayerTestServiceURL()
case .mmf:
Self.mmfUrl
case .samProduction:
SRGIntegrationLayerProductionServiceURL().appendingPathComponent("sam")
case .samStage:
SRGIntegrationLayerStagingServiceURL().appendingPathComponent("sam")
case .samTest:
SRGIntegrationLayerTestServiceURL().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(for 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(for environment: String) -> URL {
ApplicationConfiguration().serviceURL(forId: environment) ?? Service.service(for: environment).url
}
}
14 changes: 5 additions & 9 deletions Application/Sources/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,7 @@ struct SettingsView: View {
@AppStorage(PlaySRGSettingServiceIdentifier) private var selectedServiceId: String?

private var selectedService: Service {
Service.service(forId: selectedServiceId)
Service.service(for: selectedServiceId)
}

var body: some View {
Expand Down Expand Up @@ -643,7 +643,7 @@ struct SettingsView: View {
private struct ServiceSelectionView: View {
var body: some View {
List {
ForEach(Service.services) { service in
ForEach(Service.allCases) { service in
ServiceCell(service: service)
}
}
Expand All @@ -660,7 +660,7 @@ struct SettingsView: View {
private struct ServiceCell: View {
let service: Service

@AppStorage(PlaySRGSettingServiceIdentifier) var selectedServiceId: String?
@AppStorage(PlaySRGSettingServiceIdentifier) var selectedServiceIdentifier: String?

var body: some View {
Button(action: select) {
Expand All @@ -676,15 +676,11 @@ struct SettingsView: View {
}

private func isSelected() -> Bool {
if let selectedServiceId {
service.id == selectedServiceId
} else {
service == .production
}
service == Service.service(for: selectedServiceIdentifier)
}

private func select() {
selectedServiceId = service.id
selectedServiceIdentifier = service.rawValue
}
}

Expand Down
3 changes: 2 additions & 1 deletion Scripts/check-quality.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

set -e

echo "... checking Swift code..."
echo "... checking Swift code with SwiftLint..."
if [ $# -eq 0 ]; then
swiftlint --quiet --strict
elif [[ "$1" == "only-changes" ]]; then
git diff --staged --name-only | grep ".swift$" | xargs -I FILE swiftlint lint --quiet --strict "FILE"
fi
echo "... checking Swift code with SwiftFormat..."
if [ $# -eq 0 ]; then
swiftformat --lint --quiet .
elif [[ "$1" == "only-changes" ]]; then
Expand Down
2 changes: 1 addition & 1 deletion docs/REMOTE_CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ If a remote configuration is found to be invalid (usually a mandatory parameter
* `faqURL` (optional, string): The URL of the FAQs.
* `dataProtectionURL` (optional, string): The URL of the data protection information page.
* `impressumURL` (optional, string): The URL of the impressum page. If none is provided, the corresponding menu entry will not be displayed.
* `serviceURL` (optional, string): The URL of the Content service (DataProvider). If set, it overrides the local server option available in beta builds only.
* `serviceURLs` (optional, JSON): A JSON dictionary describing all URLs of the DataProvider services. Key (string) is the environment identifier of the service, value (string) is the base URL of the DataProvider service. If the key matches a service environment identifier, it overrides the default URL for this service. See `Service.swift` enum raw values for available environment identifiers.
* `identityWebserviceURL` (optional, string): The URL of the identity webservices.
* `identityWebsiteURL` (optional, string): The URL of the identity web portal.
* `userDataServiceURL` (optional, string): The URL of the service with which user data can be synchronized (history, preferences, playlists).
Expand Down