In fact, there is already a good command line tool written by Swift to detect unused image resources, namely FengNiao of Miaoshen. As for why TO write another one, it is mainly for learning SwiftSyntax. Some time ago, I wrote an article briefly introducing SwiftSyntax. SwiftSyntax is explained in detail here. If you are not familiar with SwiftSyntax, please take a look at it first.
The project is here UnusedResources. Here is a brief introduction to the principle.
1. Find all the image resources in the project
I’m looking for the following picture resources:
let imageExtensions: [String] = ["png"."jpg"."gif"."pdf"]
Copy the code
I have to mention that Homebrew author MXCL’s open source library Path is very useful. Let’s define a method to find the file with the desired extension
func recursiveFiles(withExtensions exts: [String], at path: Path) throws- > [Path] {
if path.isFile {
if exts.contains(path.extension) {
return [path]
}
return[]}else if path.isDirectory {
var files: [Path] = []
for entry in try path.ls() {
let list = try recursiveFiles(withExtensions: exts, at: entry.path)
files.append(contentsOf: list)
}
return files
}
return[]}Copy the code
All you need to do to find all the image resources is the following simple code
var images: [Path] = try recursiveFiles(withExtensions: imageExtensions, at: path)
Copy the code
2. View the source code files in the project
Find the source code file
var files: [Path] = try recursiveFiles(withExtensions: ["swift"."h"."m"."mm"], at: path)
Copy the code
That’s where SwiftSyntax comes in. We define a StringVisitor that walks through the syntax tree, retrieves the string and decides if it matches the image name, which means the image is used.
Our swift code is a simple syntax tree, can use xcrun swiftc frontend – emit – syntax. / the swift | python – m json tool view source command syntax tree structure, but it is complicated, The part that contains the import library, so I chose to use Swift AST Explorer, which is based on SwiftSyntax implementation, the syntax tree is more concise.
We just take the content of the StringSegmentSyntax and compare it to the image, which is the same as the image, which means the image is being used.
public struct StringVisitor: SyntaxVisitor {
public var images: [Path] = []
public init(_ images: [Path]) {
self.images = images
}
// Ignore string interpolated literal because it's too complex to compare public mutating func visit(_ node: StringLiteralExprSyntax) -> SyntaxVisitorContinueKind { guard node.segments.count == 1 else { return .skipChildren } return .visitChildren } public mutating func visit(_ node: StringSegmentSyntax) -> SyntaxVisitorContinueKind { let text = node.content.text .trimmingCharacters(in: .whitespacesAndNewlines) guard ! text.isEmpty else { return .skipChildren } images = images.filter { $0.basename(dropExtension: true) ! = text } return .skipChildren } }Copy the code
OC and Swift syntax are slightly different, so we can get all the TokenSyntax and find that stringLiteral is of type stringLiteral. Here I’m going to do the simple thing and check if stringLiteral contains the image name, if it does it means the image is used.
So let’s add another method to the StringVisitor
// for Objective-C
public mutating func visit(_ token: TokenSyntax) -> SyntaxVisitorContinueKind {
switch token.tokenKind {
case .stringLiteral(let text):
images = images.filter { !text.contains($0.basename(dropExtension: true))}default:
break
}
return .visitChildren
}
Copy the code
3. View the XML file
let xmlExtensions: [String] = ["xib"."storyboard"]
var xmlFiles: [Path] = try recursiveFiles(withExtensions: xmlExtensions, at: path)
Copy the code
You need to use XMLParser to find an attributeDict value whose key is “image”. If it matches the name of the image, the image is used.
4. Makefile
Unused-resources is automatically copied to /usr/local/bin when building a project using a Makefile.
5. To summarize
Hydrology, recently in the study of compiling related knowledge, progress is very slow, heart tired.