肇鑫的技术博客

肇鑫 / Owen Zhao

独立开发者,主要开发 iOS、watchOS、macOS 应用。

目前在维护 SleepTapRooster Time,以及 Markdown Writer 相关工具。

最新文章

watchOS 10无法在Xcode 15下配对问题的解决

Xcode

之前我的苹果表s5无法和Xcode 15配对。用了很多办法也没解决。后来我发现,这个问题在Xcode 15上几乎是普遍存在的。于是死心了。等苹果修。结果等来的却是WWDC2024的watchOS 11不再支持我的s5的消息。索性,趁着618的促销,把s5卖掉,换成s9。

打开开发者选项

出乎我意料的,s9与Xcode 15的配对也不是一帆风顺。最初遇到的难题就是,手表没法打开开发者模式。我使用方式是:

  1. 将s9与iPhone进行配对。设置为全新手表,并且不安装任何软件。这样的目的是保证系统的纯正,以及尽快完成设置。
  2. 手表要显示开发者模式选项,必须现在手机端开启开发者模式。
    1. 但是由于我的手机是在和手表配对之前就已经开启了开发者模式。因此需要先关闭手机的开发者模式,然后重新开启,重启手机。
    2. 手机成功开启开发者模式后。手表端需要关机,然后重启。这样应该就能看到开发者模式的选项了。

我这么做完之后,我的s9并没有成功显示开发者选项。这时,我看到了手机提示手表系统可以升级,点开一看,原来手表的系统是watchOS 10.4,而最新的是watchOS 10.5。于是将s9连上充电器,开始升级系统。

升级系统完成后,直接就发现了开发者选项。可见,第一步应该是升级系统到最新的10.5,然后是上面的那几步。

与Xcode配对

结果和Xcode 15的配置还是不成功。于是我打开Xcode 16 beta配对。结果提示了这个错误。

Transport error
Domain: com.apple.CoreDevice.ControlChannelConnectionError
Code: 0
User Info: {
    DVTErrorCreationDateKey = "2024-06-16 08:32:19 +0000";
    "com.apple.dt.DVTCoreDevice.operationName" = connect;
}
--
Control channel connection timed out while in state preparing
Domain: com.apple.dt.RemotePairingError
Code: 4
--


System Information

macOS Version 14.5 (Build 23F79)
Xcode 16.0 (23037.4) (Build 16A5171c)
Timestamp: 2024-06-16T16:32:19+08:00

我怀疑和VPN的设置有关。我使用的是Clash Pro。于是重启到macOS 15 beta系统。那个系统中Clash Pro没有设置为开机启动。果然,进入到macOS 15 beta之后,和Xcode 16 beta的配对成功了。

然后我又重启回macOS 14.5,这回提示为另外一个错误了。

Previous preparation error: A networking error occurred.. Control channel connection was invalidated while creating tunnel connection

我将Clash Pro的策略从“规则”,修改为“全局”。然后重开Xcode 15,这次可以成功配对了。

最后,我创建了一个手表应用测试了一下,可以成功调试了。

Xcode在开发SwiftUI项目时,持续CPU占用过高问题的解决

Xcode

问题的发现

在使用Xcode开发SwiftUI项目时,经常会遇到Xcode持续高CPU占用的问题。以往我没有重视这个问题,经常是很久之后才发现。此时,原本冰冷的Mac mini摸起来已经温热了。为此,我特意开发了一个小工具,提醒我关于这个问题。

App Helper

问题的解决

最初,我发现这个问题出现的几率,和我打开的SwiftUI的文件的数量相关。打开的SwiftUI的文件越多,越有可能遇到这个问题。

就在我以为这就是真正的原因,并发文之后,我在仅打开2-3个SwiftUI文件的时候又遇到了这个问题。这次,我终于找到了是哪个文件导致的这个问题了。是MainView。应该是文件功能太多导致的。我的MainView,是一个接近2000行的文件。它包含多项功能:

  1. 主视图布局,侧栏视图实现
  2. 工具栏实现
  3. 各种错误弹窗处理
  4. 文件处理(打开、解析、保存)
  5. 用户订阅状态管理等。

我尝试将代码分离按照功能为多个小文件。但是问题仍旧存在。最后发现,解决的办法就是注释掉预览的代码。

关于这个问题的一些补充

  1. CPU占用过高是因为MainView的功能太多导致的。
  2. 将MainView的代码拆分到多个文件不能解决这个问题。
  3. 即便MainView没有在预览,即预览视图显示为刷新按钮的状态。也还是会有这个问题。也就是说,只要预览视图开着,不管有没有要求运行预览,都会导致Xcode的CPU高占用。
  4. 此时,就算关闭了预览也没有用。所以,后台应该是某种卡死的状态。
  5. 解决办法只有
    1. 直接关闭预览。
    2. 一开始就不开预览。

有关XCUITest的一些补充(一)

XCTest

今天在编写项目截图的时候,遇到了好几个XCUITest的问题。解决之后,感觉这些问题应该算是蛮经典的,于是把它们记录下来,方便以后查阅。

最先遇到的是测试无法运行成功,提示有两个,一个是“Undefined symbols”,一个是“Linker command failed with exit code 1 (use -v to see invocation)”。

经过查看详细日志,发现是SPM(Swift Package Manager)的问题。当为应用目标时,SPM引入的第三方框架,如果该框架对于其它框架有依赖,那么SPM会自动导入该依赖的框架。但是在XCUITest的时候,或许是没有用到SPM,被依赖的框架并不会自动引用。因此,需要手动添加第三方框架之外,还需要手动再添加第三方框架依赖的框架。具体要添加多少,就要看错误日志提示的是哪个框架了。

解决了这个问题之后,测试终于可以通过。但是同时,又发生了一个新的问题。就是虽然Xcode显示Test成功了。但是却一直显示Testing,长久也没有测试完成。

经过查询,发现这也是Xcode一个bug。当使用XCUITest测试时,需要将并行测试的选项关闭,否则就会一直显示Testing。

还有一个注意事项,就是XCUITest测试时,必须将项目的目标设定为XCUITest这个目标,这个目标默认是隐藏的,必须手动添加出来。如果把应用作为目标,然后运行XCUITest,还是可能会出现一直显示Testing的问题。

最后,如果你需要检测文本,需要注意文本语言问题,将翻译的strings文件添加到XCUITest的目标,并不能自动调用并使用NSLocalizedString宏,因此,需要用或进行检测,像这样:

XCTAssertTrue(app.staticTexts["使用云端服务"].exists || app.staticTexts["Try Cloud"].exists)

参考

iOS项目CocoaPods的安装与使用

SwiftRealmSwiftXcode

安装

网上看了好几篇文章,综合起来才成功,这个是在macOS Sierra 10.12.4 (16E195)下正确安装CocoaPods的步骤:

1. 替换源为镜像

gem sources -l

查看当前的gem源的地址,由于墙的缘故,如果源是https://rubygems.org/,我们需要将它删除。

gem sources --remove https://rubygems.org/

然后替换为ruby china的镜像。

gem sources --add https://gems.ruby-china.org/

确认源替换是否成功

gem sources -l

2. 升级gem

系统自带的gem版本较低,使用时会出现莫名其妙的问题,因此要把它升级到最新版

sudo gem update --system

如果上面代码提示没有权限,改成

sudo gem update -n /usr/local/bin --system

3. 安装cocoapods

在macOS 10.11和10.12中安装时,安装到/usb/bin会提示错误,因此需要安装到/usb/local/bin里

sudo gem install -n /usr/local/bin cocoapods

4. 替换源

默认的源更新起来很慢,需要替换才能变快。如果你想使用默认的,可以跳过这一步。

查看当前的源:

pod repo 

删除master:

pod repo remove master

添加清华的源:

git clone https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git ~/.cocoapods/repos/master

更新源:

pod repo update

5. 更新pod

如果你做过上一步,可以跳过这一步。

pod setup

这步时间较长,需要耐心等待。

使用

如果你使用了第4步的替换源,那么需要在你的每个Podfile的最前面,添加一行。

source 'https://mirrors.tuna.tsinghua.edu.cn/git/CocoaPods/Specs.git'
  1. 新建一个iOS的项目。
  2. 在终端,进入到该项目的文件夹。
  3. 执行pod init
  4. 打开自动生成的Podfile文件
  5. 在其中添加你要使用的框架的名称
  6. 保存好Podfile文件
  7. 执行pod install
  8. 在Xcode中,关闭你的项目
  9. 在文件夹,找到pod生成的.xcworkspace文件,打开它。
  10. 像平常一样使用框架就可以了。

比如我新建的项目叫PodTest,添加了RealmSwift,最终的Podfile如下:

这其中大部分的内容都是pod自动生成的,我需要修改/添加的只有第三行,iOS的版本。第四行,去除警告。以及pod 'RealmSwift'的那一行。

最后的那段,是RealmSwift网站要求的。你需要什么框架,就到那个框架的官网,按照提示复制粘贴上去就可以了。

最后记得,每次修改完Podfile之后,都要记得运行一遍pod install

# Uncomment the next line to define a global platform for your project
platform :ios, '10.3'
inhibit_all_warnings!

target 'PodTest' do
  # Comment the next line if you're not using Swift and don't want to use dynamic frameworks
  use_frameworks!
  pod 'RealmSwift'

  # Pods for PodTest

  target 'PodTestTests' do
    inherit! :search_paths
    # Pods for testing
  end

  target 'PodTestUITests' do
    inherit! :search_paths
    # Pods for testing
  end

end

post_install do |installer|
  installer.pods_project.targets.each do |target|
    target.build_configurations.each do |config|
      config.build_settings['SWIFT_VERSION'] = '3.1'
    end
  end
end

小技巧

  1. 第四行的inhibit_all_warnings!,可以使得Xcode不对框架中不当的帮助说明生成警告。因为框架是第三方的,我们即便看到了,也不会修改它。
  2. 有时会由于网络问题导致下载的框架不全,此时可以使用这个命令重新下载。
pod deintegrate && pod install

参考资料

Xcode中,项目的语言和翻译之间的关系

Xcode

系统选择语言的机制

如果想要卖出更多的程序,你的程序就必须支持多国语言。iOS和macOS在处理程序时,语言部分的模型是这样的:

  1. 用户可以设置多个他懂得的语言,语言的优先级别是从上到下。
  2. 系统打开某个程序时,按照用户设置的语言偏好,依次查找程序是否提供了该语言的界面,如果是,就加载该语言,并打开程序;如果不是,就加载程序员指定的默认语言。

举例:
假设小明的设定的语言偏好顺序为简体中文、繁体中文、英文,程序A提供的语言为繁体中文、英文(默认)。那么小明打开程序A时,程序A会显示繁体中文的界面。
假设雅克布系统设定的语言偏好为法文,那么他打开程序A时,由于程序A没有提供法文的翻译,且程序A的默认语言为英文,所以雅克布看到的程序A界面就是英文的。

中文开发者面对的问题

程序员面对的问题更复杂一些。传统来说,程序员一般选择英文进行开发,然后再翻译成中文和其它语言。这个流程是经过检验的。可是对于中文开发者来说,直接用中文开发界面,然后再翻译成英文或其它界面,在查看时会更加直观。不过,传统上一般认为,如果你直接用中文开发,那么遇到上面举例中的雅克布的情况,由于默认开发的语言已经是中文,雅克布可能会看到一个自己不懂的中文界面的情况。

小结:中文开发者面对的问题是:

  1. 要使用中文开发
  2. 当用户偏好的语言,程序未能提供时,默认显示英文的界面

要解决这个问题,首先得知道Xcode中语言和翻译相关的模型。

Xcode中的语言模型

  1. Xcode采用Base Internationalization的方式。默认情况下,Base Internationalization为英文。
  2. 用户可以在项目的Info.plist中指定程序成默认语言,默认是英文。
  3. 用户添加多种语言的翻译。

解决方案

  1. 新建一个Cocoa程序项目
  2. 在Finder中,找到你的项目的.xcodeproj文件,右键点击,选显示包内容
  3. 双击打开project.pbxproj文件
  4. 在打开的文件中,搜索developmentRegion,默认对应的值为English,将其改为zh-Hans,并保存。这里修改的是Base Internationalization的语言,Xcode中显示为Development Language.
  5. 回到新建的项目,打开项目中的Info.plist,查看Localization native development region项,默认应该是en。如果确认为en,则不用修改。这个项的键是CFBundleDevelopmentRegion。代表程序的默认界面语言。
  6. 点击项目文件,添加新语言Chinese(Simplified),你会看到简体中文后面的括号里写着Development Language。这代表设置成功了。