diff --git a/Classes/main.swift b/Classes/main.swift index c9cda15..23e1296 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -5,6 +5,7 @@ import Foundation // Configure me \o/ var sourcePathOption:String? = nil var assetCatalogPathOption:String? = nil +var colorAssetCatalogPathOption:String? = nil let ignoredUnusedNames = [String]() for (index, arg) in CommandLine.arguments.enumerated() { @@ -13,6 +14,9 @@ for (index, arg) in CommandLine.arguments.enumerated() { sourcePathOption = arg case 2: assetCatalogPathOption = arg + colorAssetCatalogPathOption = arg + case 3: + colorAssetCatalogPathOption = arg default: break } @@ -28,6 +32,11 @@ guard let assetCatalogAbsolutePath = assetCatalogPathOption else { exit(0) } +guard let colorAssetCatalogAbsolutePath = colorAssetCatalogPathOption else { + print("AssetChecker:: error: Color Asset Catalog path was missing!") + exit(0) +} + print("Searching sources in \(sourcePath) for assets in \(assetCatalogAbsolutePath)") /* Put here the asset generating false positives, @@ -133,3 +142,77 @@ broken.forEach { print("\(assetCatalogAbsolutePath):: error: [Asset Missing] \($ if broken.count > 0 { exit(1) } + + +// MARK: Colors +func listColorAssets() -> [String] { + let extensionName = "colorset" + let enumerator = FileManager.default.enumerator(atPath: colorAssetCatalogAbsolutePath) + return elementsInEnumerator(enumerator) + .filter { $0.hasSuffix(extensionName) } // Is Asset + .map { $0.replacingOccurrences(of: ".\(extensionName)", with: "") } // Remove extension + .map { $0.components(separatedBy: "/").last ?? $0 } // Remove folder path +} + +func colorStrings(inStringFile: String) -> [String] { + var localizedStrings = [String]() + let namePattern = "([\\w-]+)" + let patterns = [ + "#colorLiteral\\(resourceName: \"\(namePattern)\"\\)", // Image Literal + "UIColor\\(named:\\s*\"\(namePattern)\"\\)", // Default UIImage call (Swift) + "UIColor colorNamed:\\s*\\@\"\(namePattern)\"", // Default UIImage call + "\\ [String] { + let enumerator = FileManager.default.enumerator(atPath:sourcePath) + print(sourcePath) + + #if swift(>=4.1) + return elementsInEnumerator(enumerator) + .filter { $0.hasSuffix(".m") || $0.hasSuffix(".swift") || $0.hasSuffix(".xib") || $0.hasSuffix(".storyboard") } // Only Swift and Obj-C files + .map { "\(sourcePath)/\($0)" } // Build file paths + .map { try? String(contentsOfFile: $0, encoding: .utf8)} // Get file contents + .compactMap{$0} // Remove nil entries + .map(colorStrings) // Find localizedStrings ocurrences + .flatMap{$0} // Flatten + #else + return elementsInEnumerator(enumerator) + .filter { $0.hasSuffix(".m") || $0.hasSuffix(".swift") || $0.hasSuffix(".xib") || $0.hasSuffix(".storyboard") } // Only Swift and Obj-C files + .map { "\(sourcePath)/\($0)" } // Build file paths + .map { try? String(contentsOfFile: $0, encoding: .utf8)} // Get file contents + .flatMap{$0} // Remove nil entries + .map(colorStrings) // Find localizedStrings ocurrences + .flatMap{$0} // Flatten + #endif +} + +print("Searching colors in \(sourcePath) for color assets in \(colorAssetCatalogAbsolutePath)") + +let colors = Set(listColorAssets()) +let usedColors = Set(listUsedColorAssetLiterals()) + +// Generate Warnings for Unused Assets +let unusedColors = colors.subtracting(usedColors) +unusedColors.forEach { print("\(colorAssetCatalogAbsolutePath):: warning: [Color Unused] \($0)") } + +// Generate Error for broken Assets +let brokenColors = usedColors.subtracting(colors) +brokenColors.forEach { print("\(assetCatalogAbsolutePath):: error: [Color Missing] \($0)") } + +if brokenColors.count > 0 { + exit(1) +} diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index e323d60..73b94ca 100644 --- a/Example/AssetChecker.xcodeproj/project.pbxproj +++ b/Example/AssetChecker.xcodeproj/project.pbxproj @@ -362,7 +362,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/AssetChecker/run --catalog ${SRCROOT}/Resource/Images.xcassets"; + shellScript = "${PODS_ROOT}/AssetChecker/run --catalog ${SRCROOT}/AssetChecker/Images.xcassets --colors ${SRCROOT}/AssetChecker/Images.xcassets\n"; }; CD3F94CDB64297E52FBE5D36 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; diff --git a/Example/AssetChecker/Base.lproj/Main.storyboard b/Example/AssetChecker/Base.lproj/Main.storyboard index 1b5cfbe..de934fd 100644 --- a/Example/AssetChecker/Base.lproj/Main.storyboard +++ b/Example/AssetChecker/Base.lproj/Main.storyboard @@ -1,11 +1,10 @@ - - - - + + - + + @@ -22,13 +21,14 @@ - + - + - + + @@ -44,6 +44,12 @@ + + + + + + diff --git a/Example/AssetChecker/Images.xcassets/Contents.json b/Example/AssetChecker/Images.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/Example/AssetChecker/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Example/AssetChecker/Images.xcassets/UnusedColor.colorset/Contents.json b/Example/AssetChecker/Images.xcassets/UnusedColor.colorset/Contents.json new file mode 100644 index 0000000..22c4bb0 --- /dev/null +++ b/Example/AssetChecker/Images.xcassets/UnusedColor.colorset/Contents.json @@ -0,0 +1,38 @@ +{ + "colors" : [ + { + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + }, + { + "appearances" : [ + { + "appearance" : "luminosity", + "value" : "dark" + } + ], + "color" : { + "color-space" : "srgb", + "components" : { + "alpha" : "1.000", + "blue" : "1.000", + "green" : "1.000", + "red" : "1.000" + } + }, + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/run b/run index d056885..e97839a 100755 --- a/run +++ b/run @@ -8,6 +8,7 @@ swiftFile="${DIR}/Classes/main.swift" SOURCE=$SRCROOT CATALOG="" +COLORS_CATALOG="" while [[ $# > 0 ]] do @@ -22,6 +23,10 @@ case $key in CATALOG="$2" shift # past argument ;; + --colors) + COLORS_CATALOG="$2" + shift # past argument + ;; *) # unknown option @@ -30,6 +35,6 @@ esac shift # past argument or value done -cmd="$swiftFile $SOURCE $CATALOG" +cmd="$swiftFile $SOURCE $CATALOG $COLORS_CATALOG" echo $cmd $cmd