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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## XX.XX.XX
* Added support for reporting the app's current theme (light or dark) when presenting feedback widgets, rating widgets, and content, so they are displayed in matching conditions.
* Added visionOS support for feedback widgets (NPS, Survey, and Rating) and content.

* Mitigated an issue where feedback widgets could be closed automatically after about a minute even while the user was still interacting with them.

Expand Down
4 changes: 2 additions & 2 deletions Countly.m
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ - (BOOL)isAutoViewTrackingActive
}
#endif
#pragma mark - Star Rating
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)

- (void)askForStarRating:(void(^)(NSInteger rating))completion
{
Expand Down Expand Up @@ -1604,7 +1604,7 @@ - (void)attemptToSendStoredRequests
}

#pragma mark - Interfaces
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
- (CountlyContentBuilder *) content
{
return CountlyContentBuilder.sharedInstance;
Expand Down
2 changes: 1 addition & 1 deletion Countly.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Pod::Spec.new do |s|
core.public_header_files = 'Countly.h', 'CountlyUserDetails.h', 'CountlyConfig.h', 'CountlyFeedbackWidget.h', 'CountlyRCData.h', 'CountlyRemoteConfig.h', 'CountlyViewTracking.h', 'CountlyExperimentInformation.h', 'CountlyAPMConfig.h', 'CountlySDKLimitsConfig.h', 'Resettable.h', "CountlyCrashesConfig.h", "CountlyCrashData.h", "CountlyContentBuilder.h", "CountlyExperimentalConfig.h", "CountlyContentConfig.h", "CountlyFeedbacks.h"
core.preserve_path = 'countly_dsym_uploader.sh'
core.ios.frameworks = ['Foundation', 'UIKit', 'UserNotifications', 'CoreLocation', 'WebKit', 'CoreTelephony', 'WatchConnectivity']
core.visionos.frameworks = ['Foundation', 'UIKit', 'UserNotifications', 'CoreLocation']
core.visionos.frameworks = ['Foundation', 'UIKit', 'UserNotifications', 'CoreLocation', 'WebKit']
end

s.subspec 'NotificationService' do |ns|
Expand Down
10 changes: 8 additions & 2 deletions CountlyCommon.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#define CLY_LOG_D(fmt, ...) CountlyInternalLog(CLYInternalLogLevelDebug, fmt, ##__VA_ARGS__)
#define CLY_LOG_V(fmt, ...) CountlyInternalLog(CLYInternalLogLevelVerbose, fmt, ##__VA_ARGS__)

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
#import <UIKit/UIKit.h>
#import <WebKit/WebKit.h>
#endif
Expand Down Expand Up @@ -117,6 +117,12 @@ void CountlyPrint(NSString *stringToPrint);
- (UIViewController *)topViewController;
- (void)tryPresentingViewController:(UIViewController *)viewController;
- (void)tryPresentingViewController:(UIViewController *)viewController withCompletion:(void (^ __nullable) (void))completion;
+ (UIWindow *)keyWindow;
+ (CGRect)screenBounds;
#endif

#if (TARGET_OS_IOS || TARGET_OS_VISION)
- (UIInterfaceOrientation)interfaceOrientation;
#endif

- (void)observeDeviceOrientationChanges;
Expand All @@ -133,7 +139,7 @@ void CountlyPrint(NSString *stringToPrint);
@end


#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
@interface CLYInternalViewController : UIViewController <WKNavigationDelegate>
@property (nonatomic, weak) WKWebView* webView;
@end
Expand Down
118 changes: 92 additions & 26 deletions CountlyCommon.m
Original file line number Diff line number Diff line change
Expand Up @@ -281,13 +281,76 @@ - (void)finishBackgroundTask
#endif
}

#if (TARGET_OS_IOS || TARGET_OS_TV)
- (UIViewController *)topViewController
#if (TARGET_OS_IOS || TARGET_OS_VISION || TARGET_OS_TV)
+ (UIWindow *)keyWindow
{
if (@available(iOS 13.0, *))
{
// connectedScenes is an unordered NSSet, so never rely on iteration order: search
// explicitly for the correct window rather than acting on whichever scene comes first.
NSArray<UIScene *> *scenes = UIApplication.sharedApplication.connectedScenes.allObjects;

// 1) The key window of the foreground-active scene (the one the user is interacting with).
for (UIScene *scene in scenes)
{
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]])
{
for (UIWindow *window in ((UIWindowScene *)scene).windows)
if (window.isKeyWindow)
return window;
}
}

// 2) Any key window across all scenes.
for (UIScene *scene in scenes)
{
if ([scene isKindOfClass:[UIWindowScene class]])
{
for (UIWindow *window in ((UIWindowScene *)scene).windows)
if (window.isKeyWindow)
return window;
}
}

// 3) Fall back to the first window of a foreground-active scene.
for (UIScene *scene in scenes)
{
if (scene.activationState == UISceneActivationStateForegroundActive && [scene isKindOfClass:[UIWindowScene class]])
{
UIWindow *window = ((UIWindowScene *)scene).windows.firstObject;
if (window)
return window;
}
}
}
#if (TARGET_OS_VISION)
return nil;
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return UIApplication.sharedApplication.keyWindow;
#pragma GCC diagnostic pop
#endif
}

+ (CGRect)screenBounds
{
UIWindow *window = [self keyWindow];
if (window)
return window.bounds;
#if (TARGET_OS_VISION)
return CGRectZero;
#else
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
UIViewController* topVC = UIApplication.sharedApplication.keyWindow.rootViewController;
return UIScreen.mainScreen.bounds;
#pragma GCC diagnostic pop
#endif
}

- (UIViewController *)topViewController
{
UIViewController* topVC = CountlyCommon.keyWindow.rootViewController;

while (YES)
{
Expand Down Expand Up @@ -359,27 +422,16 @@ - (NSURLSession *)ImmediateURLSession
return [NSURLSession sessionWithConfiguration:immediateConfig];
}

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
- (bool) hasTopNotch:(UIEdgeInsets)safeArea
{
return safeArea.top >= 44;
}
#endif

- (CGSize)getWindowSize{
#if (TARGET_OS_IOS)
UIWindow *window = nil;

if (@available(iOS 13.0, *)) {
for (UIScene *scene in UIApplication.sharedApplication.connectedScenes) {
if ([scene isKindOfClass:[UIWindowScene class]]) {
window = ((UIWindowScene *)scene).windows.firstObject;
break;
}
}
} else {
window = [[UIApplication sharedApplication].delegate window];
}
#if (TARGET_OS_IOS || TARGET_OS_VISION)
UIWindow *window = [CountlyCommon keyWindow];

if (!window) return CGSizeZero;

Expand All @@ -402,12 +454,32 @@ - (CGSize)getWindowSize{
#endif
}

#if (TARGET_OS_IOS || TARGET_OS_VISION)
- (UIInterfaceOrientation)interfaceOrientation
{
if (@available(iOS 13.0, *))
{
UIWindowScene *windowScene = [CountlyCommon keyWindow].windowScene;
if (windowScene)
return windowScene.interfaceOrientation;
}
#if (TARGET_OS_IOS)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
return UIApplication.sharedApplication.statusBarOrientation;
#pragma GCC diagnostic pop
#else
return UIInterfaceOrientationPortrait;
#endif
}
#endif


@end


#pragma mark - Internal ViewController
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
@implementation CLYInternalViewController : UIViewController

- (void)viewWillLayoutSubviews
Expand All @@ -421,10 +493,7 @@ - (void)viewWillLayoutSubviews
UIEdgeInsets insets = UIEdgeInsetsZero;
if (@available(iOS 11.0, *))
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
insets = UIApplication.sharedApplication.keyWindow.safeAreaInsets;
#pragma GCC diagnostic pop
insets = CountlyCommon.keyWindow.safeAreaInsets;
}

self.webView.navigationDelegate = self;
Expand Down Expand Up @@ -530,10 +599,7 @@ - (void)positionToTopRight:(BOOL)shouldConsiderStatusBar
{
if (@available(iOS 11.0, *))
{
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
CGFloat top = UIApplication.sharedApplication.keyWindow.safeAreaInsets.top;
#pragma GCC diagnostic pop
CGFloat top = CountlyCommon.keyWindow.safeAreaInsets.top;

if (top)
{
Expand Down
4 changes: 2 additions & 2 deletions CountlyConsentManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ - (void)setConsentForFeedback:(BOOL)consentForFeedback
{
_consentForFeedback = consentForFeedback;

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
if (consentForFeedback)
{
CLY_LOG_D(@"Consent for Feedback is given.");
Expand Down Expand Up @@ -499,7 +499,7 @@ - (void)setConsentForContent:(BOOL)consentForContent
else
{
CLY_LOG_D(@"Consent for Content is cancelled.");
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
[CountlyContentBuilderInternal.sharedInstance exitContentZone];
#endif
}
Expand Down
4 changes: 2 additions & 2 deletions CountlyContentBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@


#import <Foundation/Foundation.h>
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
#import <UIKit/UIKit.h>
#endif
NS_ASSUME_NONNULL_BEGIN
@interface CountlyContentBuilder: NSObject
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
+ (instancetype)sharedInstance;

/**
Expand Down
2 changes: 1 addition & 1 deletion CountlyContentBuilder.m
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
#import "CountlyCommon.h"

@implementation CountlyContentBuilder
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
+ (instancetype)sharedInstance
{
if (!CountlyCommon.sharedInstance.hasStarted)
Expand Down
4 changes: 2 additions & 2 deletions CountlyContentBuilderInternal.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
// Please visit www.count.ly for more information.

#import <Foundation/Foundation.h>
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
#import <UIKit/UIKit.h>
#endif
#import "CountlyCommon.h"
NS_ASSUME_NONNULL_BEGIN
@interface CountlyContentBuilderInternal: NSObject
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
@property (nonatomic, strong) NSArray<NSString *> *currentTags;
@property (nonatomic, assign) NSTimeInterval zoneTimerInterval;
@property (nonatomic) ContentCallback contentCallback;
Expand Down
6 changes: 3 additions & 3 deletions CountlyContentBuilderInternal.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ @implementation CountlyContentBuilderInternal {
dispatch_queue_t _contentQueue;
}

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
+ (instancetype)sharedInstance {
static CountlyContentBuilderInternal *instance = nil;
static dispatch_once_t onceToken;
Expand Down Expand Up @@ -368,7 +368,7 @@ - (NSString *)resolutionJson {
//TODO: check why area is not clickable and safearea things
CGSize size = [CountlyCommon.sharedInstance getWindowSize];

UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
UIInterfaceOrientation orientation = [CountlyCommon.sharedInstance interfaceOrientation];
BOOL isLandscape = UIInterfaceOrientationIsLandscape(orientation);

CGFloat lHpW = isLandscape ? size.height : size.width;
Expand Down Expand Up @@ -404,7 +404,7 @@ - (void)showContentWithHtmlPath:(NSString *)urlString placementCoordinates:(NSDi

dispatch_async(dispatch_get_main_queue(), ^ {
// Detect screen orientation
UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
UIInterfaceOrientation orientation = [CountlyCommon.sharedInstance interfaceOrientation];
BOOL isLandscape = UIInterfaceOrientationIsLandscape(orientation);

// Get the appropriate coordinates based on the orientation
Expand Down
4 changes: 2 additions & 2 deletions CountlyContentConfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

NS_ASSUME_NONNULL_BEGIN

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
typedef enum : NSUInteger
{
COMPLETED,
Expand All @@ -32,7 +32,7 @@ typedef BOOL (^ContentURLHandler)(NSURL *url);

@interface CountlyContentConfig : NSObject

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
/**
* This is an experimental feature and it can have breaking changes
* Register global completion blocks to be executed on content.
Expand Down
6 changes: 3 additions & 3 deletions CountlyContentConfig.m
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#import "CountlyCommon.h"

@interface CountlyContentConfig ()
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
@property (nonatomic) ContentCallback contentCallback;
@property (nonatomic) NSUInteger zoneTimerInterval;
@property (nonatomic) WebViewDisplayOption webViewDisplayOption;
Expand All @@ -24,15 +24,15 @@ - (instancetype)init
{
if (self = [super init])
{
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
_contentReloadOnStallTimeoutMs = 1000; // default 1 second
#endif
}

return self;
}

#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)
-(void)setGlobalContentCallback:(ContentCallback) callback
{
_contentCallback = callback;
Expand Down
2 changes: 1 addition & 1 deletion CountlyFeedbackWidget.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ extern NSString* const kCountlyReservedEventNPS;
extern NSString* const kCountlyReservedEventRating;

@interface CountlyFeedbackWidget : NSObject
#if (TARGET_OS_IOS)
#if (TARGET_OS_IOS || TARGET_OS_VISION)

@property (nonatomic, readonly) CLYFeedbackWidgetType type;
@property (nonatomic, readonly) NSString* ID;
Expand Down
Loading
Loading