如何从任意Target或Extension中以编程方式可靠获取主Bundle ID?
嘿,这个需求我太懂了!做跨Target共享UserDefaults、Keychain这些东西的时候,硬编码Bundle ID简直是噩梦——改一次要同步改N个Target,还容易漏。我给你分享几个通用又靠谱的实现方式,完全不用绑定具体项目的内容,走到哪用到哪:
方案一:利用苹果推荐的Bundle ID命名规则(最通用,无需额外配置)
苹果官方其实推荐Extension的Bundle ID采用「主App Bundle ID + 后缀」的格式,比如主App是com.yourcompany.yourapp,Extension就叫com.yourcompany.yourapp.widget或者com.yourcompany.yourapp.share。基于这个约定,我们可以写个自动提取的函数:
import Foundation public func getMainAppBundleIdentifier() -> String? { guard let currentBundleId = Bundle.main.bundleIdentifier else { return nil } let components = currentBundleId.components(separatedBy: ".") // 主App的Bundle ID组件数一般不会太少,直接从后往前尝试匹配 var mainBundleIdComponents = components while mainBundleIdComponents.count > 1 { mainBundleIdComponents.removeLast() let candidateId = mainBundleIdComponents.joined(separator: ".") // 验证这个ID对应的Bundle是否存在(主App的Bundle肯定存在) if Bundle(identifier: candidateId) != nil { return candidateId } } // 如果是主App本身调用,直接返回当前ID return currentBundleId }
这个函数的逻辑很接地气:先拿到当前组件的Bundle ID,然后从后往前一点点剪掉后缀,每剪一次就试试能不能找到对应的主App Bundle——毕竟主App的Bundle肯定是存在的嘛。如果是主App自己调用这个函数,直接就返回自己的ID,完美适配所有场景,完全不用管当前是主App还是Extension。
方案二:自定义Info.plist键(最可控,完全不依赖命名规则)
要是你不想依赖命名规则(比如怕以后团队改命名风格搞出问题),那还有个更稳妥的办法:给所有Target的Info.plist都加个自定义键,比如叫MainAppBundleID,然后把值统一设成主App的Bundle ID(主App自己的这个键也填自己的ID就行)。然后代码里直接读这个键,还能加个方案一的兜底逻辑:
import Foundation public func getMainAppBundleIdentifier() -> String? { // 优先读取自定义配置的主App Bundle ID if let mainAppId = Bundle.main.object(forInfoDictionaryKey: "MainAppBundleID") as? String { return mainAppId } // 兜底:用方案一的逻辑自动查找 guard let currentBundleId = Bundle.main.bundleIdentifier else { return nil } let components = currentBundleId.components(separatedBy: ".") var mainBundleIdComponents = components while mainBundleIdComponents.count > 1 { mainBundleIdComponents.removeLast() let candidateId = mainBundleIdComponents.joined(separator: ".") if Bundle(identifier: candidateId) != nil { return candidateId } } return currentBundleId }
这个方案的可控性拉满,哪怕你以后改了Extension的命名规则,只要Info.plist里的键值对没错,就能正常工作。加了兜底逻辑之后,就算哪个Target忘了配置这个键,也不会崩,自动切换到命名规则的方式找,双重保险!
拿到主Bundle ID后构建App Group ID
有了主App的Bundle ID,构建用于共享数据的App Group ID就小菜一碟了,直接拼个group.前缀就行:
if let mainAppId = getMainAppBundleIdentifier() { let appGroupId = "group.\(mainAppId)" // 用这个ID初始化共享的UserDefaults let sharedDefaults = UserDefaults(suiteName: appGroupId) // 或者用于Keychain访问等操作 // ... 你的后续逻辑 }
备注:内容来源于stack exchange,提问作者laconicman

