Skip to content

feat(logging): add public log listener methods #1580

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

Open
wants to merge 4 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
2 changes: 1 addition & 1 deletion iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#import <UIKit/UIKit.h>
#import <OneSignalFramework/OneSignalFramework.h>

@interface AppDelegate : UIResponder <UIApplicationDelegate, OSNotificationPermissionObserver, OSInAppMessageLifecycleListener, OSPushSubscriptionObserver, OSNotificationLifecycleListener, OSInAppMessageClickListener, OSNotificationClickListener, OSUserStateObserver>
@interface AppDelegate : UIResponder <UIApplicationDelegate, OSNotificationPermissionObserver, OSInAppMessageLifecycleListener, OSPushSubscriptionObserver, OSNotificationLifecycleListener, OSInAppMessageClickListener, OSNotificationClickListener, OSUserStateObserver, OSLogListener>

@property (strong, nonatomic) UIWindow *window;

Expand Down
6 changes: 6 additions & 0 deletions iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// [FIRApp configure];

NSLog(@"Bundle URL: %@", [[NSBundle mainBundle] bundleURL]);
// Uncomment to test LogListener
// [OneSignal.Debug addLogListener:self];
[OneSignal.Debug setLogLevel:ONE_S_LL_VERBOSE];
[OneSignal.Debug setAlertLevel:ONE_S_LL_NONE];

Expand Down Expand Up @@ -194,4 +196,8 @@ - (void)application:(UIApplication *)application
completionHandler(UIBackgroundFetchResultNoData);
}

- (void)onLogEvent:(OneSignalLogEvent * _Nonnull)event {
NSLog(@"Dev App onLogEvent: %@", event.entry);
}

@end
8 changes: 7 additions & 1 deletion iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,15 @@
import Foundation
import OneSignalFramework

class SwiftTest: NSObject {
class SwiftTest: NSObject, OSLogListener {
func onLogEvent(_ event: OneSignalLogEvent) {
print("Dev App onLogEvent: \(event.level) - \(event.entry)")
}

func testSwiftUserModel() {
let token1 = OneSignal.User.pushSubscription.token
let token = OneSignal.User.pushSubscription.token
OneSignal.Debug.addLogListener(self)
OneSignal.Debug.removeLogListener(self)
}
}
14 changes: 13 additions & 1 deletion iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@
3C47A975292642B100312125 /* OneSignalConfigManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C47A973292642B100312125 /* OneSignalConfigManager.m */; };
3C4F9E4428A4466C009F453A /* OSOperationRepo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */; };
3C5117172B15C31E00563465 /* OSUserState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C5117162B15C31E00563465 /* OSUserState.swift */; };
3C5501402E09CF0100E77DF7 /* OSCopyOnWriteSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 3C55013E2E09CF0100E77DF7 /* OSCopyOnWriteSet.h */; };
3C5501412E09CF0100E77DF7 /* OSCopyOnWriteSet.m in Sources */ = {isa = PBXBuildFile; fileRef = 3C55013F2E09CF0100E77DF7 /* OSCopyOnWriteSet.m */; };
3C5501432E09F3D900E77DF7 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C5501422E09F3D900E77DF7 /* LoggingTests.swift */; };
3C62999F2BEEA34800649187 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3C62999E2BEEA34800649187 /* PrivacyInfo.xcprivacy */; };
3C6299A12BEEA38100649187 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3C6299A02BEEA38100649187 /* PrivacyInfo.xcprivacy */; };
3C6299A32BEEA3CC00649187 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 3C6299A22BEEA3CC00649187 /* PrivacyInfo.xcprivacy */; };
Expand Down Expand Up @@ -1262,6 +1265,9 @@
3C47A973292642B100312125 /* OneSignalConfigManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalConfigManager.m; sourceTree = "<group>"; };
3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationRepo.swift; sourceTree = "<group>"; };
3C5117162B15C31E00563465 /* OSUserState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSUserState.swift; sourceTree = "<group>"; };
3C55013E2E09CF0100E77DF7 /* OSCopyOnWriteSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSCopyOnWriteSet.h; sourceTree = "<group>"; };
3C55013F2E09CF0100E77DF7 /* OSCopyOnWriteSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSCopyOnWriteSet.m; sourceTree = "<group>"; };
3C5501422E09F3D900E77DF7 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
3C62999E2BEEA34800649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
3C6299A02BEEA38100649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
3C6299A22BEEA3CC00649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2186,6 +2192,7 @@
isa = PBXGroup;
children = (
3CC063A62B6D7A8E002BB07F /* OneSignalCoreTests.swift */,
3C5501422E09F3D900E77DF7 /* LoggingTests.swift */,
3C24B0EB2BD09D7A0052E771 /* OneSignalCoreObjCTests.m */,
3C24B0EA2BD09D790052E771 /* OneSignalCoreTests-Bridging-Header.h */,
);
Expand Down Expand Up @@ -2615,9 +2622,9 @@
4529DF0B1FA932AC00CEAB1D /* OneSignalTrackFirebaseAnalytics.m */,
DE7D1831270279D9002D3A5D /* OSNotificationClasses.h */,
DE7D183527027AA0002D3A5D /* OneSignalLog.h */,
DE7D183327027A73002D3A5D /* OneSignalLog.m */,
3CCF44BC299B17290021964D /* OneSignalWrapper.h */,
3CCF44BD299B17290021964D /* OneSignalWrapper.m */,
DE7D183327027A73002D3A5D /* OneSignalLog.m */,
DE7D187627037A16002D3A5D /* OneSignalCoreHelper.h */,
DE7D187827037A26002D3A5D /* OneSignalCoreHelper.m */,
7AE28B8725B8ADF400529100 /* OSMacros.h */,
Expand All @@ -2632,6 +2639,8 @@
DEBAAEB42A436D5D00BF2C1C /* OSStubLocation.m */,
DEBA2A272C24D0ED00E234DB /* OSBundleUtils.h */,
DEBA2A252C20E9AA00E234DB /* OSBundleUtils.m */,
3C55013E2E09CF0100E77DF7 /* OSCopyOnWriteSet.h */,
3C55013F2E09CF0100E77DF7 /* OSCopyOnWriteSet.m */,
);
path = Source;
sourceTree = "<group>";
Expand Down Expand Up @@ -3157,6 +3166,7 @@
DE7D182A270271A9002D3A5D /* OneSignalCommonDefines.h in Headers */,
3CE8CC522911AE90000DB0D3 /* OSNetworkingUtils.h in Headers */,
DEBAAEB02A435B4D00BF2C1C /* OSLocation.h in Headers */,
3C5501402E09CF0100E77DF7 /* OSCopyOnWriteSet.h in Headers */,
DE971754274C48CF00FC409E /* OSPrivacyConsentController.h in Headers */,
3CE8CC4E2911ADD1000DB0D3 /* OSDeviceUtils.h in Headers */,
3C47A974292642B100312125 /* OneSignalConfigManager.h in Headers */,
Expand Down Expand Up @@ -4215,6 +4225,7 @@
files = (
3CC063A72B6D7A8E002BB07F /* OneSignalCoreTests.swift in Sources */,
3C24B0EC2BD09D7A0052E771 /* OneSignalCoreObjCTests.m in Sources */,
3C5501432E09F3D900E77DF7 /* LoggingTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -4455,6 +4466,7 @@
DE7D182E270275FA002D3A5D /* OneSignalTrackFirebaseAnalytics.m in Sources */,
DE51DDE5294262AB0073D5C4 /* OSRemoteParamController.m in Sources */,
DE7D182827026F86002D3A5D /* OneSignalUserDefaults.m in Sources */,
3C5501412E09CF0100E77DF7 /* OSCopyOnWriteSet.m in Sources */,
3CC063942B6D6B6B002BB07F /* OneSignalCore.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
43 changes: 43 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Modified MIT License
*
* Copyright 2025 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#import <Foundation/Foundation.h>

/**
Usage of this class should be limited to cases where modification is rare but reads are frequent.
*/
@interface OSCopyOnWriteSet<__covariant ObjectType> : NSObject {
NSMutableSet *_set;
NSLock *_lock;
}

- (instancetype)init;
- (void)addObject:(ObjectType)object;
- (void)removeObject:(ObjectType)object;
- (NSSet *)allObjects;

@end
71 changes: 71 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/**
* Modified MIT License
*
* Copyright 2025 OneSignal
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* 1. The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* 2. All copies of substantial portions of the Software may only be used in connection
* with services provided by OneSignal.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

#import "OSCopyOnWriteSet.h"

@implementation OSCopyOnWriteSet

- (instancetype)init {
if (self = [super init]) {
_set = [[NSMutableSet alloc] init];
_lock = [[NSLock alloc] init];
}
return self;
}

- (void)addObject:(id)object {
[_lock lock];

// Create a new copy and modify it
NSMutableSet *newSet = [_set mutableCopy];
[newSet addObject:object];

// Update the internal set
_set = newSet;

[_lock unlock];
}

- (void)removeObject:(id)object {
[_lock lock];

// Create a new copy and modify it
NSMutableSet *newSet = [_set mutableCopy];
[newSet removeObject:object];

// Update the internal set
_set = newSet;

[_lock unlock];
}

- (NSSet *)allObjects {
// Read operation - no lock needed
return _set;
}

@end
31 changes: 31 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,40 @@ typedef NS_ENUM(NSUInteger, ONE_S_LOG_LEVEL) {
ONE_S_LL_VERBOSE
};

@interface OneSignalLogEvent : NSObject
@property(readonly)ONE_S_LOG_LEVEL level;
@property(readonly, nonnull)NSString *entry;
@end

@protocol OSLogListener <NSObject>
- (void)onLogEvent:(OneSignalLogEvent *_Nonnull)event;
@end

@protocol OSDebug <NSObject>
/**
The log level the OneSignal SDK should be writing to the Xcode log. Defaults to [LogLevel.WARN].

WARNING: This should not be set higher than LogLevel.WARN in a production setting.
*/
+ (void)setLogLevel:(ONE_S_LOG_LEVEL)logLevel;
/**
The log level the OneSignal SDK should be showing as a modal. Defaults to [LogLevel.NONE].

WARNING: This should not be used in a production setting.
*/
+ (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel NS_REFINED_FOR_SWIFT;
/**
Add a listener to receive all logging messages the SDK produces.
Useful to capture and send logs to your server.

NOTE: All log messages are always passed, LogLevel has no effect on this.
*/
+ (void)addLogListener:(NSObject<OSLogListener>*_Nonnull)listener NS_REFINED_FOR_SWIFT;
/**
Removes a listener added by addLogListener
*/
+ (void)removeLogListener:(NSObject<OSLogListener>*_Nonnull)listener NS_REFINED_FOR_SWIFT;

@end

@interface OneSignalLog : NSObject<OSDebug>
Expand Down
33 changes: 33 additions & 0 deletions iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@
#import <Foundation/Foundation.h>
#import "OneSignalLog.h"
#import "OSDialogInstanceManager.h"
#import "OSCopyOnWriteSet.h"

@implementation OneSignalLogEvent
- (instancetype)initWithLevel:(ONE_S_LOG_LEVEL)level entry:(NSString*)entry {
_level = level;
_entry = entry;
return self;
}
@end

@implementation OneSignalLog

Expand All @@ -38,6 +47,15 @@ @implementation OneSignalLog
return self;
}

+ (OSCopyOnWriteSet<NSObject<OSLogListener> *>*)logListeners {
static OSCopyOnWriteSet<NSObject<OSLogListener> *> *_logListeners;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_logListeners = [OSCopyOnWriteSet new];
});
return _logListeners;
}

+ (void)setLogLevel:(ONE_S_LOG_LEVEL)nsLogLevel {
_nsLogLevel = nsLogLevel;
}
Expand All @@ -46,6 +64,14 @@ + (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel {
_alertLogLevel = logLevel;
}

+ (void)addLogListener:(NSObject<OSLogListener>*_Nonnull)listener {
[self.logListeners addObject:listener];
}

+ (void)removeLogListener:(NSObject<OSLogListener>*_Nonnull)listener {
[self.logListeners removeObject:listener];
}

+ (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message {
onesignal_Log(logLevel, message);
}
Expand Down Expand Up @@ -86,6 +112,13 @@ void onesignal_Log(ONE_S_LOG_LEVEL logLevel, NSString* message) {
if (logLevel <= _alertLogLevel) {
[[OSDialogInstanceManager sharedInstance] presentDialogWithTitle:levelString withMessage:message withActions:nil cancelTitle:NSLocalizedString(@"Close", @"Close button") withActionCompletion:nil];
}

for (NSObject<OSLogListener> *listener in OneSignalLog.logListeners.allObjects) {
if ([listener respondsToSelector:@selector(onLogEvent:)]) {
OneSignalLogEvent *event = [[OneSignalLogEvent alloc] initWithLevel:logLevel entry:[levelString stringByAppendingString:message]];
[listener onLogEvent:event];
}
}
}

@end
Expand Down
Loading
Loading