肇鑫的技术博客

业精于勤,荒于嬉

解决Xcode Cloud无法enable Swift Package包中的宏的问题

最近我在做应用适配iOS/macOS 26的特性。今天在Xcode Cloud打包的时候遇到打包失败的错误。

Macro “DefaultsMacrosDeclarations” from package “Defaults” must be enabled before it can be used.

这个问题是我应用所使用的第三方的库 “Defaults”在其内部使用了宏。这个宏在Xcode本地编译时,需要用户手动点击确认才能继续。但是Xcode Cloud中,没有点击确认的位置。因此,就无法完成打包应用的过程。

解决办法

通过运行脚本的方式,在克隆完文件夹之后,运行脚本,规避掉对于宏的验证。

#!/bin/sh 
defaults write com.apple.dt.Xcode IDESkipMacroFingerprintValidation -bool YES

必须在Xcode中的根部位置,创建一个新组,命名为ci_scripts,然后在这个组中创建ci_post_clone.sh,内容是上面的内容。

必须在Xcode的根部位置创建组,并且命名也不能错。

小插曲

我其实最开始是像GPT 4.1提出了这个问题。GPT 4.1的解答只对了一半。它提出了创建文件夹和脚本,文件夹是正确的,脚本名字是错误的。并且它也没有告诉需要在Xcode中创建组,而只是说在项目的根目录创建就可以。最后,它创建的脚本内容不完全正确。

之后我使用了Google搜索。Google搜索默认的AI总结的是正确的,但应该就是从stackoverflow里的答案总结的。我最后是看的SO里的回答,进行的总结。

另外,我建议你完整阅读下面的第一个引用。我使用了里面最为简便的方案。而非最安全的。也许你看了之后,会选择一条不同的手段。

引用文献

How do I trust a swift macro target for Xcode Cloud builds?

Writing custom build scripts

什么?AccentColor又闹幺蛾子了?

一年以前,我就踩过一次AccentColor的坑。没想到,一年之后我又掉进来了。

SwiftUI下,TextField诡异失去Focus下样式的问题

事情的起因是这样的。因为苹果的新系统发布了嘛。我不能免俗的也要改进我之前的应用,添加对于新系统特性的一些支持之类的。

但是我在修改代码后测试时发现,当使用ZStack模拟弹窗之后,弹窗后面的视图的颜色会出错。我一开始以为是ZStack的问题,于是将模拟弹窗改成了.fullScreenCover的方式。这个问题在当时看起来时解决了。但是今天我在使用中发现,这个问题又重新出现了。

于是我在Google上搜索了一下,没想到这还是一个SwiftUI长期存在的一个问题。

How do I stop the AccentColor from turning Gray when a sheet is being presented?

原来在SwiftUI中实际使用时。原本应该一致的Color.accentColor和Color("AccentColor")在实际使用中是不一致的。说得更具体些,就是Color.accentColor会使用应用设置和用户的系统设置。而Color("AccentColor")则是将AccentColor作为颜色资源从Asset文件夹直接读取。因此,虽然它的名字也叫"AccentColor",但是实际上它只是名字叫"AccentColor"的一个颜色,你改成别的名字,比如"MyAppColor"也是一样的。虽然这样会失去Color.accentColor一些独特的个性,但是能保证颜色的一致,即颜色不会莫名其妙的改变。

系统弹sheet的时候,Color.accentColor会改变的问题,应该就是sheet本身可能存在某种机制,将应用内设置的Color.accentColor从Asset文件夹设置的内容,改成了系统默认设置的内容。比如下图红色圈起来的部分,就是系统允许用户自定义用户AccentColor偏好的地方。

accent_color

大模型又暴露了……

这真的不是危言耸听,这是真的骇人听闻。盘点一下我遇到大模型的智障行为,本文不定期更新。

大模型至今不了解SwiftUI的更新机制,必须要严格约束,小心使用

今天遇到了一个sheet弹窗之后,视图更新不同步的问题。由于我已经在提示词中,告诉了我倾向使用@Observable,而不是旧版的ObservableObject,所以一开始大模型创建了正确的@Observable class,然后在视图中使用@State创建了这个model的实例。

但是为什么还是出错了呢?那就要看使用时更具体的实现了。在开启sheet之前,大模型调用了一个准备函数,在这个函数中,对于model进行了一系列的初始化。具体的步骤是这样的,先新建一个model,然后修改它,然后用这个修改完成的model,替换为系统@State里的那个model。

我首先将问题描述给大模型,sheet首次打开的时候,显示的界面并不符合预期,应该有值的地方,实际上为空。但是如果关掉,再次打开,就又正确了。

大模型思考了一番,说这可能是sheet的机制造成,sheet有时会提前锁定一些值,这会造成打开视图时的不同步。它的建议是,在sheet打开的视图内部的onAppear中新增一个fallback的调用,重新检查并赋值。然后还说,如果这样还不行,可以考虑使用GCD延迟的方式,也就是使用DispatchQueme.main.async调用来实现改动。

试了,onAppear的确不行。但是我没有继续使用它建议的GCD延迟的方式。我问它是否还有其它的办法。

大模型想了想,说可以考虑将@Observable的class改成纯粹的struct的方式。然后就其次咔嚓地修改起来,我一看改动后的代码,妈呀!连mutating都搞出来了。SwiftUI的代码哪有这么写的啊?我果断点了Undo,取消了这次修改。

正确的方式

我再次看了一下代码,突然我发现了问题的所在。前面提到,在准备函数中,大模型的改动方式是“先新建一个model,然后修改它,然后用这个修改完成model,替换为系统@State里的那个model。”这个方式在SwiftUI中显然是错误的。SwfitUI中@State的class类型,你不能替换它,因为一替换之前的跟踪就被中断了。

所以我和大模型说,我们不应该“新建model,修改,然后替换@State”,而是应该直接修改@State中的变量。

大模型想了想,觉得我说得有道理,就修改了。然后还跟我说,如果我确定这个好用,那么onAppear那里的代码,就可以删掉了。

于是我先注释掉onAppear那里的代码,然后运行应用,果然一切都正常了。

小结

大模型并没有真正了解SwiftUI的更新机制,这部分的代码写的时候,它更像是一个拙略的模仿者。虽然这部分,我之前的领悟也不怎么深刻,但是我随着持续不断地学习和使用SwiftUI,现在在这方面,我有信心可以说,我可比大模型强多了。

大模型不擅长重构

这个说法听起来是反直觉的。大模型相比于人类,应该更擅长重构吧。为什么说它不擅长呢?

事实如此。昨天我想重构一个900多行的SwiftUI的文件,里面包含一个主视图,主视图下的视图组件,以及一个弹出视图。如果是开发者来重构这个文件,之需要新建文件,然后复制,粘贴,再删掉旧的就可以了。可以很快就完成。

但是我用大模型来重构。却接连几次失败。过程一般是这样。一开始大模型计划的挺好。

  • 我将会将文件拆分为以下几个文件。
  • 然后开始拆分,但是执行到5、6个的时候。
  • 弹出出错提示,上下文已经耗尽了。

我为了节省上下文,又单独增加了对于上下文节省的办法。每次重构一个文件,复制、粘贴、删除完了,验证有没有错误。有错误修复,没有再继续。并且可以抛弃掉使用过的上下文。

再次运行,稍微好了些,多实现几个文件,但是最后还是因为上下文不够而失败了。

小结

大模型不擅长重构,不是因为它无法规划重构。而是因为大模型重构时需要考虑大量的上下文信息,最终会因为上下文耗尽而无法继续。