Application tried to present <_SFAppPasswordSavingViewController> 崩溃治理

一. 背景

线上捕获到了关于_SFAppPasswordSavingViewController的相关崩溃。具体崩溃堆栈如下:

这个崩溃出现在钱包提现输入密码页面。

二. 原因分析

从崩溃原因分析:

vbnet 复制代码
Application Specific Information:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Application tried to present modally a view controller <_SFAppPasswordSavingViewController: 0x120d60030> that is already being presented by <UIKeyboardHiddenViewController_Save: 0x123849e30>.
UserInfo:(null)'

是因为Application尝试去present存储密码_SFAppPasswordSavingViewController页面,但这个_SFAppPasswordSavingViewController已经present过,导致出现这个崩溃。而从产生该崩溃系统分析来看,这个崩溃基本发生在iOS16及相关系统,从目前来看应该是系统原因,主要出现键盘弹起自动密码填充界面。

我们从苹果论坛上面也能看到关于这个crash的相关反馈:

developer.apple.com/forums/thre...

里面有人提到针对这个崩溃的解决方法如下:

github.com/hackiftekha...

也就是hookUIViewControllerpresentViewController:animated:completion:方法,然后在hook方法里面添加判断当前的VCpresentedViewController是否有值,如果为nil,表示还没present过,直接调用``presentViewController:animated:completion:,如果有值,表示当前VC已经present过了,直接return`。

scss 复制代码
#import "UIViewController+PresentKBHook.h"

@implementation UIViewController (PresentKBHook)

+ (void)load {
    [UIViewController ff_swizzleInstanceMethodWithSrcClass:[UIViewController class] srcSel:@selector(presentViewController:animated:completion:) swizzledSel:@selector(cr_presentViewController:animated:completion:)];
}

- (void)cr_presentViewController:(UIViewController *)viewControllerToPresent animated:(BOOL)flag completion:(void (^)(void))completion {
    if (self.presentedViewController) {
        NSLog(@"[present devil]self=%@,toPresent=%@",self.description, viewControllerToPresent.description);
    }else{
        [self cr_presentViewController:viewControllerToPresent animated:flag completion:completion];
    }
}

/**
 Implementation of exchanging two object methods
 */
+ (void)ff_swizzleInstanceMethodWithSrcClass:(Class)srcClass
                                      srcSel:(SEL)srcSel
                                 swizzledSel:(SEL)swizzledSel{
    
    Method srcMethod = class_getInstanceMethod(srcClass, srcSel);
    Method swizzledMethod = class_getInstanceMethod(srcClass, swizzledSel);
    if (!srcClass || !srcMethod || !swizzledMethod) return;
    
    //Add a layer of protection measures. If the method is added successfully, it means that the method does not exist in this class, but exists in the parent class, and the method of the parent class cannot be exchanged, otherwise the method will crash when the parent class object calls the method; Adding failure indicates that the method exists in this class
    BOOL addMethod = class_addMethod(srcClass, srcSel, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
    if (addMethod){
        //After the IMP is successfully implemented by adding methods, the original implementation will be replaced with the swizzledMethod method to achieve method exchange without affecting the implementation of the parent method
        class_replaceMethod(srcClass, swizzledSel, method_getImplementation(srcMethod), method_getTypeEncoding(srcMethod));
    }else{
        //Adding failed, calling the implementation of the two interactive methods
        method_exchangeImplementations(srcMethod, swizzledMethod);
    }
}

@end

三. 解决方案

  • 添加降级开关、添加系统版本判断、只在iOS 16相关系统启用该崩溃治理。
swift 复制代码
    /// 键盘崩溃修复策略
    public struct FJFPresentCrashFixPolicy: HandyJSON {
        /// presnet事件 崩溃修复
        public var viewControllerPresentCrashFixEnable: Bool = false
        /// 开始 修复 版本
        public var startFixVersion: String = "16.0"
        /// 结束 修复 版本
        public var endFixVersion: String = "17.0"
        
        public init() {}
    }
    
    /// 是否为需要修复的系统版本
     public static func isNeedToFixSystemVersionCrash(startVersion: String, endVersion: String) -> Bool {
        let curVersion = UIDevice.current.systemVersion
        if curVersion.compare(startVersion, options: .numeric) == .orderedDescending,
           curVersion.compare(endVersion, options: .numeric) == .orderedAscending {
            return true
        }
        return false
    }
}
相关推荐
qiaofugui4 分钟前
如何用 Codex 做一次可靠的项目重构:方法、节奏和可复用提示词
前端
涛涛ing5 分钟前
前端圈7月大地震:TS 7发布、Bun 11天从Zig迁Rust、Kimi K3登顶、大厂裁员50%
前端
遇乐的果园21 分钟前
前端学习笔记-vue列表处理
前端·vue.js·学习
倾颜27 分钟前
AI 聊天刷新后记录全丢?用浏览器本地存储给 AI Chat 加一层"离线记忆"
前端·aigc
梦想的旅途233 分钟前
企业微信API 实现企业微信消息的自动发送?
java·服务器·前端
何时梦醒35 分钟前
🤖 RAG 文档分割器(Splitter)深度学习笔记 —— 从零搭建知识库文档处理流水线
前端·javascript·人工智能
ss27343 分钟前
Vue 完整版本发展史时间线(2013–2026)|版本迭代里程碑+核心特性对照表
前端·javascript·vue.js
亿元程序员1 小时前
为什么大家都那么喜欢用Shader?
前端
Codebee1 小时前
OODER Studio设计工具导入体系深度解析
前端