TL; DR:iOS上是否有某种方法可以检测iOS 10.3中添加的Storekit App Rating对话框的存在/显示?
我最近使用以下方法为我的应用添加了对新app rating dialog的支持:
[SKStoreReviewController requestReview];
但是,我知道有一些使用注意事项(如documented here),即在调用上述功能时可能会或可能不会显示对话框,不包括客户是否已对应用程序或客户已经对话3次。
我也知道Apple不希望用户操作直接调用对话框的呈现,因此会报告对话框的存在:
虽然您应该在应用的用户体验流程中调用此方法,但评级/审核请求视图的实际显示受App Store策略的约束。由于此方法可能会或可能不会显示警报,因此在响应按钮点击或其他用户操作时调用它是不合适的。
但这并不能阻止UX团队将这些按钮放在图形设计中并询问“我们能否知道对话框是否显示”?
所以,我的问题是,是否有其他间接方式可以确定此对话框的呈现?
我最近使用Appium对Android和iOS应用程序进行了一些自动化测试,并使用Xpaths查找原生UI元素,所以只是想知道是否可以在iOS应用程序的上下文中实现相同的功能。
答案 0 :(得分:9)
你的问题让我思考,这比我想象的要容易。
我的第一个想法是检查UIWindow
相关内容 - 快速查看the documentation显示,有UIWindow
相关通知 - 太棒了!我做了一个快速的项目,订阅了所有这些并提交了审查控制器。这会在日志中弹出:
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
没有发布 - 我希望主窗口会辞职,但遗憾的是没有。一些进一步的调试也表明主窗口仍然是关键而不是隐藏。您当然可以尝试将notification.object
与[UIApplication sharedApplication].window
进行比较,但也会显示其他窗口 - UITextEffectsWindow
和UIRemoteKeyboardWindow
,尤其是首次显示提醒时,他们也不公开。
我认为这个解决方案是一个黑客攻击 - 苹果公司很容易改变这个解决方案。但最重要的是,这可能是审查期间拒绝的理由,因此请自担风险。我在iPhone 7+模拟器,iOS 10.3,Xcode 8.3.2
上进行了测试现在,由于我们现在知道有点可以检测是否显示了评论控制器,因此更有趣的问题是如何检测到 NOT 显示。您需要引入一些超时,之后您将执行某些操作,因为未显示警报。这可能会让您的应用感觉被绞死,因此对您的用户来说这将是一次糟糕的体验。此外,我注意到审查控制器没有立即显示,所以更有意义的是为什么Apple不建议在按下按钮后显示它。
答案 1 :(得分:3)
好吧,我已经针对这个问题做了一个非常黑的解决方案:
警告:该解决方案包含Swizzling和对象关联方法。 该解决方案能够通过Apple审核,但未来可能会中断。
由于SKStoreReviewPresentationWindow
继承自UIWindow
,我在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!");
}
}