Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
## 26.1.3
* Added a content configuration option to reload the content web view when its load stalls, enabled via "enableContentReloadOnStall" on "CountlyContentConfig", with a configurable stall timeout "setContentReloadOnStallTimeout:" in milliseconds (default 1000).

## 26.1.2
* ! Minor breaking change ! Raised the minimum supported deployment target to iOS 12 and tvOS 12 (previously iOS 10 and tvOS 10) for compatibility with Xcode 26 and the iOS 26 SDK. Apps targeting iOS 10/11 or tvOS 10/11 are no longer supported.

Expand Down
2 changes: 1 addition & 1 deletion Countly-PL.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Countly-PL'
s.version = '26.1.2'
s.version = '26.1.3'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.'
s.homepage = 'https://github.com/Countly/countly-sdk-ios'
Expand Down
2 changes: 2 additions & 0 deletions Countly.m
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,8 @@ - (void)startWithConfig:(CountlyConfig *)config
if(config.content.getWebViewDisplayOption){
CountlyContentBuilderInternal.sharedInstance.webViewDisplayOption = config.content.getWebViewDisplayOption;
}
CountlyContentBuilderInternal.sharedInstance.enableContentReloadOnStall = config.content.getEnableContentReloadOnStall;
CountlyContentBuilderInternal.sharedInstance.contentReloadOnStallTimeout = config.content.getContentReloadOnStallTimeout / 1000.0;
#endif

[CountlyPerformanceMonitoring.sharedInstance startWithConfig:config.apm];
Expand Down
2 changes: 1 addition & 1 deletion Countly.podspec
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = 'Countly'
s.version = '26.1.2'
s.version = '26.1.3'
s.license = { :type => 'MIT', :file => 'LICENSE' }
s.summary = 'Countly is an innovative, real-time, open source mobile analytics platform.'
s.homepage = 'https://github.com/Countly/countly-sdk-ios'
Expand Down
4 changes: 2 additions & 2 deletions Countly.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -838,7 +838,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 26.1.2;
MARKETING_VERSION = 26.1.3;
PRODUCT_BUNDLE_IDENTIFIER = ly.count.CountlyiOSSDK;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down Expand Up @@ -870,7 +870,7 @@
"@loader_path/Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
MARKETING_VERSION = 26.1.2;
MARKETING_VERSION = 26.1.3;
PRODUCT_BUNDLE_IDENTIFIER = ly.count.CountlyiOSSDK;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
PROVISIONING_PROFILE_SPECIFIER = "";
Expand Down
2 changes: 1 addition & 1 deletion CountlyCommon.m
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ @interface CountlyCommon ()
#endif
@end

NSString* const kCountlySDKVersion = @"26.1.2";
NSString* const kCountlySDKVersion = @"26.1.3";
NSString* const kCountlySDKName = @"objc-native-ios";

NSString* const kCountlyErrorDomain = @"ly.count.ErrorDomain";
Expand Down
2 changes: 2 additions & 0 deletions CountlyContentBuilderInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, assign) NSTimeInterval zoneTimerInterval;
@property (nonatomic) ContentCallback contentCallback;
@property (nonatomic, assign) WebViewDisplayOption webViewDisplayOption;
@property (nonatomic, assign) BOOL enableContentReloadOnStall;
@property (nonatomic, assign) NSTimeInterval contentReloadOnStallTimeout; // seconds
@property (nonatomic, assign) int contentInitialDelay;

+ (instancetype)sharedInstance;
Expand Down
20 changes: 20 additions & 0 deletions CountlyContentConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,26 @@ typedef void (^ContentCallback)(ContentStatus contentStatus, NSDictionary<NSStri
- (void) setWebviewDisplayOption:(WebViewDisplayOption) webViewDisplayOption;

- (WebViewDisplayOption)getWebViewDisplayOption;

/**
* This is an experimental feature and it can have breaking changes.
* Enables reloading the content web view when a load stalls (does not appear within ~1
* second) instead of closing it. A reload reuses the already-warm connection and cached
* assets, which recovers loads that fail because a server/edge drops the initial burst of
* parallel resource connections. This is a one-way switch: once enabled it stays enabled.
* Disabled by default.
*/
- (void)enableContentReloadOnStall;
- (BOOL)getEnableContentReloadOnStall;

/**
* This is an experimental feature and it can have breaking changes.
* Sets how long a content load may stall (not appear) before the SDK reloads it, in
* milliseconds. Only used when reload-on-stall is enabled (see enableContentReloadOnStall).
* Default is 1000 (1 second). Can be changed at any time before starting the SDK.
*/
- (void)setContentReloadOnStallTimeout:(NSUInteger)milliseconds;
- (NSUInteger)getContentReloadOnStallTimeout;
#endif

NS_ASSUME_NONNULL_END
Expand Down
27 changes: 26 additions & 1 deletion CountlyContentConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ @interface CountlyContentConfig ()
@property (nonatomic) ContentCallback contentCallback;
@property (nonatomic) NSUInteger zoneTimerInterval;
@property (nonatomic) WebViewDisplayOption webViewDisplayOption;
@property (nonatomic) BOOL contentReloadOnStallEnabled;
@property (nonatomic) NSUInteger contentReloadOnStallTimeoutMs;
#endif
@end

Expand All @@ -20,8 +22,11 @@ - (instancetype)init
{
if (self = [super init])
{
#if (TARGET_OS_IOS)
_contentReloadOnStallTimeoutMs = 1000; // default 1 second
#endif
}

return self;
}

Expand Down Expand Up @@ -58,6 +63,26 @@ - (WebViewDisplayOption)getWebViewDisplayOption;
{
return _webViewDisplayOption;
}

- (void)enableContentReloadOnStall
{
_contentReloadOnStallEnabled = YES;
}

- (BOOL)getEnableContentReloadOnStall
{
return _contentReloadOnStallEnabled;
}

- (void)setContentReloadOnStallTimeout:(NSUInteger)milliseconds
{
_contentReloadOnStallTimeoutMs = milliseconds;
}

- (NSUInteger)getContentReloadOnStallTimeout
{
return _contentReloadOnStallTimeoutMs;
}
#endif

@end
36 changes: 36 additions & 0 deletions CountlyTests/CountlyContentBuilderTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class CountlyContentBuilderTests: CountlyBaseTestCase {

override func tearDown() {
CountlyContentBuilderInternal.sharedInstance().exitContentZone()
// Reset reload-on-stall state so it can't leak into later tests.
CountlyContentBuilderInternal.sharedInstance().enableContentReloadOnStall = false
CountlyContentBuilderInternal.sharedInstance().contentReloadOnStallTimeout = 0
Countly.sharedInstance().halt(true)
MockURLProtocol.requestHandler = nil
super.tearDown()
Expand Down Expand Up @@ -287,5 +290,38 @@ class CountlyContentBuilderTests: CountlyBaseTestCase {

wait(for: [fetchAfterExit], timeout: 15)
}

/**
* <pre>
* Content reload-on-stall configuration plumbs through to the internal builder.
*
* 1- A fresh CountlyContentConfig defaults the stall timeout to 1000 ms and reload disabled
* 2- enableContentReloadOnStall and setContentReloadOnStallTimeout: round-trip on the config
* 3- After start, the internal builder reflects the enabled flag and the timeout
* converted from milliseconds to seconds (2500 ms -> 2.5 s)
* </pre>
*/
func test_contentReloadOnStall_configDefaultsAndPlumbing() {
// 1- Fresh config object: default 1000 ms, reload disabled.
let freshContent = CountlyContentConfig()
XCTAssertEqual(freshContent.getContentReloadOnStallTimeout(), 1000)
XCTAssertFalse(freshContent.getEnableContentReloadOnStall())

// 2- Round-trip on the config used for start.
let config = createContentTestConfig()
config.content().setContentReloadOnStallTimeout(2500)
config.content().enableContentReloadOnStall()
XCTAssertEqual(config.content().getContentReloadOnStallTimeout(), 2500)
XCTAssertTrue(config.content().getEnableContentReloadOnStall())

// 3- Plumbed to the internal builder on start, converted ms -> seconds.
MockURLProtocol.requestHandler = { request in
let response = HTTPURLResponse(url: request.url!, statusCode: 200, httpVersion: "HTTP/1.1", headerFields: nil)!
return ("{}".data(using: .utf8)!, response, nil)
}
Countly.sharedInstance().start(with: config)
XCTAssertTrue(CountlyContentBuilderInternal.sharedInstance().enableContentReloadOnStall)
XCTAssertEqual(CountlyContentBuilderInternal.sharedInstance().contentReloadOnStallTimeout, 2.5, accuracy: 0.0001)
}
}
#endif
3 changes: 3 additions & 0 deletions CountlyTests/CountlyWebViewManager+Tests.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

@property(nonatomic) BOOL webViewClosed;
@property(nonatomic) BOOL hasAppeared;
@property(nonatomic) NSInteger resourceRetryCount;
@property(nonatomic) BOOL retryInProgress;
@property(nonatomic, strong) NSTimer *loadTimeoutTimer;
@property(nonatomic, strong) NSDate *loadStartDate;
@property(nonatomic, copy) void (^dismissBlock)(void);
Expand All @@ -16,6 +18,7 @@
- (void)notifyPageLoaded;
- (void)loadDidTimeout;
- (void)closeWebView;
- (void)retryOrCloseWebViewForReason:(NSString *)reason;

@end
#endif
Loading
Loading