Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
742e75a
triggering tests
diegomtz5 Jan 20, 2026
f3f0463
Updated NRLoggerTests
diegomtz5 Jan 21, 2026
55431f0
Updated PersistentStoreTests
diegomtz5 Jan 22, 2026
2820665
Updated NRMAURLSessionHeaderTrackingTests
diegomtz5 Jan 22, 2026
c076c73
Updated NRMASessionExclusivityWithoutDelegateTests
diegomtz5 Jan 22, 2026
c837d26
Updated all unit tests with known issues
diegomtz5 Jan 22, 2026
3bc905d
Merge pull request #578 from newrelic/backport761develop
cdillard-NewRelic Jan 26, 2026
1461627
Merge pull request #577 from newrelic/NR-443709
diegomtz5 Jan 29, 2026
6fa4ecd
Improve touch movement smoothness
diegomtz5 Jan 30, 2026
632a3cd
NR-508705 Background and foreground events match Android behavior (#579)
mbruin-NR Jan 30, 2026
f0bb498
Merge branch 'develop' into NR-503036
mbruin-NR Jan 30, 2026
7bd924e
Merge pull request #580 from newrelic/NR-503036
diegomtz5 Jan 30, 2026
f8a58f4
NR-510109 changes to color reflection for SwiftUI MSR for iOS 26 (#583)
mbruin-NR Feb 3, 2026
06367ab
bump:s v 7.6.2 (#586)
cdillard-NewRelic Feb 5, 2026
ae8390c
NR-520409 changes to attributed text examination to minimize crash (#…
mbruin-NR Feb 5, 2026
a2606ad
Update cocoapods license to string "Apache License, Version 2.0" (#584)
cdillard-NewRelic Feb 5, 2026
7d0368d
NR-480518 added line numbers, line break, font weight, and kern to te…
mbruin-NR Feb 6, 2026
115d911
Fix for hasher crash on older iOS versions (#591)
mbruin-NR Feb 9, 2026
90c2997
Nr 480854 Added shapes through SVG and drawing using images (#590)
mbruin-NR Feb 10, 2026
6c7557b
fix: add frameQueue when adding frame to process (#592)
cdillard-NewRelic Feb 10, 2026
d74a148
restore:s logging sanity (#593)
cdillard-NewRelic Feb 11, 2026
2296f6b
fix: NR-518139 - restore iOS 15 by not allowing unmasking SwiftUI tex…
cdillard-NewRelic Feb 12, 2026
6c603f7
NR-502502 Added the discussed crash fix (#596)
mbruin-NR Feb 12, 2026
77e263d
NR-523372 added a check for off mode when a replay is paused so that …
mbruin-NR Feb 12, 2026
4d1c9db
NR-523393: skip empty incrementalFrames (#598)
cdillard-NewRelic Feb 12, 2026
6e33ecf
NR-522021 Changed capture to always put color views first so they are…
mbruin-NR Feb 17, 2026
3363442
Merge branch 'staging' of github.com:newrelic/newrelic-ios-agent into…
cdillard-NewRelic Feb 17, 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
15 changes: 15 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ jobs:
# curl https://raw.githubusercontent.com/Homebrew/homebrew-core/e92d2ae54954ebf485b484d8522104700b144fee/Formula/lcov.rb > lcov.rb
# brew install ./lcov.rb

- name: Install Bundler 2.7.2
run: gem install bundler:2.7.2

- name: Update gem
run: bundle update

Expand All @@ -57,6 +60,9 @@ jobs:
# - name: Install lcov
# run: brew install lcov

- name: Install Bundler 2.7.2
run: gem install bundler:2.7.2

- name: Update gem
run: bundle update

Expand All @@ -83,6 +89,9 @@ jobs:
# - name: Install lcov
# run: brew install lcov

- name: Install Bundler 2.7.2
run: gem install bundler:2.7.2

- name: Update gem
run: bundle update

Expand Down Expand Up @@ -139,6 +148,9 @@ jobs:
# mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
# cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles

- name: Install Bundler 2.7.2
run: gem install bundler:2.7.2

- name: Update gem
run: bundle update

Expand Down Expand Up @@ -180,6 +192,9 @@ jobs:
with:
submodules: true

- name: Install Bundler 2.7.2
run: gem install bundler:2.7.2

- name: Update gem
run: bundle update

Expand Down
154 changes: 104 additions & 50 deletions Agent.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions Agent/Analytics/Constants.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ extern NSString *const kNRMA_RET_mobileRequestError;
extern NSString *const kNRMA_RET_mobileCrash;
extern NSString *const kNRMA_RET_mobileBreadcrumb;
extern NSString *const kNRMA_RET_mobileUserAction;
extern NSString *const kNRMA_RET_userAction;

extern NSString *const kNRMA_RA_methodExecuted;
extern NSString *const kNRMA_RA_targetObject;
Expand Down
1 change: 1 addition & 0 deletions Agent/Analytics/Constants.m
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
NSString * const kNRMA_RET_mobileCrash = @"MobileCrash";
NSString * const kNRMA_RET_mobileBreadcrumb = @"MobileBreadcrumb";
NSString * const kNRMA_RET_mobileUserAction = @"MobileUserAction";
NSString * const kNRMA_RET_userAction = @"UserAction";

//gesture attributes (not reserved)
NSString * const kNRMA_RA_methodExecuted = @"methodExecuted";
Expand Down
26 changes: 26 additions & 0 deletions Agent/Analytics/Events/NRMAUserActionEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// NRMAUserActionEvent.h
// Agent
//
// Created by Mike Bruin on 1/29/26.
// Copyright © 2026 New Relic. All rights reserved.
//

#import <Foundation/Foundation.h>

#import "NRMAMobileEvent.h"
#import "AttributeValidatorProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface NRMAUserActionEvent : NRMAMobileEvent
@property (nonatomic, strong) NSString *category;

- (nonnull instancetype) initWithTimestamp:(NSTimeInterval)timestamp
sessionElapsedTimeInSeconds:(NSTimeInterval)sessionElapsedTimeSeconds
category:(NSString *) category
withAttributeValidator:(__nullable id<AttributeValidatorProtocol>)attributeValidator;

@end

NS_ASSUME_NONNULL_END
57 changes: 57 additions & 0 deletions Agent/Analytics/Events/NRMAUserActionEvent.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// NRMAUserActionEvent.m
// Agent
//
// Created by Mike Bruin on 1/29/26.
// Copyright © 2026 New Relic. All rights reserved.
//

#import "NRMAUserActionEvent.h"
#import "Constants.h"

static NSString* const kCategoryKey = @"Category";

@implementation NRMAUserActionEvent

+ (BOOL) supportsSecureCoding {
return YES;
}

- (nonnull instancetype) initWithTimestamp:(NSTimeInterval)timestamp
sessionElapsedTimeInSeconds:(NSTimeInterval)sessionElapsedTimeSeconds
category:(NSString *) category
withAttributeValidator:(__nullable id<AttributeValidatorProtocol>)attributeValidator
{
self = [super initWithTimestamp:timestamp sessionElapsedTimeInSeconds:sessionElapsedTimeSeconds withAttributeValidator:attributeValidator];
if (self) {
self.eventType = kNRMA_RET_mobileUserAction;
self.category = category;
}

return self;
}

- (id)JSONObject {
NSDictionary *event = [super JSONObject];

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:event];
dict[kNRMA_RA_category] = self.category;

return [NSDictionary dictionaryWithDictionary:dict];
}

- (void)encodeWithCoder:(NSCoder *)coder {
[super encodeWithCoder:coder];

[coder encodeObject:self.category forKey:kCategoryKey];
}

- (nullable instancetype)initWithCoder:(nonnull NSCoder *)coder {
self = [super initWithCoder:coder];
if(self) {
self.category = [coder decodeObjectOfClass:[NSString class] forKey:kCategoryKey];
}

return self;
}
@end
88 changes: 50 additions & 38 deletions Agent/Analytics/NRMAAnalytics.mm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#import "NRMACustomEvent.h"
#import "NRMARequestEvent.h"
#import "NRMAInteractionEvent.h"
#import "NRMAUserActionEvent.h"
#import "NRMAPayload.h"
#import "NRMANetworkErrorEvent.h"
#import "NRMASAM.h"
Expand Down Expand Up @@ -932,45 +933,64 @@ - (BOOL) checkBackgroundStatus {

- (BOOL)recordUserAction:(NRMAUserAction *)userAction {
if (userAction == nil) { return NO; };

NRMACustomEvent* event = [[NRMACustomEvent alloc] initWithEventType:kNRMA_RET_mobileUserAction
timestamp:[NRMAAnalytics currentTimeMillis]
sessionElapsedTimeInSeconds:[[NSDate date] timeIntervalSinceDate:_sessionStartTime] withAttributeValidator:_attributeValidator];
if([NRMAFlags shouldEnableNewEventSystem]){
NRMAUserActionEvent* event = [[NRMAUserActionEvent alloc] initWithTimestamp:[NRMAAnalytics currentTimeMillis]
sessionElapsedTimeInSeconds:[[NSDate date] timeIntervalSinceDate:_sessionStartTime]
category:kNRMA_RET_userAction withAttributeValidator:_attributeValidator];

if (userAction.associatedMethod.length > 0) {
[event addAttribute:kNRMA_RA_methodExecuted value:userAction.associatedMethod];
}
if (userAction.associatedMethod.length > 0) {
[event addAttribute:kNRMA_RA_methodExecuted value:userAction.associatedMethod];
}

if (userAction.associatedClass.length > 0) {
[event addAttribute:kNRMA_RA_targetObject value:userAction.associatedClass];
}
if (userAction.associatedClass.length > 0) {
[event addAttribute:kNRMA_RA_targetObject value:userAction.associatedClass];
}

if (userAction.elementLabel.length > 0) {
[event addAttribute:kNRMA_RA_label value:userAction.elementLabel];
}
if (userAction.elementLabel.length > 0) {
[event addAttribute:kNRMA_RA_label value:userAction.elementLabel];
}

if ((userAction.accessibilityId.length > 0)) {
[event addAttribute:kNRMA_RA_accessibility value:userAction.accessibilityId];
}
if ((userAction.accessibilityId.length > 0)) {
[event addAttribute:kNRMA_RA_accessibility value:userAction.accessibilityId];
}

if ((userAction.interactionCoordinates.length > 0)) {
[event addAttribute:kNRMA_RA_touchCoordinates value:userAction.interactionCoordinates];
}
if ((userAction.interactionCoordinates.length > 0)) {
[event addAttribute:kNRMA_RA_touchCoordinates value:userAction.interactionCoordinates];
}

if ((userAction.actionType.length > 0)) {
[event addAttribute:kNMRA_RA_actionType value:userAction.actionType];
}
if ((userAction.actionType.length > 0)) {
[event addAttribute:kNMRA_RA_actionType value:userAction.actionType];
}

if ((userAction.elementFrame.length > 0)) {
[event addAttribute:kNRMA_RA_frame value:userAction.elementFrame];
}
if ((userAction.elementFrame.length > 0)) {
[event addAttribute:kNRMA_RA_frame value:userAction.elementFrame];
}

NSString* deviceOrientation = [NewRelicInternalUtils deviceOrientation];
if (deviceOrientation.length > 0) {
[event addAttribute:kNRMA_RA_orientation value:deviceOrientation];
}
NSString* deviceOrientation = [NewRelicInternalUtils deviceOrientation];
if (deviceOrientation.length > 0) {
[event addAttribute:kNRMA_RA_orientation value:deviceOrientation];
}

return [_eventManager addEvent:[event autorelease]];
return [_eventManager addEvent:[event autorelease]];
} else {
try {
return _analyticsController->addUserActionEvent(userAction.associatedMethod.UTF8String,
userAction.associatedClass.UTF8String,
userAction.elementLabel.UTF8String,
userAction.accessibilityId.UTF8String,
userAction.interactionCoordinates.UTF8String,
userAction.actionType.UTF8String,
userAction.elementFrame.UTF8String,
[NewRelicInternalUtils deviceOrientation].UTF8String,
[self checkOfflineStatus],
[self checkBackgroundStatus]);
} catch (std::exception &error) {
NRLOG_AGENT_VERBOSE(@"Failed to add TrackedGesture: %s.", error.what());
} catch (...) {
NRLOG_AGENT_VERBOSE(@"Failed to add TrackedGesture: unknown error.");
}
}
return false;
}

- (BOOL) incrementSessionAttribute:(NSString*)name value:(NSNumber*)number
Expand Down Expand Up @@ -1123,14 +1143,6 @@ - (void) clearLastSessionsAnalytics {

- (void) sessionWillEnd {
_sessionWillEnd = YES;

if([NRMAFlags shouldEnableGestureInstrumentation])
{
NRMAUserAction* backgroundGesture = [NRMAUserActionBuilder buildWithBlock:^(NRMAUserActionBuilder *builder) {
[builder withActionType:kNRMAUserActionAppBackground];
}];
[[NewRelicAgentInternal sharedInstance].gestureFacade recordUserAction:backgroundGesture];
}

[self endSessionReusable];
}
Expand Down
3 changes: 2 additions & 1 deletion Agent/Analytics/PersistentEventStore.m
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#import "NRMACustomEvent.h"
#import "NRMARequestEvent.h"
#import "NRMANetworkErrorEvent.h"
#import "NRMAUserActionEvent.h"

@interface PersistentEventStore ()
@property (nonatomic, strong) dispatch_queue_t writeQueue;
Expand Down Expand Up @@ -216,7 +217,7 @@ + (NSDictionary *)getLastSessionEventsFromFilename:(NSString *)filename {

+ (NSSet*) classList {
NSSet *classList = [[NSSet alloc] initWithArray:@[ [NRMAPayload class],
[NRMAInteractionEvent class],[NRMAMobileEvent class], [NRMASessionEvent class],[NRMACustomEvent class],[NRMARequestEvent class],[NRMANetworkErrorEvent class],
[NRMAInteractionEvent class],[NRMAMobileEvent class], [NRMASessionEvent class],[NRMACustomEvent class],[NRMARequestEvent class],[NRMANetworkErrorEvent class], [NRMAUserActionEvent class],
[NSMutableDictionary class],[NSDictionary class],[NSString class],[NSNumber class]]];
return classList;
}
Expand Down
20 changes: 10 additions & 10 deletions Agent/General/NewRelicAgentInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -617,16 +617,6 @@ - (void) onSessionStart {
}
}

if([NRMAFlags shouldEnableGestureInstrumentation])
{
self.gestureFacade = [[NRMAUserActionFacade alloc] initWithAnalyticsController:self.analyticsController];

NRMAUserAction* foregroundGesture = [NRMAUserActionBuilder buildWithBlock:^(NRMAUserActionBuilder *builder) {
[builder withActionType:kNRMAUserActionAppLaunch];
}];
[self.gestureFacade recordUserAction:foregroundGesture];
}

// appInstallMetricGenerator will receive the 'new install' notification
// before the harvester is setup and before the task queue is set up.
// by adding the appInstallMetricGenerator to the harvestAwareListener
Expand Down Expand Up @@ -792,6 +782,11 @@ - (void)applicationWillEnterForeground {
*/
[self sessionStartInitialization];
didFireEnterBackground = NO;

NRMAUserActionBuilder* builder = [[NRMAUserActionBuilder alloc] init];
[builder withActionType:kNRMAUserActionAppLaunch];
NRMAUserAction* backgroundGesture = [builder build];
[self.analyticsController recordUserAction:backgroundGesture];
}
}
});
Expand Down Expand Up @@ -972,6 +967,11 @@ - (void) applicationDidEnterBackground {
NSTimeInterval sessionLength = [[NSDate date] timeIntervalSinceDate:self.appSessionStartDate];
#ifndef DISABLE_NRMA_EXCEPTION_WRAPPER
@try {

NRMAUserActionBuilder* builder = [[NRMAUserActionBuilder alloc] init];
[builder withActionType:kNRMAUserActionAppBackground];
NRMAUserAction* backgroundGesture = [builder build];
[self.analyticsController recordUserAction:backgroundGesture];
#endif
self.gestureFacade = nil;
[self.analyticsController sessionWillEnd];
Expand Down
11 changes: 7 additions & 4 deletions Agent/Network/NRMAHTTPUtilities.mm
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,14 @@ + (NSArray*) trackedHeaderFields
}

+ (NSMutableURLRequest*) addCrossProcessIdentifier:(NSURLRequest*)request {

NSMutableURLRequest* mutableRequest = [self makeMutable:request];

NSString* xprocess = [NRMAHarvestController configuration].cross_process_id;

NRMAHarvesterConfiguration *config = [NRMAHarvestController configuration];

if (!config) {
return mutableRequest;
}

NSString* xprocess = config.cross_process_id;
if (xprocess.length) {
[mutableRequest setValue:xprocess
forHTTPHeaderField:NEW_RELIC_CROSS_PROCESS_ID_HEADER_KEY];
Expand Down
Loading