Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c878b89
setup PushSDK
mbruegmann Mar 18, 2026
2c798c8
Use standard UNUserNotificationCenter APIs to request push permission
mbruegmann Mar 18, 2026
b4c1451
Move push handling to AppDelegate and prepare for Airship removal
mbruegmann Mar 18, 2026
7b63fc4
use URN as tag name
mbruegmann Apr 9, 2026
804c997
Merge branch 'main' into push-sdk-setup
mbruegmann Apr 9, 2026
013aa45
Add ruby platform to Gemfile.lock for CI compatibility
mbruegmann Apr 9, 2026
745c65b
Merge branch 'main' of https://github.com/SRGSSR/playsrg-apple into p…
May 5, 2026
916be81
Fix linking
May 6, 2026
4d696af
Update Push SDK to 1.2.0
May 6, 2026
b33e912
Gate Airship calls using isFlying
May 6, 2026
c5b9f3e
Add more todos
May 6, 2026
93c3031
Update airshipIdentifier
May 6, 2026
9650bd0
Solve device token problem
May 6, 2026
1b61c93
Solve todos
May 6, 2026
5bcfeb1
Fix typos
May 6, 2026
50b332c
Update pods
May 6, 2026
2099c9f
Merge branch 'main' into push-sdk-setup
mbruegmann May 8, 2026
8e6fa57
Fix show notification icon not updating after toggle
mbruegmann May 11, 2026
9124e9a
Merge branch 'main' of https://github.com/SRGSSR/playsrg-apple into p…
May 11, 2026
63ca647
Lint
May 11, 2026
32aa03c
updated channel names
mbruegmann May 13, 2026
f9b92ca
Update gitignore
May 13, 2026
8932781
Add logging
May 13, 2026
ce5ff85
Revert "Add logging"
May 13, 2026
1508478
Fix compilation on tvOS
May 13, 2026
3ed5d51
Update tvOS targets
May 13, 2026
91b6337
Lower iOS deployment target to 15.0
mbruegmann May 20, 2026
d9c8445
Update CLAUDE.md minimum iOS to 15.0
mbruegmann May 20, 2026
fcdd165
Run make setup after a PodFile update
pyby May 28, 2026
938a686
Fix Airship takeoff problem
Jun 8, 2026
50b7bf6
Fix setting enabled flag
Jun 8, 2026
c5973e5
Set badge number also in the Push SDK flow
Jun 8, 2026
dc7787c
Forward completion handler
Jun 8, 2026
08fd5be
Fix favorites optimistic toggle ordering
Jun 8, 2026
c6871db
Improve getting tags
Jun 8, 2026
6d83ba0
Fix formatting
Jun 8, 2026
122c2d9
Add a comment
Jun 8, 2026
7e4d042
Fix signing issues
Naxyoh Jun 22, 2026
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ xcuserdata
/vendor

.vscode
settings.local.json
Comment thread
r3econ marked this conversation as resolved.
62 changes: 61 additions & 1 deletion Application/Sources/Application/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

@import AirshipCore;
@import AppCenter;
@import UserNotifications;
@import AppCenterCrashes;
@import AVFoundation;
@import CarPlay;
Expand All @@ -38,7 +39,7 @@

static void *s_kvoContext = &s_kvoContext;

@interface AppDelegate() <SRGAnalyticsTrackerDataSource>
@interface AppDelegate() <SRGAnalyticsTrackerDataSource, UNUserNotificationCenterDelegate>

@end

Expand Down Expand Up @@ -99,6 +100,7 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
name:SRGLetterboxPlaybackDidContinueAutomaticallyNotification
object:nil];

[UNUserNotificationCenter currentNotificationCenter].delegate = self;
[PushService.sharedService setupWithLaunchingWithOptions:launchOptions];
[PushService.sharedService updateApplicationBadge];

Expand Down Expand Up @@ -324,6 +326,64 @@ - (void)productViewControllerDidFinish:(SKStoreProductViewController *)viewContr
}];
}

#pragma mark Push registration

- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
if ([UAirship isFlying]) {
[UAAppIntegration application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
}
[PushService.sharedService registerDeviceToken:deviceToken];
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
if ([UAirship isFlying]) {
[UAAppIntegration application:application didFailToRegisterForRemoteNotificationsWithError:error];
}
}
Comment on lines +339 to +344

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure checking isFlying is required, I would expect Airship to do the right thing. Please check the documentation.


- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
// Normal push (com.urbanairship) or silent push (_)
NSPredicate *airshipPredicate = [NSPredicate predicateWithBlock:^BOOL(id key, NSDictionary *bindings) {
return [key isKindOfClass:NSString.class] && ([key hasPrefix:@"com.urbanairship"] || [key isEqualToString:@"_"]);
}];
BOOL isAirshipPayload = [[userInfo.allKeys filteredArrayUsingPredicate:airshipPredicate] count] > 0;
if (isAirshipPayload) {
if ([UAirship isFlying]) {
[UAAppIntegration application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler];
}
} else {
completionHandler(UIBackgroundFetchResultNewData);
}
}

#pragma mark UNUserNotificationCenterDelegate protocol

- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
{
[PushService.sharedService handleNotificationResponse:response];
if ([UAirship isFlying]) {
[UAAppIntegration userNotificationCenter:center
didReceiveNotificationResponse:response
withCompletionHandler:completionHandler];
} else {
completionHandler();
}
}

- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
if ([UAirship isFlying]) {
[UAAppIntegration userNotificationCenter:center
willPresentNotification:notification
withCompletionHandler:completionHandler];
} else {
completionHandler(UNNotificationPresentationOptionNone);
}
}

#pragma mark Notifications

- (void)playbackDidContinueAutomatically:(NSNotification *)notification
Expand Down
2 changes: 2 additions & 0 deletions Application/Sources/Configuration/ApplicationConfiguration.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ OBJC_EXPORT NSString * const ApplicationConfigurationDidChangeNotification;
@property (nonatomic, readonly, nullable) NSURL *identityWebsiteURL;
@property (nonatomic, readonly, nullable) NSURL *userDataServiceURL;

@property (nonatomic, readonly, nullable) NSURL *pushServiceURL;

@property (nonatomic, readonly) NSURL *whatsNewURL;
@property (nonatomic, readonly, nullable) NSURL *supportFormURL;
@property (nonatomic, readonly, nullable) NSURL *faqURL;
Expand Down
11 changes: 10 additions & 1 deletion Application/Sources/Configuration/ApplicationConfiguration.m
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,7 @@ @interface ApplicationConfiguration ()
@property (nonatomic) NSURL *identityWebserviceURL;
@property (nonatomic) NSURL *identityWebsiteURL;
@property (nonatomic) NSURL *userDataServiceURL;
@property (nonatomic) NSURL *pushServiceURL;

@property (nonatomic) NSURL *whatsNewURL;
@property (nonatomic) NSURL *supportFormURL;
Expand Down Expand Up @@ -483,7 +484,15 @@ - (BOOL)synchronizeWithFirebaseConfiguration:(PlayFirebaseConfiguration *)fireba

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


#if defined(DEBUG) || defined(NIGHTLY)
self.pushServiceURL = [NSURL URLWithString:@"https://api.dev.srf.ch/push"];
#elif defined(BETA)
self.pushServiceURL = [NSURL URLWithString:@"https://api.int.srf.ch/push"];
#else
self.pushServiceURL = [NSURL URLWithString:@"https://api.srf.ch/push"];
#endif

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

Expand Down
7 changes: 6 additions & 1 deletion Application/Sources/Favorites/Favorites.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,13 @@ OBJC_EXPORT void FavoritesUpdatePushService(void);
#endif

/**
* Return `YES` iff the user has subscribed to the specified show.
* Return `YES` if the user has subscribed to the specified show.
*/
OBJC_EXPORT BOOL FavoritesIsSubscribedToShow(SRGShow * _Nonnull show);

/**
* Return `YES` if the user has subscribed to the show with the specified URN.
*/
OBJC_EXPORT BOOL FavoritesIsSubscribedToShowURN(NSString * _Nonnull URN);

NS_ASSUME_NONNULL_END
19 changes: 11 additions & 8 deletions Application/Sources/Favorites/Favorites.m
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,20 @@ BOOL FavoritesToggleSubscriptionForShow(SRGShow *show)
if (! FavoritesContainsShow(show)) {
FavoritesToggleShow(show);
}

if (! [PushService.sharedService toggleSubscriptionForShow:show]) {

// Subscriptions can only be changed when notifications are enabled; this may prompt the user.
if (! [PushService.sharedService requestSubscriptionAuthorization]) {
return NO;
}
BOOL subscribed = [PushService.sharedService isSubscribedToShowURN:show.URN];

BOOL newSubscribed = ! FavoritesIsSubscribedToShowURN(show.URN);
NSString *path = [[[PlayFavoritesPath stringByAppendingPathComponent:show.URN] stringByAppendingPathComponent:PlayNotificationsPath] stringByAppendingPathComponent:PlayNewOnDemandPath];
[SRGUserData.currentUserData.preferences setNumber:@(subscribed) atPath:path inDomain:PlayPreferencesDomain];


// Update SRGUserData before invoking the push service: syncTagsToPushSDK reads back the subscription state from here.
[SRGUserData.currentUserData.preferences setNumber:@(newSubscribed) atPath:path inDomain:PlayPreferencesDomain];

[PushService.sharedService toggleSubscriptionForShow:show];

return YES;
}

Expand All @@ -141,8 +146,6 @@ void FavoritesUpdatePushService(void)
NSSet<NSString *> *toUnsubscribeURNs = [subscribedPushServiceURNs setByRemovingObjectsIn:subscribedURNs];
[PushService.sharedService unsubscribeFromShowURNs:toUnsubscribeURNs];
}

NSCAssert([subscribedURNs isEqualToSet:PushService.sharedService.subscribedShowURNs], @"Subscribed favorite shows have to be equal to Push Service subscribed shows");
}

#endif
12 changes: 8 additions & 4 deletions Application/Sources/Helpers/PushService+Private.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,15 @@ NS_ASSUME_NONNULL_BEGIN
@interface PushService (Helpers)

/**
* Toggle subscription for the specified show and notifified with a banner. Return `YES` if toggled. If notifications
* are not enabled an alert is presented to ask the user to enable push notifications instead. The toggle action is
* ignored and the method returns `NO`.
* Return `YES` if show subscriptions can be changed. If notifications are not enabled the system permission prompt
* or an alert directing the user to Settings is presented, and the method returns `NO`.
*/
- (BOOL)toggleSubscriptionForShow:(SRGShow *)show;
- (BOOL)requestSubscriptionAuthorization;

/**
* Toggle subscription for the specified show. Callers must ensure `-requestSubscriptionAuthorization` returns `YES` first.
*/
- (void)toggleSubscriptionForShow:(SRGShow *)show;

@end

Expand Down
10 changes: 10 additions & 0 deletions Application/Sources/Helpers/PushService.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,16 @@ OBJC_EXPORT NSString * const PushServiceEnabledKey;
*/
@property (nonatomic, readonly, copy, nullable) NSString *airshipIdentifier;

/**
* Register the device token with PushSDK. Call from `application:didRegisterForRemoteNotificationsWithDeviceToken:`.
*/
- (void)registerDeviceToken:(NSData *)deviceToken;

/**
* Handle a notification response. Call from `userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:`.
*/
- (void)handleNotificationResponse:(UNNotificationResponse *)notificationResponse;

/**
* Attempt to present the system alert to enable push notifications. Returns `YES` iff presented.
*/
Expand Down
Loading
Loading