Skip to content
Merged
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 @@ -11,7 +11,7 @@

@interface ViewLoadLoadingIndicatorState : NSObject

@property(nonatomic, strong) NSArray<BugsnagPerformanceSpanCondition *> *conditions;
@property(nonatomic, strong) NSMutableArray<BugsnagPerformanceSpanCondition *> *conditions;
@property(nonatomic, strong) BugsnagPerformanceSpan *loadingIndicatorSpan;
@property(nonatomic) BOOL needsSpanUpdate;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,14 @@
#import "ViewLoadLoadingIndicatorState.h"

@implementation ViewLoadLoadingIndicatorState

- (instancetype)init
{
self = [super init];
if (self) {
_conditions = [NSMutableArray array];
}
return self;
}

@end
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ class ViewLoadLoadingIndicatorsHandler {
public:
virtual void onLoadingIndicatorWasAdded(BugsnagPerformanceLoadingIndicatorView *loadingIndicator) noexcept = 0;
virtual void onViewControllerUpdatedView(UIViewController *viewController) noexcept = 0;
virtual void onViewControllerDidAppear(UIViewController *viewController) noexcept = 0;
virtual void setCallbacks(ViewLoadLoadingIndicatorsHandlerCallbacks *callbacks) noexcept = 0;
virtual ~ViewLoadLoadingIndicatorsHandler() {}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ class ViewLoadLoadingIndicatorsHandlerImpl: public ViewLoadLoadingIndicatorsHand

void onLoadingIndicatorWasAdded(BugsnagPerformanceLoadingIndicatorView *loadingIndicator) noexcept;
void onViewControllerUpdatedView(UIViewController *viewController) noexcept;
void onViewControllerDidAppear(UIViewController *viewController) noexcept;
void setCallbacks(ViewLoadLoadingIndicatorsHandlerCallbacks *callbacks) noexcept {
callbacks_ = callbacks;
}
Expand All @@ -39,6 +40,10 @@ class ViewLoadLoadingIndicatorsHandlerImpl: public ViewLoadLoadingIndicatorsHand

void updateIndicatorsState(BugsnagPerformanceLoadingIndicatorView *loadingIndicator, ViewLoadLoadingIndicatorState *state) noexcept;
ViewLoadLoadingIndicatorState *newState(BugsnagPerformanceLoadingIndicatorView *loadingIndicator) noexcept;
void addToState(ViewLoadLoadingIndicatorState *state,
BugsnagPerformanceLoadingIndicatorView *loadingIndicator,
ViewLoadInstrumentationState *viewLoadState,
BOOL isFirstViewController) noexcept;
void updateLoadingIndicators(UIView *view) noexcept;
bool checkNeedsSpanUpdate(BugsnagPerformanceLoadingIndicatorView *loadingIndicator,
BugsnagPerformanceSpanContext *parentContext) noexcept;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,12 @@
updateLoadingIndicators(viewController.view);
}

void
ViewLoadLoadingIndicatorsHandlerImpl::onViewControllerDidAppear(UIViewController *viewController) noexcept {
std::lock_guard<std::mutex> guard(mutex_);
updateLoadingIndicators(viewController.view);
}

#pragma mark Helpers

void
Expand All @@ -43,46 +49,54 @@

ViewLoadLoadingIndicatorState *
ViewLoadLoadingIndicatorsHandlerImpl::newState(BugsnagPerformanceLoadingIndicatorView *loadingIndicator) noexcept {
NSMutableArray<BugsnagPerformanceSpanCondition *> *newConditions = [NSMutableArray array];
auto needsSpanUpdate = false;
auto hasFoundFirstViewController = false;
BugsnagPerformanceSpan *loadingIndicatorSpan;

auto state = [ViewLoadLoadingIndicatorState new];
UIView *view = loadingIndicator;
while (view != nil) {
ViewLoadInstrumentationState *state = repository_->getInstrumentationState(view);
__strong UIViewController *viewController = state.viewController;
if (state != nil &&
state.overallSpan.isValid &&
viewController != nil) {

if (callbacks_.onLoading) {
BugsnagPerformanceSpanCondition *condition = callbacks_.onLoading(viewController);
if (condition != nil) {
[newConditions addObject:condition];
}
}
if (callbacks_.getParentContext &&
!hasFoundFirstViewController &&
loadingIndicator.name != nil) {
BugsnagPerformanceSpanContext *parentContext = callbacks_.getParentContext(viewController);
needsSpanUpdate = checkNeedsSpanUpdate(loadingIndicator, parentContext);
if (parentContext && needsSpanUpdate) {
loadingIndicatorSpan = spanFactory_->startLoadingIndicatorSpan(loadingIndicator.name, parentContext);
}
}
ViewLoadInstrumentationState *viewLoadState = repository_->getInstrumentationState(view);
if (viewLoadState.overallSpan.isValid && viewLoadState.viewController != nil) {
addToState(state,
loadingIndicator,
viewLoadState,
!hasFoundFirstViewController);
hasFoundFirstViewController = true;
}

view = view.superview;
}

auto state = [ViewLoadLoadingIndicatorState new];
state.conditions = newConditions;
state.loadingIndicatorSpan = loadingIndicatorSpan;
state.needsSpanUpdate = needsSpanUpdate;
return state;
}

void
ViewLoadLoadingIndicatorsHandlerImpl::addToState(ViewLoadLoadingIndicatorState *state,
BugsnagPerformanceLoadingIndicatorView *loadingIndicator,
ViewLoadInstrumentationState *viewLoadState,
BOOL isFirstViewController) noexcept {
__strong UIViewController *viewController = viewLoadState.viewController;

if (viewController == nil || !viewLoadState.hasAppeared) {
return;
}
if (callbacks_.onLoading) {
BugsnagPerformanceSpanCondition *condition = callbacks_.onLoading(viewController);
if (condition != nil) {
[state.conditions addObject:condition];
}
}
if (callbacks_.getParentContext &&
isFirstViewController &&
loadingIndicator.name != nil) {
BugsnagPerformanceSpanContext *parentContext = callbacks_.getParentContext(viewController);
BOOL needsSpanUpdate = checkNeedsSpanUpdate(loadingIndicator, parentContext);
if (parentContext && needsSpanUpdate) {
state.needsSpanUpdate = needsSpanUpdate;
state.loadingIndicatorSpan = spanFactory_->startLoadingIndicatorSpan(loadingIndicator.name, parentContext);
}
}
}

void
ViewLoadLoadingIndicatorsHandlerImpl::updateLoadingIndicators(UIView *view) noexcept {
if ([view isKindOfClass:[BugsnagPerformanceLoadingIndicatorView class]]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@
state.overallSpan);
originalImplementation();
[state.viewDidAppearSpan end];
state.hasAppeared = true;
updateViewIfNeeded(state, viewController);
loadingIndicatorsHandler_->onViewControllerDidAppear(viewController);
endOverallSpan(state, viewController, CFAbsoluteTimeGetCurrent());
}

Expand Down Expand Up @@ -167,14 +169,6 @@
loadingIndicatorsHandler_->onLoadingIndicatorWasAdded(loadingIndicator);
}

//void
//ViewLoadLifecycleHandlerImpl::onLoadingIndicatorWasRemoved(BugsnagPerformanceLoadingIndicatorView *loadingIndicator) noexcept {
// if (loadingIndicator == nil) {
// return;
// }
// loadingIndicatorsHandler_->onLoadingIndicatorWasRemoved(loadingIndicator);
//}

#pragma mark Helpers

void
Expand All @@ -186,10 +180,7 @@
BugsnagPerformanceSpan *overallSpan = state.overallSpan;
[crosstalkAPI_ willEndViewLoadSpan:overallSpan viewController:viewController];

if (state.loadingPhaseSpan != nil) {
// Adjust span start time to reflect the view appearing time
[state.loadingPhaseSpan updateStartTime:[NSDate date]];
} else {
if (state.loadingPhaseSpan == nil) {
[state.overallSpan blockWithTimeout:kLoadingBlockTimeout];
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ typedef void (^ ViewLoadInstrumentationStateOnDeallocCallback)(ViewLoadInstrumen
@interface ViewLoadInstrumentationState : NSObject

@property (nonatomic) BOOL isMarkedAsPreloaded;
@property (nonatomic) BOOL hasAppeared;
@property (nonatomic, nullable, weak) UIViewController *viewController;
@property (nonatomic, nullable, weak) UIView *view;
@property (nonatomic, nullable, strong) BugsnagPerformanceSpan *overallSpan;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,19 +32,21 @@ - (instancetype)initWithSpan:(BugsnagPerformanceSpan *)span {
- (void)setType:(NSString *_Nullable)type {
@synchronized (self) {
__strong BugsnagPerformanceSpan *span = self.span;
if (span == nil || !span.isValid) {
if (span == nil || !(span.isValid || span.isBlocked)) {
return;
}

if (type == nil) {
[span updateName:self.spanPreviousName];
} else {
NSString *typeStr = type;
// Original span name should be in format "[AppStart/$platform$type]"
NSString *newName = [NSString stringWithFormat:@"%@%@", self.spanPreviousName, typeStr];
[span updateName:newName];
}
[span setAttribute:AppStartNameAttribute withValue:type];
[span forceMutate:^{
if (type == nil) {
[span updateName:self.spanPreviousName];
} else {
NSString *typeStr = type;
// Original span name should be in format "[AppStart/$platform$type]"
NSString *newName = [NSString stringWithFormat:@"%@%@", self.spanPreviousName, typeStr];
[span updateName:newName];
}
[span setAttribute:AppStartNameAttribute withValue:type];
}];
}
}

Expand Down
12 changes: 12 additions & 0 deletions features/default/loading_indicator.feature
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" is a child of span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController"
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController"
Expand Down Expand Up @@ -69,6 +70,8 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" duration is equal or greater than 2.0
* a span named "SimpleStopScenarioIndicatorName" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" started
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController" ended before a span named "SimpleStopScenarioIndicatorName" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewSimpleStopScenario_ViewController"
Expand Down Expand Up @@ -105,6 +108,7 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" is a child of span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController"
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController"
Expand Down Expand Up @@ -147,6 +151,8 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" duration is equal or greater than 2.0
* a span named "SimpleRemoveScenarioIndicatorName" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" started
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController" ended before a span named "SimpleRemoveScenarioIndicatorName" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewSimpleRemoveScenario_ViewController"
Expand Down Expand Up @@ -196,6 +202,8 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" duration is equal or greater than 3.0
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" duration is equal or greater than 3.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" started
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController"
Expand Down Expand Up @@ -291,6 +299,8 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" duration is equal or greater than 3.0
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" started
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" started
* every span field "kind" equals 1
* a span string attribute "bugsnag.span.category" equals "view_load"
* a span string attribute "bugsnag.view.name" equals "Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController"
Expand Down Expand Up @@ -350,6 +360,8 @@ Feature: LoadingIndicator view to mark data loading phase
* a span named "[ViewLoad/UIKit]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended at the same time as a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController"
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" duration is equal or greater than 3.0
* a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" duration is equal or greater than 2.0
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ParentViewController" started
* a span named "[ViewLoadPhase/viewDidAppear]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" ended before a span named "[ViewLoadPhase/viewDataLoading]/Fixture.LoadingIndicatorViewNestedViewStopScenario_ChildViewController" started
* a span named "NestedViewStopScenarioIndicatorName1" duration is equal or greater than 4.0
* a span named "NestedViewStopScenarioIndicatorName2" duration is equal or greater than 2.0
* a span named "NestedViewStopScenarioIndicatorName3" duration is equal or greater than 3.0
Expand Down
8 changes: 8 additions & 0 deletions features/default/plugins.feature
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,11 @@ Feature: Plugins
And I wait to receive at least 5 spans
Then the trace "Content-Type" header equals "application/json"
* a span field "name" equals "[AppStart/iOSCold]"

Scenario: App start type plugin correctly changes the span name during data loading phase of the first view
Given I run "AppStartTypeLoadingScenario"
Then I relaunch the app after shutdown
And I wait to receive at least 6 spans
Then the trace "Content-Type" header equals "application/json"
* a span field "name" equals "[AppStart/iOSCold]AppStartTypeLoadingScenario"

Loading