检测iOS 10.3应用评级对话框显示的机制?

TL; DR:iOS上有什么方法可以检测iOS 10.3中添加的Storekit App Rating对话框的存在/显示吗?

我最近添加了新的应用程序评级对话框支持我的应用程序使用:

[SKStoreReviewController requestReview]; 

但是,我知道有一些使用注意事项(如此处所述 ),即对话可能会或可能不会在调用上述function时显示,不包括客户是否已经对应用程序进行了评级或客户已经暗淡对话3次。

我也知道,苹果不希望用户操作直接调用对话框的显示,因此报告的对话框的存在:

虽然您应该在应用程序的用户体验stream程中调用此方法,但评分/评论请求视图的实际显示由App Store策略pipe理。 由于此方法可能会或可能不会显示警报,因此响应button点击或其他用户操作来调用它是不合适的。

但是,这并不能阻止UX团队将这些button放在graphicsdevise中,并询问“我们能否知道对话框是否显示”?

所以,我的问题是,是否有其他间接的方式,可以确定这个对话框的介绍?

我最近一直在使用Appium对Android和iOS应用程序进行一些自动化testing,并使用Xpath来查找本机UI元素,所以只是想知道是否可以在iOS应用程序的上下文中实现相同的function。

你的问题让我思考,而且比我想象的要容易。

我的第一个想法是检查UIWindow相关的事情 – 快速看文档显示,有UIWindow相关的通知 – 太棒了! 我做了一个快速项目,订阅了所有这些项目,并提交了审查负责人。 这popup在日志中:

 method : windowDidBecomeVisibleNotification: object -> <SKStoreReviewPresentationWindow: 0x7fe14bc03670; baseClass = UIApplicationRotationFollowingWindow; frame = (0 0; 414 736); opaque = NO; gestureRecognizers = <NSArray: 0x61800004de30>; layer = <UIWindowLayer: 0x61800003baa0>> 

因此,为了检测审查控制器是否显示,您需要订阅一个通知,并检查它的object属性以找出它的类:

 - (void)viewDidLoad { [super viewDidLoad]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeVisibleNotification:) name:UIWindowDidBecomeVisibleNotification object:nil]; } - (void)windowDidBecomeVisibleNotification:(NSNotification *)notification { if ([notification.object isKindOfClass:NSClassFromString(@"SKStoreReviewPresentationWindow")]) { NSLog(@"the review request was shown!"); } } 

现在请记住, SKStoreReviewPresentationWindow是不公开的 – 所以你不能简单地写[SKStoreReviewPresentationWindow class] ,并通过使用NSClassFromString欺骗系统只是欺骗系统。 不幸的是,其他最有趣的通知, UIWindowDidResignKey ,没有发布 – 我希望主窗口会辞职,但不幸的是不是。 一些进一步的debugging还显示,主窗口仍然是关键,而不是隐藏的。 你当然可以尝试比较notification.object[UIApplication sharedApplication].window ,但也有其他的窗口显示 – UITextEffectsWindowUIRemoteKeyboardWindow ,特别是当第一次显示警报,而且它们也都不公开。

我认为这个解决scheme是一个破解 – 它很容易被苹果改变,将打破它。 但最重要的是,这可能是在审查过程中被拒绝的理由,所以请自担风险。 我在iPhone 7+模拟器,iOS 10.3,Xcode 8.3.2上testing了这个


现在,因为我们现在知道检测控制器是否显示是有可能的,所以更有趣的问题是如何检测它没有被显示。 你需要介绍一些超时之后,你会做一些事情,因为没有显示警报。 这可能会觉得你的应用程序被挂起,所以这将是一个不好的经验,为您的用户。 此外,我注意到审查控制器没有立即显示,所以它甚至更有意义,为什么苹果不推荐按下button后显示它。

那么,我已经对这个问题做了一个非常黑客的解决scheme:

警告:该解决scheme包含方法Swizzling和对象关联。 该解决scheme能够通过苹果的审查,但未来可能会打破。

由于SKStoreReviewPresentationWindowUIWindowinheritance,我已经在UIWindow上做了一个分类,每当窗口显示或隐藏时就发布事件:

 @interface MonitorObject:NSObject @property (nonatomic, weak) UIWindow* owner; -(id)init:(UIWindow*)owner; -(void)dealloc; @end @interface UIWindow (DismissNotification) + (void)load; @end #import "UIWindow+DismissNotification.h" #import <objc/runtime.h> @implementation MonitorObject -(id)init:(UIWindow*)owner { self = [super init]; self.owner = owner; [[NSNotificationCenter defaultCenter] postNotificationName:UIWindowDidBecomeVisibleNotification object:self]; return self; } -(void)dealloc { [[NSNotificationCenter defaultCenter] postNotificationName:UIWindowDidBecomeHiddenNotification object:self]; } @end @implementation UIWindow (DismissNotification) static NSString* monitorObjectKey = @"monitorKey"; static NSString* partialDescForStoreReviewWindow = @"SKStore"; + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ Class class = [self class]; SEL originalSelector = @selector(setWindowLevel:); SEL swizzledSelector = @selector(setWindowLevel_startMonitor:); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } }); } #pragma mark - Method Swizzling - (void)setWindowLevel_startMonitor:(int)level{ [self setWindowLevel_startMonitor:level]; if([self.description containsString:partialDescForStoreReviewWindow]) { MonitorObject *monObj = [[MonitorObject alloc] init:self]; objc_setAssociatedObject(self, &monitorObjectKey, monObj, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } } @end 

像这样使用它:

订阅活动:

  [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeVisibleNotification:) name:UIWindowDidBecomeVisibleNotification object:nil]; [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(windowDidBecomeHiddenNotification:) name:UIWindowDidBecomeHiddenNotification object:nil]; 

当事件发生时,对他们作出反应:

 - (void)windowDidBecomeVisibleNotification:(NSNotification *)notification { if([notification.object class] == [MonitorObject class]) { NSLog(@"Review Window shown!"); } } - (void)windowDidBecomeHiddenNotification:(NSNotification *)notification { if([notification.object class] == [MonitorObject class]) { NSLog(@"Review Window hidden!"); } } 

您可以在这里看到解决scheme的video