Summary after learning the componentized resource file management scheme
origin
Before componentization, all code and resource files were in the main project, and a large number of image resources would inevitably cause naming conflicts. In general, we need a strict naming convention, such as adding prefixes according to the division of modules, to avoid naming conflicts.
The company adopts a componentized solution, but the resource files are not split and placed in the main project. It is very painful to maintain components and projects. We split the main project resource file into the corresponding Pod, and let the component manage it by itself. In this way, the resource files are split into each Pod, which makes the division of responsibilities more reasonable, reduces external dependencies, and facilitates independent operation.
Features provided by CocoaPods
CocoaPods provides use_frameworks
, static_framework
, resource
, resource_bundles
the choice of these functions, and the final storage path of resource files after some of these functions are used in combination, resulting in various processing of image files, font files, localization files, and other files by users. The way is unknown.
Resource file storage path in combined scenarios
use_frameworks!
Different combinations of , static_framework
, resource_bundles
, and resources
, will result in different final storage paths for resource files. The following is a summary of the paths in various scenarios:
# use_frameworks! 动态库
# static_framework 静态库 podspec文件中不声明,默认为false
# resource_budles 生成独立的bundle
# resources 不生成独立的bundle,放置在.app根目录
# ---------------------------------------------------------------------------
use_frameworks!
static_framework = false
resource_bundles: .app/Frameworks/pod_name.framework/pod_name.bundle/.
resources: .app/Frameworks/pod_name.framework/.
static_framework = true
resource_bundles: .app/pod_name.bundle/.
resources: .app/.
# use_frameworks!
static_framework = false
resource_bundles: .app/pod_name.bundle/.
resources: .app/.
static_framework = true
resource_bundles: .app/pod_name.bundle/.
resources: .app/.
复制代码
(.app/. represents the main bundle)
You can refer to this article for the image value method and difference under different paths: [CocoaPods resource management and Asset Catalog optimization]( dreampiggy.com/2018/11/26/… Catalog optimization/)
It should be noted that when the .xcassets
type finally stored in the main bundle, it will .xcassets
conflict with the file of the main project, and the compilation will report an error:
error: Multiple commands produce ‘/Users/gonghonglou/Library/Developer/Xcode/DerivedData/HoloResource-giqfnwiluvssbzbyvuhpkrjoewvd/Build/Products/Debug-iphonesimulator/HoloResource_Example.app/Assets.car’: \1) Target ‘HoloResource_Example’ (project ‘HoloResource’) has compile command with input ‘/Users/gonghonglou/holo/HoloResource/Example/HoloResource/Images.xcassets’ \2) That command depends on command in Target ‘HoloResource_Example’ (project ‘HoloResource’): script phase “[CP] Copy Pods Resources”
解决方法,在 Podfile 里添加:
install! 'cocoapods', :disable_input_output_paths => true
复制代码
Tip: resource+不使用use_frameworks! +assets时会编译失败,因为工程只会生成一份Assets.car
文件
Showing All Messages
2022-03-26 20:42:51.046 ibtoold[25007:9169669] Launching AssetCatalogSimulatorAgent using native architecture.
/* com.apple.actool.errors */
: error: There are multiple stickers icon set or app icon set instances named "AppIcon".
Off-topic: After Xcode 10, a new compilation system was introduced, which made it impossible to compile into the project in time if there were code changes in the Pod. The solution is also the above method.
参考:Build System Release Notes for Xcode 10
Resource Handling Practices
ImageUIImage
public extension Resource where Base: UIImage {
/// 加载图片资源
/// - Parameters:
/// - name: 图片名
/// - bundleName: s.resource_bundles中设置的名字 一般为组件名
/// - Returns: description
static func loadImage(name: String, bundleName: String) -> UIImage? {
/// 静态库+独立bundle
var image = loadImage1(name: name, in: bundleName)
if image == nil {
/// 动态库+独立bundle
image = loadImage2(name: name, in: bundleName)
}
if image == nil {
/// 动态库+非独立bundle
image = loadImage3(name: name, in: bundleName)
}
if image == nil {
// 根目录 .app/
image = loadImage4(name: name, in: bundleName)
}
return image
}
/// 加载 .app/pod_name.bundle/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadImage1(name: String, in bundleName: String) -> UIImage? {
let pathComponent = "/\(bundleName).bundle"
return commonLoadImage(name: name, in: pathComponent)
}
/// 加载 .app/Frameworks/pod_name.framework/pod_name.bundle/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadImage2(name: String, in bundleName: String) -> UIImage? {
let pathComponent = "/Frameworks/\(bundleName).framework/\(bundleName).bundle"
return commonLoadImage(name: name, in: pathComponent)
}
/// 加载.app/Frameworks/pod_name.framework/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadImage3(name: String, in bundleName: String) -> UIImage? {
let pathComponent = "/Frameworks/\(bundleName).framework"
return commonLoadImage(name: name, in: pathComponent)
}
/// 加载.app/
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadImage4(name: String, in bundleName: String) -> UIImage? {
return UIImage(named: name)
}
fileprivate static func commonLoadImage(name: String, in pathComponent: String) -> UIImage? {
guard let resourcePath: String = Bundle.main.resourcePath else { return nil }
let bundlePath = resourcePath + pathComponent
let bundle = Bundle(path: bundlePath)
return UIImage(named: name, in: bundle, compatibleWith: nil)
}
}
复制代码
Each component UIImage.resource.loadImage(name: "图片名", bundleName: "组件名")
call can access the pictures in its own bundle.
The four paths mentioned here are prioritized:
1. The first access is the path of the static library + resource_bundles
2, the second is the dynamic library path using use_frameworks!
+ 3, the dynamic library path using + resource_bundles
is accessed again 4, and finally the main bundle pathuse_frameworks!
resources
The most recommended and reasonable configuration method is of course the first one, providing static libraries and using to resource_bundles
store resource files.
Path Bundle
Since pictures can be encapsulated in this way, then refer to this idea to encapsulate a bundle as a basic method. With a unified bundle calling method, access to other resource files is much simpler.
public extension Resource where Base: Bundle {
/// 加载图片资源
/// - Parameters:
/// - name: 图片名
/// - bundleName: s.resource_bundles中设置的名字 一般为组件名
/// - Returns: description
static func loadBundle(in bundleName: String) -> Bundle? {
var image = loadBundle1(in: bundleName)
if image == nil {
image = loadBundle2(in: bundleName)
}
if image == nil {
image = loadBundle3(in: bundleName)
}
if image == nil {
image = loadBundle4(in: bundleName)
}
return image
}
/// 加载 .app/pod_name.bundle/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadBundle1(in bundleName: String) -> Bundle? {
let pathComponent = "/\(bundleName).bundle"
return commonLoadBundle(in: pathComponent)
}
/// 加载 .app/Frameworks/pod_name.framework/pod_name.bundle/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadBundle2(in bundleName: String) -> Bundle? {
let pathComponent = "/Frameworks/\(bundleName).framework/\(bundleName).bundle"
return commonLoadBundle(in: pathComponent)
}
/// 加载.app/Frameworks/pod_name.framework/.
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadBundle3(in bundleName: String) -> Bundle? {
let pathComponent = "/Frameworks/\(bundleName).framework"
return commonLoadBundle(in: pathComponent)
}
/// 加载.app/
/// - Parameters:
/// - name: 图片名
/// - bundleName: 组件名
/// - Returns: description
fileprivate static func loadBundle4(in bundleName: String) -> Bundle? {
return Bundle.main
}
fileprivate static func commonLoadBundle(in pathComponent: String) -> Bundle? {
guard let resourcePath = Bundle.main.resourcePath else { return nil }
let bundlePath = resourcePath + pathComponent
let bundle = Bundle(path: bundlePath)
return bundle
}
}
复制代码