diff --git a/AssetChecker.podspec b/AssetChecker.podspec index ec7af47..e418cf0 100644 --- a/AssetChecker.podspec +++ b/AssetChecker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AssetChecker' - s.version = '0.2.1' + s.version = '0.3.1' s.summary = 'Sanitizes your Assets.xcassets files' s.description = "AssetChecker is a tiny run script that keeps your Assets.xcassets files clean and emits warnings when something is suspicious." s.homepage = 'https://github.com/freshos/AssetChecker' diff --git a/Classes/main.swift b/Classes/main.swift index c9cda15..6f9f424 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -3,34 +3,11 @@ import Foundation // Configure me \o/ -var sourcePathOption:String? = nil -var assetCatalogPathOption:String? = nil -let ignoredUnusedNames = [String]() - -for (index, arg) in CommandLine.arguments.enumerated() { - switch index { - case 1: - sourcePathOption = arg - case 2: - assetCatalogPathOption = arg - default: - break - } -} - -guard let sourcePath = sourcePathOption else { - print("AssetChecker:: error: Source path was missing!") - exit(0) -} - -guard let assetCatalogAbsolutePath = assetCatalogPathOption else { - print("AssetChecker:: error: Asset Catalog path was missing!") - exit(0) -} - -print("Searching sources in \(sourcePath) for assets in \(assetCatalogAbsolutePath)") +var sourcePathOption:String? +var ignoredUnusedNames = [String]() +var catalogPath: String? -/* Put here the asset generating false positives, +/* Put here the asset generating false positives, For instance whne you build asset names at runtime let ignoredUnusedNames = [ "IconArticle", @@ -38,13 +15,49 @@ let ignoredUnusedNames = [ "voteEN", "voteES", "voteFR" -] +] */ - // MARK : - End Of Configurable Section -func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [String] { +/// Attempt to fetch source path from run script arguments +// command line arguments passed as "source:/path/to" +struct CommandLineArg { + let arg: String + let value: String? +} + +let commandLineArguments = CommandLine.arguments.map { clArg -> CommandLineArg in + let splitArgs = clArg.split(separator: ":") + let value = splitArgs.indices.contains(1) ? String(splitArgs[1]) : nil + return CommandLineArg(arg: String(splitArgs[0]), value: value) +} + +for arg in commandLineArguments { + switch arg.arg { + case "source": + if let sourcePath = arg.value, sourcePathOption == nil { + sourcePathOption = sourcePath + } + case "catalog": + if let catelog = arg.value, catalogPath == nil { + catalogPath = catelog + } + case "ignore": + if let ignoreAssetsNames = arg.value, ignoredUnusedNames.isEmpty { + ignoredUnusedNames = ignoreAssetsNames.split(separator: ",").map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + } + default: + break + } +} + +guard let sourcePath = sourcePathOption else { + print("AssetChecker:: error: Source path was missing!") + exit(0) +} + +private func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [String] { var elements = [String]() while let e = enumerator?.nextObject() as? String { elements.append(e) @@ -52,83 +65,103 @@ func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [St return elements } +/// Search for asset catalogs within the source path -// MARK: - List Assets - -func listAssets() -> [String] { - let extensionName = "imageset" - let enumerator = FileManager.default.enumerator(atPath: assetCatalogAbsolutePath) - 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 -} +let assetCatalogPaths: [String] = { + if let providedCatalog = catalogPath { + return [providedCatalog] + } else { + // detect automatically + return elementsInEnumerator(FileManager.default.enumerator(atPath: sourcePath)).filter { $0.hasSuffix(".xcassets") } + } +}() +print("Searching sources in \(sourcePath) for assets in \(assetCatalogPaths)") -// MARK: - List Used Assets in the codebase - -func localizedStrings(inStringFile: String) -> [String] { - var localizedStrings = [String]() - let namePattern = "([\\w-]+)" - let patterns = [ - "#imageLiteral\\(resourceName: \"\(namePattern)\"\\)", // Image Literal - "UIImage\\(named:\\s*\"\(namePattern)\"\\)", // Default UIImage call (Swift) - "UIImage imageNamed:\\s*\\@\"\(namePattern)\"", // Default UIImage call - "\\ [(asset: String, catalog: String)] { + + return assetCatalogPaths.flatMap { (catalog) -> [(asset: String, catalog: String)] in + + let extensionName = "imageset" + let enumerator = FileManager.default.enumerator(atPath: catalog) + 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 + .map { (asset: $0,catalog: catalog)} } - return localizedStrings } -func listUsedAssetLiterals() -> [String] { +/// List Assets used in the codebase +private func listUsedAssetLiterals() -> [String: [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} - .compactMap{$0} // Remove nil entries - .map(localizedStrings) // 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} - .flatMap{$0} // Remove nil entries - .map(localizedStrings) // Find localizedStrings ocurrences - .flatMap{$0} // Flatten - #endif + var assetUsageMap: [String: [String]] = [:] + + // Only Swift and Obj-C files + let files = elementsInEnumerator(enumerator) + .filter { $0.hasSuffix(".m") || $0.hasSuffix(".swift") || $0.hasSuffix(".xib") || $0.hasSuffix(".storyboard") } + + /// Find sources of assets within the contents of a file + func localizedStrings(inStringFile: String) -> [String] { + var assetStringReferences = [String]() + let namePattern = "([\\w-]+)" + let patterns = [ + "#imageLiteral\\(resourceName: \"\(namePattern)\"\\)", // Image Literal + "UIImage\\(named:\\s*\"\(namePattern)\"\\)", // Default UIImage call (Swift) + "UIImage imageNamed:\\s*\\@\"\(namePattern)\"", // Default UIImage call + "\\ Bool in !usedAssetNames.contains(asset) && !ignoredUnusedNames.contains(asset) }) +unused.forEach { print("\($1):: warning: [Asset Unused] \($0)") } // Generate Error for broken Assets -let broken = used.subtracting(assets) -broken.forEach { print("\(assetCatalogAbsolutePath):: error: [Asset Missing] \($0)") } +let broken = usedAssets.filter { (assetName, references) -> Bool in !availableAssetNames.contains(assetName) } +broken.forEach { print("\($1.first ?? $0):: error: [Asset Missing] \($0)") } if broken.count > 0 { exit(1) diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index e323d60..53ba587 100644 --- a/Example/AssetChecker.xcodeproj/project.pbxproj +++ b/Example/AssetChecker.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 3333CADC9CDF4D6C0279EAF6 /* Pods_AssetChecker_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0BF5C510348421D31756E79F /* Pods_AssetChecker_Tests.framework */; }; + 5D7631B4214170BA0017A471 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5D7631B3214170BA0017A471 /* Media.xcassets */; }; 5D8C437E1FC6CB2100DDF7D0 /* run in Resources */ = {isa = PBXBuildFile; fileRef = 5D8C437D1FC6CB2100DDF7D0 /* run */; }; 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; @@ -35,6 +36,7 @@ 4E3F248C6D57FAF6F94A24BC /* Pods-AssetChecker_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AssetChecker_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AssetChecker_Tests/Pods-AssetChecker_Tests.debug.xcconfig"; sourceTree = ""; }; 5512D041F5A3EF8493D7505C /* Pods-AssetChecker_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AssetChecker_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-AssetChecker_Example/Pods-AssetChecker_Example.debug.xcconfig"; sourceTree = ""; }; 58816321B24F6A6272E52BB1 /* Pods-AssetChecker_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AssetChecker_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-AssetChecker_Tests/Pods-AssetChecker_Tests.release.xcconfig"; sourceTree = ""; }; + 5D7631B3214170BA0017A471 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; 5D8C437D1FC6CB2100DDF7D0 /* run */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; name = run; path = ../run; sourceTree = ""; }; 607FACD01AFB9204008FA782 /* AssetChecker_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AssetChecker_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -71,6 +73,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5D7631B2214170A70017A471 /* Resources */ = { + isa = PBXGroup; + children = ( + 5D7631B3214170BA0017A471 /* Media.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( @@ -100,6 +110,7 @@ 607FACD91AFB9204008FA782 /* Main.storyboard */, 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, + 5D7631B2214170A70017A471 /* Resources */, 607FACD31AFB9204008FA782 /* Supporting Files */, ); name = "Example for AssetChecker"; @@ -219,6 +230,7 @@ TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; + DevelopmentTeam = JEH7PW263G; LastSwiftMigration = 0900; }; 607FACE41AFB9204008FA782 = { @@ -252,6 +264,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5D7631B4214170BA0017A471 /* Media.xcassets in Resources */, 5D8C437E1FC6CB2100DDF7D0 /* run in Resources */, 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, @@ -362,7 +375,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/AssetChecker/run --catalog ${SRCROOT}/Resource/Images.xcassets"; + shellScript = "${PODS_ROOT}/AssetChecker/run --ignore \"icon_red\"\n"; }; CD3F94CDB64297E52FBE5D36 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -536,6 +549,7 @@ baseConfigurationReference = 5512D041F5A3EF8493D7505C /* Pods-AssetChecker_Example.debug.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JEH7PW263G; INFOPLIST_FILE = AssetChecker/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; @@ -551,6 +565,7 @@ baseConfigurationReference = C2AB56DA4BC27DE54F97B2E5 /* Pods-AssetChecker_Example.release.xcconfig */; buildSettings = { ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + DEVELOPMENT_TEAM = JEH7PW263G; INFOPLIST_FILE = AssetChecker/Info.plist; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; MODULE_NAME = ExampleApp; diff --git a/Example/AssetChecker/Base.lproj/LaunchScreen.xib b/Example/AssetChecker/Base.lproj/LaunchScreen.xib index 0ec11cb..5c995be 100644 --- a/Example/AssetChecker/Base.lproj/LaunchScreen.xib +++ b/Example/AssetChecker/Base.lproj/LaunchScreen.xib @@ -1,8 +1,13 @@ - - + + + + + - + + + @@ -12,7 +17,7 @@ + + + + + + + + + + + - + + + + + + + + + diff --git a/Example/AssetChecker/Base.lproj/Main.storyboard b/Example/AssetChecker/Base.lproj/Main.storyboard index 1b5cfbe..8e75148 100644 --- a/Example/AssetChecker/Base.lproj/Main.storyboard +++ b/Example/AssetChecker/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -21,14 +21,17 @@ - + - - + + - - + + + + + @@ -41,9 +44,19 @@ + + + + + + + + + + diff --git a/Example/AssetChecker/Images.xcassets/Contents.json b/Example/AssetChecker/Images.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/AssetChecker/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Images.xcassets/icon_green.imageset/Contents.json b/Example/AssetChecker/Images.xcassets/icon_green.imageset/Contents.json new file mode 100644 index 0000000..234dcea --- /dev/null +++ b/Example/AssetChecker/Images.xcassets/icon_green.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icons8-48.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-96.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icons8-144.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-144.png b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-144.png new file mode 100644 index 0000000..bb31ab7 Binary files /dev/null and b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-144.png differ diff --git a/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-48.png b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-48.png new file mode 100644 index 0000000..9b2c504 Binary files /dev/null and b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-48.png differ diff --git a/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-96.png b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-96.png new file mode 100644 index 0000000..889a5dd Binary files /dev/null and b/Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-96.png differ diff --git a/Example/AssetChecker/Images.xcassets/icon_purple.imageset/Contents.json b/Example/AssetChecker/Images.xcassets/icon_purple.imageset/Contents.json new file mode 100644 index 0000000..107fffe --- /dev/null +++ b/Example/AssetChecker/Images.xcassets/icon_purple.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icons8-icons8_purple.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Images.xcassets/icon_purple.imageset/icons8-icons8_purple.pdf b/Example/AssetChecker/Images.xcassets/icon_purple.imageset/icons8-icons8_purple.pdf new file mode 100644 index 0000000..2a3fffa Binary files /dev/null and b/Example/AssetChecker/Images.xcassets/icon_purple.imageset/icons8-icons8_purple.pdf differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/Contents.json b/Example/AssetChecker/Resources/Media.xcassets/Contents.json new file mode 100644 index 0000000..da4a164 --- /dev/null +++ b/Example/AssetChecker/Resources/Media.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/Contents.json b/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/Contents.json new file mode 100644 index 0000000..86cd985 --- /dev/null +++ b/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icons8-icons8_blue.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/icons8-icons8_blue.pdf b/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/icons8-icons8_blue.pdf new file mode 100644 index 0000000..39025b2 Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/icons8-icons8_blue.pdf differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/Contents.json b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/Contents.json new file mode 100644 index 0000000..546f6df --- /dev/null +++ b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icons8-49.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-96.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icons8-144.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-144.png b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-144.png new file mode 100644 index 0000000..c1b9360 Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-144.png differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-49.png b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-49.png new file mode 100644 index 0000000..c69d37c Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-49.png differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-96.png b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-96.png new file mode 100644 index 0000000..068c1f6 Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-96.png differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/Contents.json b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/Contents.json new file mode 100644 index 0000000..234dcea --- /dev/null +++ b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "icons8-48.png", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "icons8-96.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "icons8-144.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-144.png b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-144.png new file mode 100644 index 0000000..a5bbd0e Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-144.png differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-48.png b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-48.png new file mode 100644 index 0000000..5e83c9f Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-48.png differ diff --git a/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-96.png b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-96.png new file mode 100644 index 0000000..ae65ed8 Binary files /dev/null and b/Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-96.png differ diff --git a/Example/AssetChecker/ViewController.swift b/Example/AssetChecker/ViewController.swift index e92141b..9e1ba68 100644 --- a/Example/AssetChecker/ViewController.swift +++ b/Example/AssetChecker/ViewController.swift @@ -10,15 +10,21 @@ import UIKit class ViewController: UIViewController { + @IBOutlet weak var imageViewOne: UIImageView! + @IBOutlet weak var imageViewTwo: UIImageView! + @IBOutlet weak var imageViewThree: UIImageView! + override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. + + imageViewOne.image = UIImage(named: "icon_orange") + imageViewTwo.image = UIImage(named: "icon_vermillion") } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } - } diff --git a/README.md b/README.md index 611f569..fafd849 100644 --- a/README.md +++ b/README.md @@ -18,20 +18,31 @@ Because **Image Assets** files are not safe, if an asset is ever deleted, nothin AssetChecker is part of [freshOS](http://freshos.org) iOS toolset. Try it in an example App ! Download Starter Project -## How -By using a **script** running automatically, you have a **safety net** that makes using Asset Catalog a breeze. - ## What +By using a **script** running automatically, you have a **safety net** that makes using Asset Catalog a breeze. -Automatically (On build) - - Raises Errors for **Missing Assets** - - Raises warnings for **Unused Assets** - +## How +The script will automatically find the asset catalogs ( `.xcassets`) in your project and serarch your source code to determine if there are errors with them. It searches for the following types of files: +- xibs +- storyboards +- swift files +- Obj-C (.m) files + +For these types of references: +- `#imageLiteral(resourceName: )` +- `UIImage(named: )` (swift) +- `[UIImage imageNamed: ]` (ObjC) +- `R.image.name()` (supports [R.Swift](https://github.com/mac-cain13/R.swift)) + +Then the script will automatically (On build) + - Raise Errors for **Missing Assets** + - Raise warnings for **Unused Assets** + ## Installation Installation available via Cocoapods. Add the following to your Podfile: ```shell -pod 'AssetChecker', :git => 'https://github.com/joeboyscout04/AssetChecker.git', :branch => 'pods' +pod 'AssetChecker' ``` Or copy the script into your project. @@ -39,27 +50,25 @@ Add the following `Run Script` in XCode, this will run the script at every build If you installed via Cocoapods, you can use the following script: ```shell -${PODS_ROOT}/AssetChecker/run --catalog ${SRCROOT}/Resource/Images.xcassets +${PODS_ROOT}/AssetChecker/run ``` with arguments: ``` ---catalog Absolute path to your Asset catalog (required) ---source Absolute path to your source directory. Defaults to $SRCROOT +--source (optional) Absolute path to your source directory. Defaults to $SRCROOT ``` If you didn't use Cocoapods, use the path of where you copied AssetChecker script: ```shell -${SRCROOT}/{PATH_TO_THE_SCRIPT}/AssetChecker.swift ${SRCROOT}/Sources ${SRCROOT}/Resources/Images.xcassets +${SRCROOT}/{PATH_TO_THE_SCRIPT}/AssetChecker.swift ${SRCROOT}/Sources ``` -In this example your source files are located in `/Sources` and your Asset catalog is in `/Resources/Images.xcassets`. +In this example your source files are located in `/Sources`. And configure top section of the script : ```swift // Configure me \o/ let sourcePath = "/Sources" -let assetCatalogPath = "/Resources/Assets.xcassets" let ignoredUnusedNames = [String]() ``` Run and Enjoy \o/ diff --git a/run b/run index d056885..b05555d 100755 --- a/run +++ b/run @@ -8,6 +8,7 @@ swiftFile="${DIR}/Classes/main.swift" SOURCE=$SRCROOT CATALOG="" +IGNORE="" while [[ $# > 0 ]] do @@ -22,6 +23,10 @@ case $key in CATALOG="$2" shift # past argument ;; + -i|--ignore) + IGNORE="$2" + shift # past argument + ;; *) # unknown option @@ -30,6 +35,6 @@ esac shift # past argument or value done -cmd="$swiftFile $SOURCE $CATALOG" +cmd="$swiftFile source:$SOURCE catalog:$CATALOG ignore:$IGNORE" echo $cmd $cmd