阅读视图
Swift 6.2 列传(第十七篇):钟灵的“雷电蟒”与测试附件
App 暴毙现场直击:如何用 MetricKit 写一份完美的“验尸报告”
一款轻量、低侵入的 iOS 新手引导组件,虽然大清都亡了
AT 的人生未必比 MT 更好 -- 肘子的 Swift 周报 #118
AT 的人生未必比 MT 更好
学车时我开的是手动挡,起初因为技术生疏,常搞得手忙脚乱,所以第一台车就直接选了自动挡。但开了几年,我开始追求那种完全掌控的驾驶感,于是又增购了一台手动挡。遗憾的是,随着交通日益拥堵,换挡的乐趣逐渐被疲惫抵消,最终这台车也被冷落。算起来,我已经快二十年没认真开过手动挡了,但内心深处,我仍会时不时地怀念那段“人车合一”的时光。
随着 AI 深度介入我的工作与生活,我感觉自己的人生正从 MT 转向 AT。毫无疑问,AI 助我突破了许多能力瓶颈,也在熟悉领域带来了巨大的效率提升。但奇怪的是,我对它的“惊叹”却在与日俱减。看似它节省了我的时间,但我并未从这些“多出来的时间”里获得预期的满足感。也许是我对它的期望阈值不断提高,但一个不争的事实是:我已经有一段时间没有在学习和开发过程中,体会到那种单纯的快乐了。
幸好,几天前我又找回了这种久违的开心。在准备 iOS Conf SG 的 Keynote 时,由于 Keynote 在动画构建上相对“原始”且缺乏 AI 辅助,我难得地拥有一段完整的时间,去纯手工地尝试和修改。哪怕只是一个简单的并行动画,究竟是用转场、普通动画还是“神奇移动”,我都玩得不亦乐乎。那些在专家手里可能两三分钟搞定的设置,我折腾了大半天。尽管毫无效率可言,但我乐在其中。看着最终那个略显“简陋”的效果,我竟被自己感动了。
对于职场中人,效率和完成度固然是硬指标;但能否体会到过程带来的“实感”,或许才是作为“人”最朴素的追求。
我大概率不会再买 MT 的车了,但在我还能握紧方向盘的时候,也不会轻易让“智能驾驶”代劳。写代码也是如此,当初爱上它,正是因为它能给我带来纯粹的快乐。当所有工具都在催促我们变得更快更好时,我想应该给自己留一点变慢、变“笨”的空间——毕竟,AT 的人生未必比 MT 更好。
🚀 《肘子的 Swift 周报》
每周为你精选最值得关注的 Swift、SwiftUI 技术动态
- 📮 立即订阅 | weekly.fatbobman.com 获取完整内容
- 👥 加入社区 | Discord 与 2000+ 开发者交流
- 📚 深度教程 | fatbobman.com 探索 200+ 原创文章
近期推荐
告别“可移植汇编”:我已让 Swift 在 MCU 上运行七年
从 2024 年开始,Swift 官方正式提供了对嵌入式系统的支持,但要在这个领域获得显著份额,仍有很长的路要走。其实,早在官方下场之前的 2018 年,Andy Liu 和他的 MadMachine 团队就开始在探索和实践将 Swift 应用于嵌入式领域,并陆续推出了相关硬件。他们坚信,在功能日益复杂的开发场景中,Swift 的现代语言特性将展现出巨大的优势。在本文中,Andy 分享了过去几年中在该领域的探索历程。我真心希望,Swift 能够在更多的场景中,展现其魅力。
在 SwiftUI 中构建基础拨号滑块组件 (Building a Base DialSlider Component in SwiftUI)
在 AI 功能越来越强大的今天,看到一个动效,让 AI 帮你实现已经越来越容易了。但每当看到开发者凭借自己的"奇思妙想"不断探索实现方式并打磨效果时,我仍会由衷赞叹。codelaby 在这篇复刻"老式电话拨号盘"的文章中,巧妙运用 SwiftUI 的 .compositingGroup() 和 .blendMode(.destinationOut) 实现了动态"镂空"效果,使旋转层下的静态数字清晰显现,相比单纯旋转图片更具灵活性和原生质感。此外,文章对环形手势处理、角度计算(atan2)以及限位逻辑(Stopper)的阐述也十分透彻清晰。
CKSyncEngine 答疑与实战经验分享 (CKSyncEngine Questions and Answers)
不少开发者对 Core Data 的 NSPersistentCloudKitContainer 颇有诟病,认为其不透明且缺乏定制性。但真正想自己着手解决 CloudKit 的数据同步问题时,才发现需要考虑的地方实在太多,难度远超想象。苹果在几年前推出的 CKSyncEngine 彻底打破了这个困境,提供了更清晰的状态管理和错误处理,并自动处理了诸多复杂的边缘情况,让开发者可以专心构建数据同步逻辑。
Christian Selig 通过自问自答的方式,分享了他在使用 CKSyncEngine API 时的经验,详细拆解了 CKSyncEngine 如何作为一个完美的中间层,在保留数据存储灵活性(你可以继续用 SQLite、Realm 或 JSON)的同时,接管了最令人头疼的同步状态管理。
我对 iOS 26 Tab Bar 的吐槽 (My Beef with the iOS 26 Tab Bar)
SwiftUI 在 iOS 18 中对 Tab Bar 的调整,其影响几乎堪比当年 NavigationStack/NavigationSplitView 取代 NavigationView,不仅改变了设计语言,对应用的实现方式和数据流走向都产生了颠覆性影响。而为 iOS 26 Liquid Glass 风格引入的"搜索标签"功能进一步推进了这种变革。Ryan Ashcraft 在这篇文章中直言不讳地指出了新 Tab Bar 设计的诸多问题:默认的浮动样式在某些界面中显得突兀,与应用整体视觉风格难以协调;新的边距和间距规则打破了长期以来的设计惯例,让开发者需要重新调整大量现有界面;更重要的是,这些改动并未明显提升用户体验,反而增加了适配成本。
我个人对新 Tab 的最大感受是,它会显著影响开发者在开发应用时对最低系统版本的决策。为 Tab 维护两套代码是否值得?如果为了简化实现而不得不将最低版本提高到 iOS 18,这或许正是 SwiftUI 团队的另一个设计意图?
Dia:深度剖析 The Browser Company 的 macOS 浏览器架构 (Dia: A Technical Deep Dive into The Browser Company's macOS Browser)
Arc 是第一个使用 Swift 构建的大型 Windows 平台应用,而且 The Browser Company 也因此为 Swift 社区的 Windows 工具链做出了突出贡献。在从 Arc 转型到 Dia 后,开发团队并没有放弃使用 Swift,那么 macOS 端的 Dia 具体使用了哪些开发框架呢?
Everett 在本文中揭示了 Dia 独特的技术架构:这是一个基于 AppKit + SwiftUI 的原生 macOS 应用,但其核心渲染引擎并非 WebKit,而是嵌入了自行修改的 Chromium(ArcCore)。此外,在 Dia 的二进制文件中发现了大量与本地 AI 相关的库(如 Apple MLX 和 LoRA 适配器),这预示着 Dia 并非只是为了"快",而是已经为设备端 AI 推理做好了底层工程准备。
关于罗技开发者证书过期的迷思 (Myths about Logitech Developer ID certificate expiration)
几天前,不少 macOS 用户发现罗技鼠标的自定义按钮失效。由于控制台日志中充斥着代码签名(Code Signing)相关的报错,不少用户和媒体将其归咎于"苹果撤销了证书"。Jeff Johnson 通过分析系统日志为苹果在本次事件中的角色进行了平反:这并非苹果的证书服务故障,而是罗技自身软件工程问题导致的。Logi Options+ 的后台进程在更新后,未能通过 macOS taskgated 的代码签名有效性验证,从而被系统直接终止。这篇文章不仅是一份故障分析报告,更提醒开发者:在 macOS 严格的安全机制下,应用更新的签名验证流程容不得半点马虎。
“如果你的证书过期了,用户仍然可以下载、安装和运行用该证书签名的 Mac 应用程序版本。但是,你需要一个新的证书来签署更新和新申请。” —— 苹果官方文档
拒绝 LLM 生成的平庸代码 (Stop Getting Average Code from Your LLM)
不可否认,在人类长久以来累积的信息海洋中,高质量的数据与信息只占少数。对于个体来说,我们完全可以有目的地去甄别和学习这些优质内容。但是,受限于机制,LLM 默认倾向于训练数据的“平均值”,这就导致它生成的内容在各个方面都显得比较平庸。具体到 Swift 开发领域,这往往意味着它会生成大量旧版的、非结构化的代码。
要想获得高质量、符合 Swift 6 标准甚至特定架构风格的代码,关键在于对抗这种“回归均值”的本能。Krzysztof Zabłocki 详细介绍了如何利用 Few-Shot Prompting(少样本提示)和上下文注入技术,通过提供具体的代码范例和架构规范,强迫 LLM “忘记”平庸的默认设置,转而生成精准匹配项目标准的高质量代码。
工具
swift-effect: 一种基于类型驱动的副作用处理方案
Alex Ozun 长期关注 Swift 中的 类型驱动设计,这个库是他对“代数效应(Algebraic Effects)+ 处理器(Effect Handlers)”在 Swift 里的实践。 swift-effect 不是把副作用变成“数据结构再解释”,而是将副作用建模为可拦截的全局操作(@Effect),通过 handler 在运行时组合和替换行为,让业务代码保持线性/过程式风格,同时又能精细控制 I/O、并发等行为。
核心亮点:
- 保持代码线性:调用 Console.print 等 effect 就像普通函数,但行为可由 handler 动态决定。
-
无 Mock 的行为测试:用
withTestHandler逐步拦截/断言 effect 序列,像“交互式脚本”一样测试流程。 - 并发可控:支持对 Task/AsyncSequence 的确定性测试,解决并发顺序不稳定的问题。
Codex Skill Manager: 一款面向众多 CLI 的 macOS 工具
很多开发者都会同时使用多种 AI 编程服务,尽管它们拥有类似的概念、设定和工具类型,但在具体设置和细节描述上仍有差异,这导致开发者很难对所有服务进行统一管理。Thomas Ricouard 开发的 Codex Skill Manager 将 Codex、Claude Code(以及 OpenCode、Copilot)的技能集中在一个 UI 里查看、搜索、删除和导入,避免在多个隐藏目录中手动寻找。
核心功能
-
本地技能:扫描
~/.codex/skills/public、~/.claude/skills等路径,展示列表与详情 - 详情渲染:Markdown 视图+引用预览
- 远程 Skill:Clawdhub 搜索/最新列表、详情拉取与下载
- 导入/删除/自定义路径:支持从 zip 或文件夹导入、侧边栏删除、添加自定义路径
- 多平台安装状态:为不同平台标记已安装状态
活动
LET'S VISION 2026|邀请你与我们同行!
✨ 大会主题:Born to Create, Powered by AI
- 📍 地点:上海漕河泾会议中心
- ⏰ 时间:2026 年 3 月 27 日 - 3 月 29 日
- 🏁 重点:汇聚顶尖创作者与 AI 技术大咖,共同探索 AI 应用的未来边界
- 🌍 官网:letsvision.swiftgg.team
别走开!请关注官方账号和主理人 SwiftSIQI,我们将持续放送更多精彩内容!
Swift Student Challenge 2026
每年一度的学生挑战赛再次登场。挑战赛为数以千计的学生开发者提供了展现创造力和编程能力的机会,让他们可以通过 App Playground 呈现自己的作品,并从中学习在职业生涯中受用的实际技能。
今年作品提交通道将于 2026 年 2 月 6 日至 2 月 28 日开放。
往期内容
- 2026:当 AI 隐入工作流,你准备好了吗?- #117
- Swift、SwiftUI 与 SwiftData:走向成熟的 2025 - #116
- 周日小插曲 - #115
- 挖掘“沉默的专家”- #114
💝 支持与反馈
如果本期周报对你有帮助,请:
- 👍 点赞 - 让更多开发者看到
- 💬 评论 - 分享你的看法或问题
- 🔄 转发 - 帮助同行共同成长
🚀 拓展 Swift 视野
- 📮 邮件订阅 | weekly.fatbobman.com 获取独家技术洞察
- 👥 开发者社区 | Discord 实时交流开发经验
- 📚 原创教程 | fatbobman.com 学习 Swift/SwiftUI 最佳实践
深入理解 Swift Concurrency:从 async/await 到隔离域
1月10日用户隐私保护新规出炉,政策解读
2026年1月10日,国家互联网信息办公室发布了《互联网应用程序个人信息收集使用规定(征求意见稿)》,进一步优化了个人隐私保护法案内容。
政策原文:mp.weixin.qq.com/s/epF6mh-Oc…
一句话总结:这次的新政细化了隐私合规规则,堵住了一些规则漏洞,进一步保护用户隐私。对于大部分开展正规业务的开发者来说,无需特别的改动。后续可按渠道平台(华为、小米等)要求,做进一步调整。
内容概览:
1、明确禁止“无关场景调用相机/麦克风”,直击“偷听偷拍”乱象。——旧规:仅原则性要求“不得超范围收集”,但未明确哪些行为算违规。
2、位置权限分级管理:区分“实时定位”与“单次定位” ——旧规:仅笼统要求“最小必要”。
3、进一步强化了不允许获取用户全部相册权限,必须使用系统系框架Android SAF,只能获取用户授权后的部分照片。
4、操作系统厂商,需提供“精细化授权”选项,如 “仅本次允许”“仅今天允许”。
5、生物识别信息(人脸、指纹等)原则上只能本地存储,不能上传。除非法律允许或用户单独同意。
6、App运营者对第三方SDK负连带审核义务。用户向App提出对SDK的数据权利请求(如删除),App必须转达并督促SDK响应,不能再以“这是第三方SDK行为,与我无关”推责。
7、账号注销流程进一步简化:不得强制用户提供额外身份证明(如手持身份证、学历证明等)。堵住了一些App以“安全验证”为名设置注销门槛的做法。
下面是详细介绍
一、首次 明确禁止“无关场景调用相机/麦克风” —— 直击“偷听偷拍”乱象
- 旧规:仅原则性要求“不得超范围收集”,但未明确哪些行为算违规。
-
新规(第14条):
- 必须“用户主动选择使用拍照、语音、录音录像等功能时”才能调用相机/麦克风;
- 禁止在用户停止使用相关功能后继续调用;
- 禁止在无关场景(如浏览商品页、看新闻)调用音视频权限。
实质变化:这是首次以部门规章形式将“后台静默调用麦克风/摄像头”直接定性为违规。此前企业常以“预加载”“性能优化”为由辩解,今后不再成立。
二、位置权限分级管理:区分“实时定位”与“单次定位”
- 旧规:仅笼统要求“最小必要”。
-
新规(第14条):
- 实时定位类(导航、外卖) → 调用频率必须“限于业务最低频度”;
- 单次定位类(搜索、推荐、广告) → 仅允许调用一次,且需用户进入界面或主动刷新;
- 原则上禁止申请“后台持续获取位置”权限(除非法律另有规定或确需)。
实质变化:终结了“只要用户开了定位,App就可高频后台上报位置”的灰色操作。例如,电商App在首页展示附近门店,只能触发一次定位,不能持续追踪。
三、强制使用系统级存储访问框架(如Android SAF)
-
新规(第14条):
- 用户上传图片/文件时,若系统提供标准存储访问框架(如Android的Storage Access Framework),App 不得再索要“相册”“存储”全权限。
- 即使因文件编辑/备份获得存储权限,也不得访问用户未主动选择的其他文件。
实质变化:推动从“粗放式读取整个相册”转向“按需访问单个文件”,大幅降低隐私泄露面。这要求开发者重构文件上传逻辑。
四、操作系统需提供“精细化授权”选项(开发者需适配)
-
新规(第14条):
- 操作系统在弹窗征得用户同意时,应提供基于时间、频度、精度的授权选项(如“仅本次允许”“仅今天允许”“模糊位置”)。
实质变化:虽然责任在OS厂商,但开发者需确保App能兼容这些细粒度授权(例如用户选择“仅本次允许位置”,下次使用需重新申请)。否则功能将异常。
五、生物识别信息默认本地处理,禁止网络传输
-
新规(第15条):
- 人脸、指纹等生物特征信息,默认应在终端设备本地处理和存储;
-
不得通过网络传输至服务器,除非:
- 法律法规明确允许;或
- 用户单独书面同意(且目的充分必要)。
实质变化:许多App当前将人脸照片上传服务器进行比对(如刷脸登录),今后必须评估是否真有必要。若非必要,必须改为本地验证(如Face ID/指纹API)。
六、SDK责任穿透:App运营者对第三方SDK负连带审核义务
-
新规(第19条):
- App运营者必须审核嵌入的SDK,确保其行为符合公示规则;
- 用户向App提出对SDK的数据权利请求(如删除),App必须转达并督促SDK响应。
实质变化:不能再以“这是第三方SDK行为,与我无关”推责。开发者需建立SDK准入机制,并保留沟通记录。
七、账号注销流程进一步简化(禁止设置障碍)
-
新规(第18条):
- 注销后15个工作日内必须删除或匿名化数据;
- 不得强制用户提供额外身份证明(如手持身份证、学历证明等);
- 若使用统一账号体系(如微信/QQ登录),必须支持单App注销,不影响其他服务。
实质变化:堵住了一些App以“安全验证”为名设置注销门槛的做法。
Claude Code 四大核心技能使用指南
iOS实现 WKWebView 长截图的优雅方案
在 iOS 开发中,为 WKWebView 实现长截图功能是一个常见且棘手的需求。开发者通常会遇到以下几个痛点:
- 网页内容高度不确定
- 滚动区域难以完整截取
- 截图过程中的界面闪烁影响用户体验
本文将介绍一种高效、稳定的解决方案,通过分段渲染与图像拼接,完美捕获整个网页内容,并提供可直接集成的完整代码。
🎯 核心思路
我们的方案主要分为三个清晰的步骤:
- 布局调整:将 WebView 移至临时容器,为完整渲染做准备。
- 分段渲染:按屏幕高度分段捕获内容,生成多张切片图像。
- 图像拼接:将所有切片图像无缝拼接成一张完整的长图。
这种方法巧妙地绕过了直接截取
UIScrollView的局限性,同时通过遮罩视图,保证了用户界面的视觉稳定性,避免闪烁。
💻 完整实现代码
WKWebView分类中添加长截图方法
- WKWebView+Capture.h
#import <WebKit/WebKit.h>
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
@interface WKWebView (Capture)
/**
* 捕获 WKWebView 的完整内容并生成长截图
* @param completion 完成回调,返回拼接好的长图(失败则返回 nil)
*/
- (void)captureEntireWebViewWithCompletion:(void (^)(UIImage * _Nullable capturedImage))completion;
@end
NS_ASSUME_NONNULL_END
- WKWebView+Capture.m
#import "WKWebView+Capture.h"
@implementation WKWebView (Capture)
/**
* 捕获 WKWebView 的完整内容并生成长截图
* @param completion 完成回调,返回拼接好的长图(失败则返回 nil)
*/
- (void)captureEntireWebViewWithCompletion:(void (^)(UIImage *capturedImage))completion {
// ⚠️ 关键:确保在主线程执行
if (![NSThread isMainThread]) {
NSLog(@"错误:WebView 截图必须在主线程执行");
if (completion) completion(nil);
return;
}
// 步骤1: 检查父视图并保存原始状态
UIView *parentView = self.superview;
if (!parentView) {
if (completion) completion(nil);
return;
}
CGRect originalFrame = self.frame;
CGPoint originalContentOffset = self.scrollView.contentOffset;
// 步骤2: 创建遮罩视图,保持界面"静止"的视觉效果,可以额外添加loading
UIView *snapshotCoverView = [self snapshotViewAfterScreenUpdates:NO];
snapshotCoverView.frame = self.frame; // 确保遮罩视图位置与 WebView 完全一致
[parentView insertSubview:snapshotCoverView aboveSubview:self];
// 步骤3: 创建隐藏的临时窗口和容器
UIWindow *temporaryWindow = [[UIWindow alloc] initWithFrame:self.bounds];
temporaryWindow.windowLevel = UIWindowLevelNormal - 1000; // 置于底层
temporaryWindow.hidden = NO;
temporaryWindow.alpha = 0;
temporaryWindow.userInteractionEnabled = NO;
UIView *captureContainerView = [[UIView alloc] initWithFrame:self.bounds];
captureContainerView.clipsToBounds = YES;
// 将 WebView 移入临时容器
[self removeFromSuperview];
[captureContainerView addSubview:self];
[temporaryWindow addSubview:captureContainerView];
// 步骤4: 获取完整内容高度并调整布局
CGFloat fullContentHeight = self.scrollView.contentSize.height;
self.frame = CGRectMake(0, 0, originalFrame.size.width, fullContentHeight);
self.scrollView.contentOffset = CGPointZero;
__weak typeof(self) weakSelf = self;
// ⭐ 延迟执行,确保 WebView 内容布局与渲染完成
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.5 * NSEC_PER_SEC)),
dispatch_get_main_queue(), ^{
__strong typeof(weakSelf) strongSelf = weakSelf;
if (!strongSelf) {
if (completion) completion(nil);
return;
}
// 步骤5: 分段截图核心逻辑
CGFloat pageHeight = captureContainerView.bounds.size.height; // 单屏高度
CGFloat totalHeight = fullContentHeight; // 总内容高度
NSMutableArray<UIImage *> *imageSlices = [NSMutableArray array];
CGFloat offsetY = 0;
while (offsetY < totalHeight) {
CGFloat remainingHeight = totalHeight - offsetY;
CGFloat sliceHeight = MIN(pageHeight, remainingHeight);
// 处理最后一段高度不足一屏的情况
if (remainingHeight < pageHeight) {
CGRect containerFrame = captureContainerView.frame;
containerFrame.size.height = remainingHeight;
captureContainerView.frame = containerFrame;
}
// 移动 WebView,将当前要截取的区域"暴露"出来
CGRect webViewFrame = strongSelf.frame;
webViewFrame.origin.y = -offsetY;
strongSelf.frame = webViewFrame;
// 渲染当前分段到图像上下文
UIGraphicsBeginImageContextWithOptions(
CGSizeMake(originalFrame.size.width, sliceHeight),
NO,
[UIScreen mainScreen].scale
);
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat scaleX = originalFrame.size.width / captureContainerView.bounds.size.width;
CGFloat scaleY = sliceHeight / captureContainerView.bounds.size.height;
CGContextScaleCTM(context, scaleX, scaleY);
[captureContainerView.layer renderInContext:context];
UIImage *sliceImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
if (sliceImage) {
[imageSlices addObject:sliceImage];
}
offsetY += sliceHeight; // 移动到下一段
}
UIImage *finalImage = nil;
// 步骤6: 图像拼接
if (imageSlices.count == 1) {
finalImage = imageSlices.firstObject;
} else if (imageSlices.count > 1) {
UIGraphicsBeginImageContextWithOptions(
CGSizeMake(originalFrame.size.width, totalHeight),
NO,
[UIScreen mainScreen].scale
);
CGFloat drawOffsetY = 0;
for (UIImage *slice in imageSlices) {
[slice drawInRect:CGRectMake(0,
drawOffsetY,
slice.size.width,
slice.size.height)];
drawOffsetY += slice.size.height;
}
finalImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
// 步骤7: 恢复原始状态
[strongSelf removeFromSuperview];
[captureContainerView removeFromSuperview];
temporaryWindow.hidden = YES;
strongSelf.frame = originalFrame;
strongSelf.scrollView.contentOffset = originalContentOffset;
[parentView insertSubview:strongSelf belowSubview:snapshotCoverView];
[snapshotCoverView removeFromSuperview];
// 步骤8: 在主线程回调最终结果
if (completion) {
completion(finalImage);
}
});
}
@end
📱 效果展示
🚀 使用方法
调用方式非常简单,只需一行代码。
// 在需要截图的地方调用
[webView captureEntireWebViewWithCompletion:^(UIImage *capturedImage) {
if (capturedImage) {
// ✅ 截图成功,处理结果
// 例如:保存到相册
UIImageWriteToSavedPhotosAlbum(capturedImage, nil, nil, nil);
// 或:上传、分享、预览等
} else {
// ❌ 截图失败
NSLog(@"截图失败");
}
}];
📝 总结
本文提供的方案通过以下关键技术,优雅地解决了 WKWebView 长截图的难题:
- 临时容器管理:隔离渲染环境,避免干扰主界面。
- 分段渲染:将长内容分解为多个可管理的屏幕片段。
- 状态恢复:完整保存并恢复 WebView 的原始状态,确保业务无感知。
如果你有更好的实现思路,或在实际应用中遇到了特殊场景,欢迎在评论区分享交流!
Swift 方法派发深度探究
“死了么”App荣登付费榜第一名!
AppStore卡审依旧存在,预计下周将逐渐恢复常态!
背景
圣诞节🎄虽然结束了,后劲儿依旧在。最直观的感受就是AppStore审核节奏还未恢复正常。依然存在审核时间较久或等待审核时间过长的问题。
举一个直观的例子🌰:
一座5层高的商场,每层都预备了洗手间🚾。正常情况下,足够满足整座商城客流量的需求。但是赶上了节假日高峰,并且只有3层洗手间可用。那么在常态客流量不变的情况也已经拥挤,更不要说节假日高峰期。
就第三方上架&更新趋势来看,AppStore审核节奏也将逐步正常。
非必要迭代
如果不是遇到重大线上问题或重大功能迭代,建议不更新或不上新包。避免正常产品遭遇卡审状态,导致难以定位问题或者审核员摆烂直接一手4.3a。
毕竟AppStore审核团队,刚刚经历了年关肯定积压了大量待审核的产品,多少也有些烦躁。(PS:单纯从心理角度来讲)
新包、新账号和新代码,“三新原则”基本上叠满了卡审buffer。【特指中国大陆的开发者,海外账号亲测影响不大。】
重大更新
对于产品有着节前活动或市场战略布局的产品,那么也不用担心。在AppStore依然存在便捷通道:即加急审核!
常规产品,不必担心,这是官方提供的合理渠道,确实保障开发者的紧急需求【AppStore中的急诊室】。
遵守规则,方得长治久安,最后祝大家大吉大利,今晚过审!
相关推荐
# AppStore敏感词排查手册,多维度分析Guideline 2.3.1隐藏功能,轻松过审。