diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h index b94a956d6..b119bcd4d 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.h @@ -31,7 +31,7 @@ #import #import -@interface AppDelegate : UIResponder +@interface AppDelegate : UIResponder @property (strong, nonatomic) UIWindow *window; diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m index d3061a94a..3e96aad1e 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/AppDelegate.m @@ -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]; @@ -194,4 +196,8 @@ - (void)application:(UIApplication *)application completionHandler(UIBackgroundFetchResultNoData); } +- (void)onLogEvent:(OneSignalLogEvent * _Nonnull)event { + NSLog(@"Dev App onLogEvent: %@", event.entry); +} + @end diff --git a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift index eae4a119f..6b8d705b4 100644 --- a/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift +++ b/iOS_SDK/OneSignalDevApp/OneSignalDevApp/SwiftTest.swift @@ -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) } } diff --git a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj index ad9d90b77..be669e6f3 100644 --- a/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj +++ b/iOS_SDK/OneSignalSDK/OneSignal.xcodeproj/project.pbxproj @@ -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 */; }; @@ -1262,6 +1265,9 @@ 3C47A973292642B100312125 /* OneSignalConfigManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OneSignalConfigManager.m; sourceTree = ""; }; 3C4F9E4328A4466C009F453A /* OSOperationRepo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSOperationRepo.swift; sourceTree = ""; }; 3C5117162B15C31E00563465 /* OSUserState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OSUserState.swift; sourceTree = ""; }; + 3C55013E2E09CF0100E77DF7 /* OSCopyOnWriteSet.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = OSCopyOnWriteSet.h; sourceTree = ""; }; + 3C55013F2E09CF0100E77DF7 /* OSCopyOnWriteSet.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = OSCopyOnWriteSet.m; sourceTree = ""; }; + 3C5501422E09F3D900E77DF7 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = ""; }; 3C62999E2BEEA34800649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3C6299A02BEEA38100649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 3C6299A22BEEA3CC00649187 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; @@ -2186,6 +2192,7 @@ isa = PBXGroup; children = ( 3CC063A62B6D7A8E002BB07F /* OneSignalCoreTests.swift */, + 3C5501422E09F3D900E77DF7 /* LoggingTests.swift */, 3C24B0EB2BD09D7A0052E771 /* OneSignalCoreObjCTests.m */, 3C24B0EA2BD09D790052E771 /* OneSignalCoreTests-Bridging-Header.h */, ); @@ -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 */, @@ -2632,6 +2639,8 @@ DEBAAEB42A436D5D00BF2C1C /* OSStubLocation.m */, DEBA2A272C24D0ED00E234DB /* OSBundleUtils.h */, DEBA2A252C20E9AA00E234DB /* OSBundleUtils.m */, + 3C55013E2E09CF0100E77DF7 /* OSCopyOnWriteSet.h */, + 3C55013F2E09CF0100E77DF7 /* OSCopyOnWriteSet.m */, ); path = Source; sourceTree = ""; @@ -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 */, @@ -4215,6 +4225,7 @@ files = ( 3CC063A72B6D7A8E002BB07F /* OneSignalCoreTests.swift in Sources */, 3C24B0EC2BD09D7A0052E771 /* OneSignalCoreObjCTests.m in Sources */, + 3C5501432E09F3D900E77DF7 /* LoggingTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -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; diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.h new file mode 100644 index 000000000..3f29c4241 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.h @@ -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 + +/** + 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 diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.m new file mode 100644 index 000000000..596802c00 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OSCopyOnWriteSet.m @@ -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 diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h index d5e72bcbe..43c92024a 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.h @@ -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 +- (void)onLogEvent:(OneSignalLogEvent *_Nonnull)event; +@end + @protocol OSDebug +/** + 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*_Nonnull)listener NS_REFINED_FOR_SWIFT; +/** + Removes a listener added by addLogListener + */ ++ (void)removeLogListener:(NSObject*_Nonnull)listener NS_REFINED_FOR_SWIFT; + @end @interface OneSignalLog : NSObject diff --git a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m index 0a840128b..2ed5b3896 100644 --- a/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m +++ b/iOS_SDK/OneSignalSDK/OneSignalCore/Source/OneSignalLog.m @@ -28,6 +28,15 @@ #import #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 @@ -38,6 +47,15 @@ @implementation OneSignalLog return self; } ++ (OSCopyOnWriteSet *>*)logListeners { + static OSCopyOnWriteSet *> *_logListeners; + static dispatch_once_t onceToken; + dispatch_once(&onceToken, ^{ + _logListeners = [OSCopyOnWriteSet new]; + }); + return _logListeners; +} + + (void)setLogLevel:(ONE_S_LOG_LEVEL)nsLogLevel { _nsLogLevel = nsLogLevel; } @@ -46,6 +64,14 @@ + (void)setAlertLevel:(ONE_S_LOG_LEVEL)logLevel { _alertLogLevel = logLevel; } ++ (void)addLogListener:(NSObject*_Nonnull)listener { + [self.logListeners addObject:listener]; +} + ++ (void)removeLogListener:(NSObject*_Nonnull)listener { + [self.logListeners removeObject:listener]; +} + + (void)onesignalLog:(ONE_S_LOG_LEVEL)logLevel message:(NSString* _Nonnull)message { onesignal_Log(logLevel, message); } @@ -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 *listener in OneSignalLog.logListeners.allObjects) { + if ([listener respondsToSelector:@selector(onLogEvent:)]) { + OneSignalLogEvent *event = [[OneSignalLogEvent alloc] initWithLevel:logLevel entry:[levelString stringByAppendingString:message]]; + [listener onLogEvent:event]; + } + } } @end diff --git a/iOS_SDK/OneSignalSDK/OneSignalCoreTests/LoggingTests.swift b/iOS_SDK/OneSignalSDK/OneSignalCoreTests/LoggingTests.swift new file mode 100644 index 000000000..953537141 --- /dev/null +++ b/iOS_SDK/OneSignalSDK/OneSignalCoreTests/LoggingTests.swift @@ -0,0 +1,144 @@ +/* + 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 XCTest +import OneSignalCore + +class TestLogListener: NSObject, OSLogListener { + var calls: [String] = [] + let callback: ((OneSignalLogEvent) -> Void)? + + init(_ callback: ((OneSignalLogEvent) -> Void)? = nil) { + self.callback = callback + } + + func onLogEvent(_ event: OneSignalLogEvent) { + calls.append(event.entry) + guard let callback = callback else { return } + callback(event) + } +} + +final class LoggingTests: XCTestCase { + override func setUpWithError() throws { + OneSignalLog.setLogLevel(.LL_NONE) + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testAddListener() throws { + // Given + let listener = TestLogListener() + OneSignalLog.debug().__add(listener) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + + // Then + XCTAssertEqual(listener.calls, ["DEBUG: test"]) + } + + func testAddListenerTwice() throws { + // Given + let listener = TestLogListener() + OneSignalLog.debug().__add(listener) + OneSignalLog.debug().__add(listener) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + + // Then + XCTAssertEqual(listener.calls, ["DEBUG: test"]) + } + + func testRemoveListener() throws { + // Given + let listener = TestLogListener() + OneSignalLog.debug().__add(listener) + OneSignalLog.debug().__remove(listener) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + + // Then + XCTAssertEqual(listener.calls, []) + } + + func testRemoveListenerTwice() throws { + // Given + let listener = TestLogListener() + OneSignalLog.debug().__add(listener) + OneSignalLog.debug().__remove(listener) + OneSignalLog.debug().__remove(listener) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + + // Then + XCTAssertEqual(listener.calls, []) + } + + func testAddListenerNested() throws { + // Given + let nestedListener = TestLogListener() + let firstListener = TestLogListener({ _ in + OneSignalLog.debug().__add(nestedListener) + }) + + OneSignalLog.debug().__add(firstListener) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test2") + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test3") + + // Then + XCTAssertEqual(nestedListener.calls, ["DEBUG: test2", "DEBUG: test3"]) + } + + func testRemoveListenerNested() throws { + // Given + var calls: [String] = [] + var listener: OSLogListener? + + listener = TestLogListener({ event in + calls.append(event.entry) + OneSignalLog.debug().__remove(listener!) + }) + + OneSignalLog.debug().__add(listener!) + + // When + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test") + OneSignalLog.onesignalLog(.LL_DEBUG, message: "test2") + + // Then + XCTAssertEqual(calls, ["DEBUG: test"]) + } +} diff --git a/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift b/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift index b29c525cb..bf399005c 100644 --- a/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift +++ b/iOS_SDK/OneSignalSDK/Source/OneSignalSwiftInterface.swift @@ -63,9 +63,29 @@ public extension OneSignal { } public extension OSDebug { + /** + 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. + */ static func setAlertLevel(_ logLevel: ONE_S_LOG_LEVEL) { __setAlert(logLevel) } + /** + 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. + */ + static func addLogListener(_ listener: OSLogListener) { + __add(listener) + } + /** + Removes a listener added by addLogListener + */ + static func removeLogListener(_ listener: OSLogListener) { + __remove(listener) + } } public extension OSInAppMessages {