From b094e22e97cce702dd1fe872a35f7456964a98a4 Mon Sep 17 00:00:00 2001 From: Joe Elliott Date: Wed, 6 Dec 2017 19:41:29 +0100 Subject: [PATCH 1/8] work on finding asset catalogs and refactor. --- Classes/main.swift | 29 ++++++++++++------- .../AssetChecker.xcodeproj/project.pbxproj | 12 ++++++++ 2 files changed, 31 insertions(+), 10 deletions(-) diff --git a/Classes/main.swift b/Classes/main.swift index 5a9f6fd..3227da7 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -52,16 +52,25 @@ func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [St return elements } +// MARK: - Search for asset catalogs +let assetCatalogPaths = elementsInEnumerator(FileManager.default.enumerator(atPath: sourcePath)).filter { $0.hasSuffix(".xcassets") } + +print("Searching sources in \(sourcePath) for assets in \(assetCatalogPaths)") // 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 +func listAssets() -> [(asset: String, catalog: String)] { + + return assetCatalogPaths.flatMap { (catalog) -> [(asset: String, catalog: String)] in + + 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 + .map { (asset: $0,catalog: catalog)} + } } @@ -88,14 +97,12 @@ func localizedStrings(inStringFile: String) -> [String] { return localizedStrings } -func listUsedAssetLiterals() -> [String] { +func listUsedAssetLiterals() -> [(asset: String, references: [String])] { let enumerator = FileManager.default.enumerator(atPath:sourcePath) - print(sourcePath) 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 @@ -108,11 +115,13 @@ let used = Set(listUsedAssetLiterals() + ignoredUnusedNames) // Generate Warnings for Unused Assets +// (name, catalog) let unused = assets.subtracting(used) unused.forEach { print("\(assetCatalogAbsolutePath):: warning: [Asset Unused] \($0)") } // Generate Error for broken Assets +// (name, [fileReferences]) let broken = used.subtracting(assets) broken.forEach { print("\(assetCatalogAbsolutePath):: error: [Asset Missing] \($0)") } diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index e323d60..30f6e08 100644 --- a/Example/AssetChecker.xcodeproj/project.pbxproj +++ b/Example/AssetChecker.xcodeproj/project.pbxproj @@ -15,6 +15,7 @@ 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; + AFC739991FD86E9E00B5B103 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFC739981FD86E9E00B5B103 /* Media.xcassets */; }; BA341A14EF45F09231AACBFE /* Pods_AssetChecker_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2F82D1638FD77B9042FE9A6 /* Pods_AssetChecker_Example.framework */; }; /* End PBXBuildFile section */ @@ -47,6 +48,7 @@ 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 63C1D8F311B1C327036F7550 /* AssetChecker.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = AssetChecker.podspec; path = ../AssetChecker.podspec; sourceTree = ""; }; + AFC739981FD86E9E00B5B103 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; B2F82D1638FD77B9042FE9A6 /* Pods_AssetChecker_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AssetChecker_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C2AB56DA4BC27DE54F97B2E5 /* Pods-AssetChecker_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AssetChecker_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AssetChecker_Example/Pods-AssetChecker_Example.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -95,6 +97,7 @@ 607FACD21AFB9204008FA782 /* Example for AssetChecker */ = { isa = PBXGroup; children = ( + AFC739971FD86E8E00B5B103 /* Resources */, 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 607FACD71AFB9204008FA782 /* ViewController.swift */, 607FACD91AFB9204008FA782 /* Main.storyboard */, @@ -142,6 +145,14 @@ name = "Podspec Metadata"; sourceTree = ""; }; + AFC739971FD86E8E00B5B103 /* Resources */ = { + isa = PBXGroup; + children = ( + AFC739981FD86E9E00B5B103 /* Media.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; D341C51AD021821B48CE184D /* Frameworks */ = { isa = PBXGroup; children = ( @@ -252,6 +263,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + AFC739991FD86E9E00B5B103 /* Media.xcassets in Resources */, 5D8C437E1FC6CB2100DDF7D0 /* run in Resources */, 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, From f2e28de18fd61d21b0568df9ad8e2cf7db47c211 Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Thu, 6 Sep 2018 17:19:26 +0200 Subject: [PATCH 2/8] Find asset catalogs, add some testing images, improve error messages. --- Classes/main.swift | 78 +++++++++--------- .../AssetChecker.xcodeproj/project.pbxproj | 29 ++++--- .../AssetChecker/Base.lproj/LaunchScreen.xib | 18 ++-- .../AssetChecker/Base.lproj/Main.storyboard | 20 +++-- .../Images.xcassets/Contents.json | 6 ++ .../icon_green.imageset/Contents.json | 23 ++++++ .../icon_green.imageset/icons8-144.png | Bin 0 -> 2264 bytes .../icon_green.imageset/icons8-48.png | Bin 0 -> 656 bytes .../icon_green.imageset/icons8-96.png | Bin 0 -> 1382 bytes .../icon_purple.imageset/Contents.json | 12 +++ .../icons8-icons8_purple.pdf | Bin 0 -> 1522 bytes .../Resources/Media.xcassets/Contents.json | 6 ++ .../icon_blue.imageset/Contents.json | 12 +++ .../icon_blue.imageset/icons8-icons8_blue.pdf | Bin 0 -> 1520 bytes .../icon_red.imageset/Contents.json | 23 ++++++ .../icon_red.imageset/icons8-144.png | Bin 0 -> 1969 bytes .../icon_red.imageset/icons8-48.png | Bin 0 -> 672 bytes .../icon_red.imageset/icons8-96.png | Bin 0 -> 1276 bytes 18 files changed, 162 insertions(+), 65 deletions(-) create mode 100644 Example/AssetChecker/Images.xcassets/Contents.json create mode 100644 Example/AssetChecker/Images.xcassets/icon_green.imageset/Contents.json create mode 100644 Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-144.png create mode 100644 Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-48.png create mode 100644 Example/AssetChecker/Images.xcassets/icon_green.imageset/icons8-96.png create mode 100644 Example/AssetChecker/Images.xcassets/icon_purple.imageset/Contents.json create mode 100644 Example/AssetChecker/Images.xcassets/icon_purple.imageset/icons8-icons8_purple.pdf create mode 100644 Example/AssetChecker/Resources/Media.xcassets/Contents.json create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/Contents.json create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_blue.imageset/icons8-icons8_blue.pdf create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/Contents.json create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-144.png create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-48.png create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_red.imageset/icons8-96.png diff --git a/Classes/main.swift b/Classes/main.swift index 3227da7..abfca5a 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -4,15 +4,12 @@ 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 } @@ -23,27 +20,6 @@ guard let sourcePath = sourcePathOption else { 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)") - -/* Put here the asset generating false positives, - For instance whne you build asset names at runtime -let ignoredUnusedNames = [ - "IconArticle", - "IconMedia", - "voteEN", - "voteES", - "voteFR" -] -*/ - - -// MARK : - End Of Configurable Section - func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [String] { var elements = [String]() while let e = enumerator?.nextObject() as? String { @@ -64,7 +40,7 @@ func listAssets() -> [(asset: String, catalog: String)] { return assetCatalogPaths.flatMap { (catalog) -> [(asset: String, catalog: String)] in let extensionName = "imageset" - let enumerator = FileManager.default.enumerator(atPath: assetCatalogAbsolutePath) + let enumerator = FileManager.default.enumerator(atPath: catalog) return elementsInEnumerator(enumerator) .filter { $0.hasSuffix(extensionName) } // Is Asset .map { $0.replacingOccurrences(of: ".\(extensionName)", with: "") } // Remove extension @@ -97,33 +73,57 @@ func localizedStrings(inStringFile: String) -> [String] { return localizedStrings } -func listUsedAssetLiterals() -> [(asset: String, references: [String])] { +func listUsedAssetLiterals() -> [String: [String]] { let enumerator = FileManager.default.enumerator(atPath:sourcePath) - return elementsInEnumerator(enumerator) + + var assetUsageMap: [String: [String]] = [:] + + let files = 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(localizedStrings) // Find localizedStrings ocurrences - .flatMap{$0} // Flatten + + for filename in files { + // Build file paths + let filepath = "\(sourcePath)/\(filename)" + + // Get file contents + if let fileContents = try? String(contentsOfFile: filepath, encoding: .utf8) { + // Find occurrences of asset names + let references = localizedStrings(inStringFile: fileContents) + + // assemble the map + for asset in references { + let updatedReferences = assetUsageMap[asset] ?? [] + assetUsageMap[asset] = updatedReferences + [filename] + } + } + } + + return assetUsageMap } // MARK: - Begining of script -let assets = Set(listAssets()) -let used = Set(listUsedAssetLiterals() + ignoredUnusedNames) + +let availableAssets = listAssets() +let availableAssetNames = Set(availableAssets.map{$0.asset} ) +let usedAssets = listUsedAssetLiterals() +let usedAssetNames = Set(usedAssets.keys + ignoredUnusedNames) // Generate Warnings for Unused Assets // (name, catalog) -let unused = assets.subtracting(used) -unused.forEach { print("\(assetCatalogAbsolutePath):: warning: [Asset Unused] \($0)") } - +let unused = availableAssets.filter({ (asset, catalog) -> Bool in !usedAssetNames.contains(asset) }) +unused.forEach { print("\($0):: warning: [Asset Unused] \($0) in \($1)") } +// unused asset found in // Generate Error for broken Assets // (name, [fileReferences]) -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("\($0):: error: [Asset Missing] \($0) found in files \($1)") } +// asset used in file wasn't found! if broken.count > 0 { exit(1) diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index 30f6e08..e9c6023 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 */; }; @@ -15,7 +16,6 @@ 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 607FACEC1AFB9204008FA782 /* Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* Tests.swift */; }; - AFC739991FD86E9E00B5B103 /* Media.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = AFC739981FD86E9E00B5B103 /* Media.xcassets */; }; BA341A14EF45F09231AACBFE /* Pods_AssetChecker_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B2F82D1638FD77B9042FE9A6 /* Pods_AssetChecker_Example.framework */; }; /* End PBXBuildFile section */ @@ -36,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 = ""; }; @@ -48,7 +49,6 @@ 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 607FACEB1AFB9204008FA782 /* Tests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests.swift; sourceTree = ""; }; 63C1D8F311B1C327036F7550 /* AssetChecker.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = AssetChecker.podspec; path = ../AssetChecker.podspec; sourceTree = ""; }; - AFC739981FD86E9E00B5B103 /* Media.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Media.xcassets; sourceTree = ""; }; B2F82D1638FD77B9042FE9A6 /* Pods_AssetChecker_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_AssetChecker_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; C2AB56DA4BC27DE54F97B2E5 /* Pods-AssetChecker_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AssetChecker_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-AssetChecker_Example/Pods-AssetChecker_Example.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -73,6 +73,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 5D7631B2214170A70017A471 /* Resources */ = { + isa = PBXGroup; + children = ( + 5D7631B3214170BA0017A471 /* Media.xcassets */, + ); + path = Resources; + sourceTree = ""; + }; 607FACC71AFB9204008FA782 = { isa = PBXGroup; children = ( @@ -97,7 +105,7 @@ 607FACD21AFB9204008FA782 /* Example for AssetChecker */ = { isa = PBXGroup; children = ( - AFC739971FD86E8E00B5B103 /* Resources */, + 5D7631B2214170A70017A471 /* Resources */, 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 607FACD71AFB9204008FA782 /* ViewController.swift */, 607FACD91AFB9204008FA782 /* Main.storyboard */, @@ -145,14 +153,6 @@ name = "Podspec Metadata"; sourceTree = ""; }; - AFC739971FD86E8E00B5B103 /* Resources */ = { - isa = PBXGroup; - children = ( - AFC739981FD86E9E00B5B103 /* Media.xcassets */, - ); - path = Resources; - sourceTree = ""; - }; D341C51AD021821B48CE184D /* Frameworks */ = { isa = PBXGroup; children = ( @@ -230,6 +230,7 @@ TargetAttributes = { 607FACCF1AFB9204008FA782 = { CreatedOnToolsVersion = 6.3.1; + DevelopmentTeam = JEH7PW263G; LastSwiftMigration = 0900; }; 607FACE41AFB9204008FA782 = { @@ -263,7 +264,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - AFC739991FD86E9E00B5B103 /* Media.xcassets in Resources */, + 5D7631B4214170BA0017A471 /* Media.xcassets in Resources */, 5D8C437E1FC6CB2100DDF7D0 /* run in Resources */, 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, @@ -374,7 +375,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/AssetChecker/run --catalog ${SRCROOT}/Resource/Images.xcassets"; + shellScript = "${PODS_ROOT}/AssetChecker/run --source ${SRCROOT}\n"; }; CD3F94CDB64297E52FBE5D36 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -548,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; @@ -563,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..aeef3c1 100644 --- a/Example/AssetChecker/Base.lproj/LaunchScreen.xib +++ b/Example/AssetChecker/Base.lproj/LaunchScreen.xib @@ -1,8 +1,12 @@ - - + + + + + - - + + + @@ -11,20 +15,20 @@ - - + diff --git a/Example/AssetChecker/Base.lproj/Main.storyboard b/Example/AssetChecker/Base.lproj/Main.storyboard index 1b5cfbe..61e6f8f 100644 --- a/Example/AssetChecker/Base.lproj/Main.storyboard +++ b/Example/AssetChecker/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -24,11 +24,14 @@ - - + + - - + + + + + @@ -46,4 +49,9 @@ + + + + + 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 0000000000000000000000000000000000000000..bb31ab70fd852921f107394f9e10391f936d6521 GIT binary patch literal 2264 zcmc(hYg7{07RPZMv!v7+O)4KVYD>+0G&-r17K4w}T*|1WDM}$evJl4?;*d6$ucETa zyi8$XnzyUhOhwTWB_Tl5e56w#J{iFW5)q1mieu@lnR~z9yH+3e-fN$=etZA-IcKeN zF8>hVy+dn{761U);p^iWv>tDKu&rCx{nSx@^LkKA3G#La)btq5tS6txLHr>AKwTkl z1);H?Z~M_FECm42W_&O;cH+OH0f4OuzMhcdSh)C3WP$O*Zq|U=i64$t%H4C0wFlLL za;;C)md+o&f1Fna-`U{&Y7|?(7Y2-^T3ciEs^V`(`c@{~?*rOmEp4(8)kN~;dvhfz zu}=Ni-;~)x#?T$%Qk$htStpsew%5^BQj(GN&b+fNWmML)cH1#7ww{=y>%P_W?{1)r z&*y6_=P^Y8&}16OW&{vbmExZnMnhxx3PCmYg=(1E;P7#rfoRTW{bTWTny z&42F#2EUDAmdi~D~GVzc(O0;tmd5e@)oAffxI*37@=Yhx;?(5ktmQz#8aUNqkfB(i(GUs zsk|Lsok6>>^zU7M4Y{MI)ADk-SHs$jk&n9C+Q$uXWIhPIttH++g_j;-W8G92jjfuvRD8@mKYz;I5`@gPnhoj? zIG(NXK8`Z&e>Sf0@GeR4T5>cZf@7A(6V z7@IY$%H*_ZtKu~~dIas$agS@LN#DWc`*1o&)K_3BDRTUPb|`JiQ5nXT|Ncf!F6`w9 zjTRhlKH^Ed%lmVSa;Lw{(xRwO2EcNrx=L8KIeJ1z9C;-~hl%6TzIwph(x<&*X#r)B z`H9u=U+mLn%SK4WoOFd+y>3h=Mk=ZF{#0;-+ z%yhb&TsM@z&{&xf@U21&t}m*Ga?7tfdrk8&4jDZ~JNKtfMT?b< zeL)u%Lz;6-?}EJ+W0@*%B-i8%m=WiOLrsCLHp*vr=yL;2rw3Q} zN8!~CgIGK3nAtn_{eFeRf0lPE$qSWq+T%sD8O5d6G$!IxW6{R&MLLwF18QNdnlq&N zmMTYSqxq%IUMdS7%%UtpKZ*})Y?^z5b912_f&(9qP^avaL;Ac?+SL!!c38Knn5646 zWa8<{H!Keq0yLSoOxVCGvaUb{_oFn$b@vh^r$RpLN_24>`px#rrGpxxzb`78vDR$p zc^_PL++XyepR4|?6Al$bw0$--N~_-Jnc2*_EI8~+d%5=ge;`?gZf+P|KdKYApI?$l z<_&KGRSWs3OXcUl1u`a>q?xp?=IBQ?#>$5Sq@uaCuZa<;r>bt#JzzhNQb8BN1RyQ!}Led5Ee{m;DEa6c_MSR6u8_xF}&D8vKw`462-!9 zMP2h!m^(!hs+5BekUYub_K19v(1f}>))<3UjiF4Bh=d8y;md@(kPVu%i$az?l(-ET zpwloI`DfI*?I_|gpA>6gd;Wa1+AVK;6k02h(6gOycK z<5a{~h78)kOjL6UFmd%PdVVFX6paf(;lVdHq7XSzUTgQK!lvI6;>1s;*b3=DjSL74G){)!X^1}0Tc7srqc=eJV~{ck4-v>At_zPz>culS1k z#nP`XE!A$?5b7E$z}%C&WRc#d?QD_SzwI2^W`8Y{C0O-YKn{tBL;_`&c|?t=pwDcT_={M~*ha;sABT_~I^a(bH7COuj9?awBZx&E0D z5zqhcV_<@g?k@kIidpr-o|DczjCYACdM~%+^ok<$A6o^R<AqG9tpm3Nx8M6E2?UVfPbKOzsMuatd|p=Y+QA1gF2S4 zSnAI8Q3Rye_4eVfe~!*ItX)-ui0Yg;3q&z*NNG>FVdQ&MBb@064ic`v3p{ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..889a5ddf2add2fd29d151bf94671bfe7d2c4ece3 GIT binary patch literal 1382 zcmeAS@N?(olHy`uVBq!ia0vp^2_VeD1|%QND7OGojKx9jP7LeL$-D$|SkfJR9T^xl z_H+M9WCij$3p^r=85sBugD~Uq{1qucHNQPw978JN-cCQ;9i1w2EV*z!>%sS}(&b7W z+U8x_`(LrI$SzHM&EC{>E}%eJFDAoXaJ6ahhj@W4dVeJT9FbDa$U$+IP^6hhMz zZ_f-dY+lRr1ecS(9qdPP3U5`PAjoF3$LLkAi+7t^fY}Zd_b?{8Ciz%2NG(*Cbl2Zta>p z?{$4~E8EPJyGK=a%g5)xak2HyU-109e9pw(lj5#lOyhU+8{H{r}y_cYzM`mT~{uFwJbf zePz*4ub%05WOuz?bX+fWPEq~5OWgmqEpU&$WECEpb+>uy<0E%fOBTMrd}#5V$p07S zmTsA6^4;)w$dbE!7kLXCt<5HV30gk;rDjZC%!T{!zVF=K5?eE=^V|Nme>QEF+DpL_xzZQKr^A02 z6>nJK$**_i%OW+~9RZiSXMK6#*jpttwc!OdOe_rjwsFLSG~6P*r_ww z?s~IlYVW_i`^T9(fA}r#w3paTiTExht-rVQ%f*{JKh3h)aqBG)-v8yE zT>LaXonQW5_eR|pGcxQ%ex1|!E@l1Y^Pk?xUuU}8pXDDt@qfa(cA?jC$;bbfoPPb~ zzE$iAZT&rUPq<{>Ia|qlt*M3;K!$6lc>QOJUuoR4?9>G}VA;sv>FVdQ&MBb@01HK^ A0{{R3 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..2a3fffa4e856ad2634dbafdb7069b048ca386c1c GIT binary patch literal 1522 zcmY!laBGd0A!PezgFZZVhM1TG1Kf*wn!E2n(KjJti_ z=|BDHt~z_gCPSGnEmixgdY4|Yq>P&$@>DtE@} z?OJ;_hTOBLS3EWU{=>e-Pr6q|sUqNSKL&uc`DKph3 zJ(7`iNo5YKZdk#d^)+Ivyuia_y8^4T*gC2twzX8<4R1K0yX5Xgs~7LWZcq9A=DEbu z*HLORi|0Pt@6J$^ZF_k3e!lm08kZE59)R zvkLEQa^?t8by@oEm}rR;Ll9@CDMx0>o)esnLM;xaPEmzUBAd)k*L4XvezZ|E;@kRm zj(|z?%77LDLAGtyin$t!xe|&s91mv;lsWyFa;km8%a$|FC#>SMIU>y!wK)>iT@dQsbDh$ z5{pvvN`Ts6`NlUjB{R_}zd|7zC}f~uVgW+2V71Qqc_l!_#R{N|2gzmbMfs%#NX~(n z4i<&EFc@fOQDR;xLP=36*bN|WI0IF=q?TnSrv|w@fs{aep&yi5oL^d$ zoLZ~^3X32lKY?5f%lrsG6@wk>otaV$bU(=3L2$R1g1m`jfEkhj&cH&UB)>>OBRMg% zC||))&(K)UP(dT3q@=(~9~i7a!StfUf{e`MV!iyLbWKgLH33EWDW%E4LWX#4h&VAv zvL51o=fsl4ocwf%(ZKjAhQ`_RzH!hlOv)z#mP3jnTD1AYJi literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..39025b2436b071ae540add8004dde24824978145 GIT binary patch literal 1520 zcmY!laBGd0A!NLG>OPBE9>1WqA^2NHHpuR`DGym6iL zJACQaMo+Vq(?3vJ@QeZ-L_%*JRR2# zRfhFn*A@SLlw`SC;DzLE_kSBR_b>8%d;b5$Q0MOKr%{3|^KR@2yS*TA-3#B-44LIt zezQ9dGFRA7T6ZgWW97#yHxgX;@3A>G^LS{;;b}WxN^e}Waf!T%{-I57Zk=t{qqN#$ zc&7$jNO>_Y+{mS7S>H1Kam) zA!Zkkt$w0^=I3YYO@&oIoLgLP|J*RK*QWe7%S?_9nF5US_t^U|_p!coS)=sY_14lO z^L`&bUy=WJR#x#6<@e6KLR06u=vLp4@(cSIWyRJxS8ZE>=bNCUr*m8KSEziuHX+68 z(!}qEY@9;sj=7T;I8R+9w$(Xld8e+4cdu_#-=TdA?|$3q`9SDXRsr9c*p+>&7?wx9 z-EgJE)VS}W|Hu3L8E)ME_3~lutHe5nukC!lgWCQd{j2}(|HFU&U48%Ef7HvQ_dotG z`EUO5|F2Uty`*Bc|G)n2|BZjs-`1b>=QR4~E)W+sC7t7tF$-gWV~N40iHf_N{~3!|s-l1pMsKAn6dz>>ht^rGpCP?JQe@#!irfz*$C6nfrO+)~c{|w!+rk3PmgvqXjq}pKQ@`pCH)OqkO_9vY6W_-|?B4i<7|O zoMUg#b1Kzyf0|UsTt71@nkzLg1)2J3A^ED(ELC0!d&J2TA}*AcC4>3O7eVKcuoCRY5->F+CM* zWjI^|a=L<5Bk6ih5YC>E^NIX|xisJK`GlnD=q-plw1iD_Eab-Dh0a%R1_EL~Hkqj_HGQb&FB$VVADQF}o zW)|fu80r}s>lrF&WR#Q?Sm^_U6)2cqlvt3FnOv-wUzDz?3AQGnC_kk%8Cb*+uMH6= z=1A5<-0z%Nl9-d94lx=SKgG~kLFlO{N=@T3P_W=KfCB|HQ&VG8g){}In4y6&FfDc@l%HRs0P;8_@p$H?_P9nOktFs=E5SaRC6c_8DCO literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..a5bbd0e8f28f7981fd097f76ae3a5fce61be355d GIT binary patch literal 1969 zcmb_dXH=7E7XCu7Qd~NOBI*pTfl+!1Q4=}>5{eKhDltfjKm-I5I)n6pvWuXABQ5NP z6-G)30i_5-2_OOK9HNFI-4KxwHqP#k*+2VZ&)#$Hd(VBId*1h+`@HAehpty`r6p7) z005A-w}W5f$Aa%JCc-~iHp{L2AQW}k9x2AR1hLzh{9OE=-SsE{5MBN5pht0*Fg~b) zLwMudv1nXOU}O*w6B7do4iAaC6?iWQf{naWx@oD(uZGyeFCk;gHs%vU=k9B~-I+R^ zZT}_xzJT-?!sIWF$&}EgxT$&cyi6+PWx7h@XBlSsDd#SO=XH*DUEfZnp)r~0K3Uw8 zzbdE7of7PWwf|_mG0&L{jR1Xa&Pg-)`b&MZy>DF1VoPEuO#6KMHyWK2zj#OkCpMSU ztQWCc)9W!Y9U3tBA8evbcTEr@MBA!Cyf`P8GOW|+=3%X;qXC!$KO{xSsdzC=0P3HF zUhkkFF%Iw)Oul8@!xOcxKm2XS604bn6=)47`V-vX1m8L&p)IgGBhkW?>TP!$R&_1- zDG4gEbmD!dD2YTDTp9crdEBUZgQ}}nQFPYD&&0yw&|Pj?va5dYE6k(3*0UaekU(f0 zX;SmNDWD1l^Wb`q&83!%-}-h41b7*jTCK;HXB1>6W%VxKL43FlXi5eTiUa zScERl5gP!Djs}(CXZEP^lMVjIG0ZYVNNbWr`kUS2;^GYwMYyX5NRWp~LXLZK>(^RY z5301pGDwWIMSd^7QJn-F4j1&w$0}QdN^@sOx1q=tq~$|Gc_vpGM)6~UxZLq7$HSJe zS!4H_5-)}IKX97dNb%CYgsKLcP~^gl#&cGm|61{O{` z7OTq>v1T*q*y!kJR<&~}#Qe!Ilqib)vtmnDzdn#KbHNs#fn-|%&w4&)(`^h->*$l# zZsc*y9uM4ZCi}L8zuAF~2^*T4zNg?;#|24o*hg>2dU(^5ll$pLA(KOtIo-nbG$N6h zx4*y7Bz+4r0DD1AjEszy;)+JG)XK_AwTmS~KLH`#3l_;pcr)(){iAM%NaccHZf@?4 zqN_DBsXJ*1B)fuS-v0E8G$t}~*G+CZmi-uM8{$%C`f`@BJu0B5nC;tuGH~xlvY}p0 zSH$f+tmMqZ{0wY^23J;A9F$V8i1%}jtN`fAK0ibKI?s9wl+=mST5@e?bMTlz5@nzl z#R47B5-+MU>3Z_caHsq)`noys75b@D(A<(aoEf`Arqb=e4o8VI5Af z2B8kpg6}>Wmgdtq%2!vpmiY-cKc0Ie;SA={=X1K?>nZmUW{YfnOA?s z2P^p$^Z&1F$F_NOKDcipn=bI!;W_K)^PUa4*wt3?DQm+d9Bb`?wLf%E?+oEr&DXs< z@Dcx6W z6pzab`+jF!*NgQhsm)<>A*DgzD_!p%{5CU1uEmEn4dpw83Q)Mw-mf4>iDVWNYOVTG zyf%^-Ivg_C@YHdRTibJ{V)d5(1T`AHjamL=dm0MTz*DNrmv`HKa_Zc6`VJX`g4=Q) z@}Q6Cx#wG}6Fzx3aVuneemon_e~fKsxXr5(Sa^V{isa=K)u=9;c6zeobj_ndZg}0z zI;p!(|EDScTd=kLaddXWBBzV0pUq>^!G04vjF3FZ;kZ1i=zoeDoN3~FoiRP_8im44 zMt!jQ?@B*}YBADzvUel4I@}t}Aci|;HelD|Tb&2}37Sd)I$3;Zj}MK`UfV3$VoFdI zdexPl9H^g)H>MqrR!){2V`OA4o4X2$2CUYCv?wW~iY;j_siVq66!|jhQzt{AK<4n{ z-)&L!2yJnesaGv%!06jakGUJ&<9kZ8Db{Vzn3v^8BFL@{$wH0o89{ml_j-5WF7*@<{9VIEhzoTf#J7ulabvh)6(7*SZW zDxjTTWc$2&o`nUxC6=6FjkMY(n>T;G O0``cjaPsAVlz#$ua*y=@ literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..5e83c9fa45d3ba5a9efa54e229057864880477cc GIT binary patch literal 672 zcmV;R0$=@!P)p zhP zOU%3jV8mg`Zz9SjlgYfQJ(UTRN~H`Dy#-*nENwCKaw?VDSFx`Wfoion)@U@o0EkOo zav_A6v2FW%EEdZG$O5z`SkSPdhDe6^KToD*)z{jpUt2otmx!ZUH_p)2Zor51c-H!0Kn|p2-g% zZ=6-;1paHZK+tG`pwR+BGq?ib)B;AWcNpEyBWzxOq3pFUOVt^4_ot^l)&0oo`xDRtK_e}&x@on4UgvKzb-%KtZsOI~ z)+e<7{XY;oaI5>31;~F3T>>~GqI`Jh&r6BAUs)Q>!1EU!a@T#Xi1~m300004nJ zu)2XT9gP202Q%Rdb&7< zRLprh*FQt1ROI;n{B4;oceu>9@;Y*xK3du|doJg}Fo8WQB68O*5x*Gpc9a*dZp{5XOq&>=d%l^%$`+}Ubp%E z_TBG`=iIN(zjsc3^3S*b>fSJQEz$s^5^0sY4#pzgM{^nER2Q`~E6kWSQ`_ZB z@AJy|r$sy0cXm9r+FLkB{J@!chH3hOdwCLG^Dg^)=uCTBT3R0vxMhBuF0!0;ir3QJ zi#4p+5~|M#J)c>r!_hS9?7o~wYZ?rA;q3Rn^19c3TY{Np%sgaZ!~URg zieKQYdGq+z)fz}Azhl|ZJ+oZkB16NzqYFyU+GpDP zewI07Bgb+uk-^~D7r~2;X>!aHB2`Nn4+Nh7%*V&e`!{d({18?jU*FtMHu>TWa~a+- zY)Je#C!FEWfiuq+X;_&W8GT}BkVrnqd}8TM;f9wrcJ>)PJLh+tW7x6wke z;A^HHLu_n3T;_kk^zV1Un%NNy8*YC8>oq4Ox_)i`0&9IchA#pa zCl^i@=GK^gnwepqZ`AU+iO<|0JyR14SbepOIb!k_zuCqT=7t*^RK8hv{M*s#Yu}>X zpPS!3RZa7k{r_z%vv2tZyQv2<9$LYW7}8*%%mnsRf6yVC3nuIX61q z+_1Qox0j!x;OlauRrB}suPEB6`ziO*C)JZa_uod?|1{i_^H^_M)z|zO%{Q)Jo~JJV z9Dewx>d7~sTT}Jr81}reN|`TRZg<~ISNy_k?l1}IG9Y_xJxB2&U(riHr)BPJ`1$^@ z=&_#9KeqwdJ0Ht0vI~zb+hZ5*`t=9@gI|rOc#7!!0?&c)_mWZRUe`bpSqboIe%@%y0pjEOZE3ML&G5Gwyf=rRPR+E z3k%mGL}3BuW5qOMcJ=OiIW;jdZ?r`j*UQ*l`{BB*C2}@U{YL%QOvlvKtM{&`Tkuff z^7?I$tk>#izi)}J{l55aLY;JJ&c3pqgQx5Am)`v0_d1)iTi*49pnzH2^B=BJy9-n* zYnFfgs-JnjN$W$#@gx8K)-M&27f=7SNuwc-p@8#1D1&|T2eq{?ca|?Q_-1dv~ i{ZdGt*YMi@iT{fq3ttcW{;$B2iow&>&t;ucLK6UB{xN|7 literal 0 HcmV?d00001 From f650c0d67c1cf4daa03a236d87d33f5225f1a799 Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Thu, 6 Sep 2018 17:28:04 +0200 Subject: [PATCH 3/8] Update file references. --- Classes/main.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Classes/main.swift b/Classes/main.swift index abfca5a..b6d1d0d 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -113,7 +113,7 @@ let usedAssetNames = Set(usedAssets.keys + ignoredUnusedNames) // Generate Warnings for Unused Assets // (name, catalog) let unused = availableAssets.filter({ (asset, catalog) -> Bool in !usedAssetNames.contains(asset) }) -unused.forEach { print("\($0):: warning: [Asset Unused] \($0) in \($1)") } +unused.forEach { print("\($1):: warning: [Asset Unused] \($0)") } // unused asset found in // Generate Error for broken Assets @@ -122,7 +122,7 @@ let broken = usedAssets.filter { (assetName, references) -> Bool in !availableAssetNames.contains(assetName) } -broken.forEach { print("\($0):: error: [Asset Missing] \($0) found in files \($1)") } +broken.forEach { print("\($1.first ?? $0):: error: [Asset Missing] \($0)") } // asset used in file wasn't found! if broken.count > 0 { From e2b6d76ffadaf9bd50c8d46212177bff92bf6e48 Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Fri, 7 Sep 2018 08:49:15 +0200 Subject: [PATCH 4/8] Add more tests for assets within codebase, refactor script code and clean up. --- Classes/main.swift | 103 ++++++++++-------- .../AssetChecker.xcodeproj/project.pbxproj | 4 +- .../AssetChecker/Base.lproj/LaunchScreen.xib | 30 ++++- .../AssetChecker/Base.lproj/Main.storyboard | 23 ++-- .../icon_orange.imageset/Contents.json | 23 ++++ .../icon_orange.imageset/icons8-144.png | Bin 0 -> 1963 bytes .../icon_orange.imageset/icons8-49.png | Bin 0 -> 664 bytes .../icon_orange.imageset/icons8-96.png | Bin 0 -> 1287 bytes Example/AssetChecker/ViewController.swift | 8 +- 9 files changed, 126 insertions(+), 65 deletions(-) create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/Contents.json create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-144.png create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-49.png create mode 100644 Example/AssetChecker/Resources/Media.xcassets/icon_orange.imageset/icons8-96.png diff --git a/Classes/main.swift b/Classes/main.swift index 767d37f..44096d4 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -6,12 +6,28 @@ import Foundation var sourcePathOption:String? = nil let ignoredUnusedNames = [String]() -for (index, arg) in CommandLine.arguments.enumerated() { - switch index { - case 1: - sourcePathOption = arg - default: - break +/* Put here the asset generating false positives, + For instance whne you build asset names at runtime +let ignoredUnusedNames = [ + "IconArticle", + "IconMedia", + "voteEN", + "voteES", + "voteFR" +] +*/ + +// MARK : - End Of Configurable Section + +/// Attempt to fetch source path from run script arguments +if sourcePathOption == nil { + for (index, arg) in CommandLine.arguments.enumerated() { + switch index { + case 1: + sourcePathOption = arg + default: + break + } } } @@ -20,7 +36,7 @@ guard let sourcePath = sourcePathOption else { exit(0) } -func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [String] { +private func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [String] { var elements = [String]() while let e = enumerator?.nextObject() as? String { elements.append(e) @@ -28,14 +44,13 @@ func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator?) -> [St return elements } -// MARK: - Search for asset catalogs +/// Search for asset catalogs within the source path let assetCatalogPaths = elementsInEnumerator(FileManager.default.enumerator(atPath: sourcePath)).filter { $0.hasSuffix(".xcassets") } print("Searching sources in \(sourcePath) for assets in \(assetCatalogPaths)") -// MARK: - List Assets - -func listAssets() -> [(asset: String, catalog: String)] { +/// List assets in found asset catalogs +private func listAssets() -> [(asset: String, catalog: String)] { return assetCatalogPaths.flatMap { (catalog) -> [(asset: String, catalog: String)] in @@ -49,39 +64,39 @@ func listAssets() -> [(asset: String, catalog: String)] { } } - -// 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 - "\\ [String: [String]] { +/// List Assets used in the codebase +private func listUsedAssetLiterals() -> [String: [String]] { let enumerator = FileManager.default.enumerator(atPath:sourcePath) 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") } // Only Swift and Obj-C files + .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) }) unused.forEach { print("\($1):: warning: [Asset Unused] \($0)") } -// unused asset found in // Generate Error for broken Assets -// (name, [fileReferences]) -let broken = usedAssets.filter { (assetName, references) -> Bool in - !availableAssetNames.contains(assetName) -} - +let broken = usedAssets.filter { (assetName, references) -> Bool in !availableAssetNames.contains(assetName) } broken.forEach { print("\($1.first ?? $0):: error: [Asset Missing] \($0)") } -// asset used in file wasn't found! if broken.count > 0 { exit(1) diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index e9c6023..3ce2536 100644 --- a/Example/AssetChecker.xcodeproj/project.pbxproj +++ b/Example/AssetChecker.xcodeproj/project.pbxproj @@ -105,12 +105,12 @@ 607FACD21AFB9204008FA782 /* Example for AssetChecker */ = { isa = PBXGroup; children = ( - 5D7631B2214170A70017A471 /* Resources */, 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 607FACD71AFB9204008FA782 /* ViewController.swift */, 607FACD91AFB9204008FA782 /* Main.storyboard */, 607FACDC1AFB9204008FA782 /* Images.xcassets */, 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, + 5D7631B2214170A70017A471 /* Resources */, 607FACD31AFB9204008FA782 /* Supporting Files */, ); name = "Example for AssetChecker"; @@ -375,7 +375,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/AssetChecker/run --source ${SRCROOT}\n"; + shellScript = "${PODS_ROOT}/AssetChecker/run"; }; CD3F94CDB64297E52FBE5D36 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; diff --git a/Example/AssetChecker/Base.lproj/LaunchScreen.xib b/Example/AssetChecker/Base.lproj/LaunchScreen.xib index aeef3c1..5c995be 100644 --- a/Example/AssetChecker/Base.lproj/LaunchScreen.xib +++ b/Example/AssetChecker/Base.lproj/LaunchScreen.xib @@ -1,11 +1,12 @@ - + - + + @@ -15,31 +16,50 @@ - + + + + + + + + diff --git a/Example/AssetChecker/Base.lproj/Main.storyboard b/Example/AssetChecker/Base.lproj/Main.storyboard index 61e6f8f..8e75148 100644 --- a/Example/AssetChecker/Base.lproj/Main.storyboard +++ b/Example/AssetChecker/Base.lproj/Main.storyboard @@ -1,11 +1,11 @@ - + - + @@ -21,17 +21,17 @@ - + - - + + - - + + - - + + @@ -44,6 +44,11 @@ + + + + + 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 0000000000000000000000000000000000000000..c1b9360588c806e215acb50c6660e000c5a4ecba GIT binary patch literal 1963 zcmbtVX;70{7X1i~5Eer~5ouW@-AE%wKxrVD01_Z9qG-a>Adx++2?@;_OhjdMSh~7J z7TG}pNVFMNfru!}Ad8?NAuJ8bE@2A^d&ioZs-F2VKW0wVeYfsAr*7SM&ySaM&E85z zQb`g302ylx`noWue+`Kv!gJ1Yx`CAzWL_GFI zAw}LN^@`g#lf5s|JQ>d>!i%=Yjv(~>;a%Ba@-G5i)AV*n?&FAu4`LW0YJ5&^{ zb&_)-lgK3_>4|?qxsf$Td_dxr&f7#x8YLtpF$nv?)}ln-^b8=TqoM9qv10 zx$wlUIiO06^!>!)X(mVj>AP9N`iX%(dP4I4;SOT{C;iXNXFAeOA_8%`)HLehMlPj# zU~J5FXY~vg_m9u(_RvOKTz$dT*lQ8PiVAUZBPS&ksWnk0f)u~y#OYi+E!yZ7GQ)w~ z^Jq|cDGx+HsIj`M))2eD!=wG!=kj~Jl<=+Yql1ILo8#(1f@AcnAVOPXeuFMfSt_nz zId_TaSV_}5Ci!Ic_Wr=Yz+ymcw46+2?30Rb*SDz-xbLOgTh!Op=yoj~qLEZGdA;?^ zSbaWY=&gl5+O$IHo`=8(+9>)`;r;CyCoHcXI66yioJ>rN_YeSdZj`aJC^Ra-)3b=U zz5Y1?{V6A}?&PDatj4IJvMV!rvozCXk>6}egVonzz>+ovU4qAPkE~wmyGi}A=m+Tb z-HO}Y8z|UlP`GPE1mC}Z_w|W^444($vC4TZ7S{snn<0480*ORcVP$1t$DGDd5 zkA{bbg`L8E;Kr>9)<+o(#(t|o(q6|Bmy6Hp^~H;kg5SqHNl)$?&dbYV3D|0bF$MBx z?6rHm_0Hc}Lt9{Z^or;gk6|(dPtR#6bhJTWQ&Ak4rfgg{G_u+S()!J2tPihq<29CN zkFSA&F*s4E5#X-V>9ZtUUV!Rd7+6!f{U(4?e&c@eJj1aPzp88a3myGwOLMF6K4Vcs zmC_=9EdCE1;ekmdMjxt+9Au?yuG|R$F=Lh5v{cQDB@+3lzZJH34m6Eluv2kk@Ukrq zeqL5#_5@FoXcx@8U(vhniDZC9)#Z<%HKY^m6lJq?)bu<|z5gfTyC`CvoOgyx%vrq= zs?^|fC7?i_>9r*odW1aCp7I?+ns_Ef2RFNsjMzk~f|(z`IX{93@!H`}((NB-^EY}? zv>yifY{5ex0&AgBjI|BOx{;OH#62-t?;!&djU=#U&5E&vWB9oy`24b^?|A(U0~2^R zc)r3lzx3J;$OSx5%2z81(M2R@uEa5Vw12F&h zcK^+&#jQc#6xWqz+C-)WYxSeKEuuBLcdgK+KZE}t=EyP@&X_Cp=?>CY#t*%%Y;#te-t_uSJgttSzCTYyF2|%o z{0%!B)nzab7a-VBR~g$$MPdYN6WN(?F?Yr-!(9~Y?I+WNRg1}+Uvgb-QC-siCj-Tc zo)h#B@Dja_I819|TDvtc$ z{Vk5;#h!s)y3!w>HslP?=8cYw7##n6N_CKwivBD7S80bx8}=!lU8(S61*|RX(bbo4 G#r+BMLz#jA literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..c69d37c2860c0a14f2bf94630a4677e94529dff8 GIT binary patch literal 664 zcmV;J0%!e+P)*r9ZBId!ZVoC>a4S_p)*$q*>COQA)F(nYzm=Ea{&oa*J?#m8Mk z-e(HG-@EVoeR#R#NC1syxFOYnwOZ|#X_{psS^#jTTmHe!`<+f_vrs7fRJos00oQe( zG4mFH8v#p>iKvvzWT8cEPV@%K7I?sx(maa#@M3`<$YQD ze+!Xu^=nmqN&3F>@23^8cLeqhar${xd0!U&Fqbr1AZfHf(rAIC8F+z66wB*eEU*9b z1s7D0BWvJKAQffty_uOv^*FKy{sgo@(nt%ey?44luk$aNdR$pjH}Tp#=K$xI{|^{@ yVfDDO0Qpa$GXTGds4|*5eJxRsD@&spdM*IBB-S2~iy*`R00004nJ zu)2XT9gP202Q&cc)B=- zRLprh*Ed5ZRpR*n^Er>b)_P?hof67z`pA|4K7%gNx@44^_83) zvvhW5sBYVS!(0CnSKE(^%?eAz4S)NU2MR=|1hdTU$e8V>ee&Iz#|K4jE>pWZv;5o4 zyR~)4XMV4Iw*T|#l(g@)H(5no1HmZM+{4PjR)nkdEyFyeMaLORn0BOQUY9XGFK|)w zc;UWR@812J)e)ih^6TU(#xrjulH-&XU1z*gKQ}df#?K31zI?fH;DEz~lzlsvL^*7X z*kjJow3tDz=f)!wWB#gn;ag^&R$D3h;P*e_Gc`;HcFkBT*72gX#;*R3o8IjuK@JN8 zZaBKAGv3KqnL2gm{Es&8y}kdgnP|m$Y(_Oh4fg}H(`%nI=9I^NuRdLyJW-4BjL*LJ z{!A4b3!OzyKiyRRwfCk(v3WyX+C5JfO9tubWq;Woq%%BDl<52Z?c2Fqd{eX-_q~6g z_^f}j4V&@%&o=K37$lO-gf1Ew^D~`T6BNa0@XX>|fxrLx%IHcVQ3F%ct1m75>>FYk z-Z2&YzGPU>{z2B*pUtsg(fal8!x;>Y&0)JRH${!%X^q|e)SjDeF2W3ES0e4tpE+MW z&pc7tM~TU6X^{;$>T;H}P2 zwe|cB&SuqSjN8+UW-=E_MeJniF`Uitr{lk!Ot!ifgW2wN*1}6~T#J8gopF9LKZC5x zmz*n)Zh+jxcc8oQw~6tYKPgvk-CzP5n#Yj+=v!vdu0>+U!~~Alac|VjO1x|S@aINe zU&G5PN6$9bUn%vw{c@t=)D;YgAq@sv4icgYJzN5AtQ?D(S^^ml;SlaP^Yqd#>xp-* zdVHRnF*q3f<`v`4SKecCSLCZ%=8q{Vcc&b__0Kb)toCeK+>VCR^X+*4Wj}2_e(CG# ziN7>Y=G;G+sxQZ&v0Luu+QTc?Z}&aSno?vPcH?`-tJk^81;Ty1S2*pur^hQUFvouD zp?xQ>T)(~SA(I$4`vGq8>ABBayCZbg?7I-L>z)}<$+^ERryut(sCs8y@YKH`Rqkr> z{KxM*j|;|WE#G&~Y?skFqm}hMp;@^7g3XJbZ=!|gOINK&t6Gf@Ww7GUm(JMg4YN)?zvP@#0Cx zVy>~ik(NDc<+x*in)2ny=*Q8z)3=r%%CGxyG0$LsyTI)A_l`eE-M>BP{g1HIrL5iZ zt{(&i-t9Q{E&fH@sfj;;%CGNN`72_=++NoA=5Dp(!jFNTzIxqEFGLwOGl((%VK}sV x%d*Ff6Bi!*|5Nd7)$#37deHnIcx!4sgVGyG{gt{C9{>w222WQ%mvv4FO#q#bLAn3{ literal 0 HcmV?d00001 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. } - } From a7266a68c4cc733ccebef735b8c50b212d0f7dfc Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Fri, 7 Sep 2018 08:59:29 +0200 Subject: [PATCH 5/8] Update readme --- README.md | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) 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/ From c72dae7f7152da094d3b823355cee975655190f3 Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Fri, 7 Sep 2018 10:51:53 +0200 Subject: [PATCH 6/8] Bump version number --- AssetChecker.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssetChecker.podspec b/AssetChecker.podspec index ec7af47..69df738 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.0' 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' From 48f6326235e926cb4b0812fa21b8c652df35a7eb Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Wed, 20 Mar 2019 12:50:53 +0100 Subject: [PATCH 7/8] Improve command line argument handling, added back ignore option. --- Classes/main.swift | 51 +++++++++++++++---- .../AssetChecker.xcodeproj/project.pbxproj | 2 +- run | 7 ++- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/Classes/main.swift b/Classes/main.swift index 44096d4..6f9f424 100755 --- a/Classes/main.swift +++ b/Classes/main.swift @@ -3,8 +3,9 @@ import Foundation // Configure me \o/ -var sourcePathOption:String? = nil -let ignoredUnusedNames = [String]() +var sourcePathOption:String? +var ignoredUnusedNames = [String]() +var catalogPath: String? /* Put here the asset generating false positives, For instance whne you build asset names at runtime @@ -20,14 +21,34 @@ let ignoredUnusedNames = [ // MARK : - End Of Configurable Section /// Attempt to fetch source path from run script arguments -if sourcePathOption == nil { - for (index, arg) in CommandLine.arguments.enumerated() { - switch index { - case 1: - sourcePathOption = arg - default: - break +// 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 } } @@ -45,7 +66,15 @@ private func elementsInEnumerator(_ enumerator: FileManager.DirectoryEnumerator? } /// Search for asset catalogs within the source path -let assetCatalogPaths = elementsInEnumerator(FileManager.default.enumerator(atPath: sourcePath)).filter { $0.hasSuffix(".xcassets") } + +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)") @@ -127,7 +156,7 @@ let usedAssets = listUsedAssetLiterals() let usedAssetNames = Set(usedAssets.keys + ignoredUnusedNames) // Generate Warnings for Unused Assets -let unused = availableAssets.filter({ (asset, catalog) -> Bool in !usedAssetNames.contains(asset) }) +let unused = availableAssets.filter({ (asset, catalog) -> Bool in !usedAssetNames.contains(asset) && !ignoredUnusedNames.contains(asset) }) unused.forEach { print("\($1):: warning: [Asset Unused] \($0)") } // Generate Error for broken Assets diff --git a/Example/AssetChecker.xcodeproj/project.pbxproj b/Example/AssetChecker.xcodeproj/project.pbxproj index 3ce2536..53ba587 100644 --- a/Example/AssetChecker.xcodeproj/project.pbxproj +++ b/Example/AssetChecker.xcodeproj/project.pbxproj @@ -375,7 +375,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "${PODS_ROOT}/AssetChecker/run"; + shellScript = "${PODS_ROOT}/AssetChecker/run --ignore \"icon_red\"\n"; }; CD3F94CDB64297E52FBE5D36 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; 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 From 705eed80aa8e19707a9d5a31fc0028f77ae409c2 Mon Sep 17 00:00:00 2001 From: Joseph Elliott Date: Wed, 20 Mar 2019 13:03:06 +0100 Subject: [PATCH 8/8] Update version --- AssetChecker.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssetChecker.podspec b/AssetChecker.podspec index 69df738..e418cf0 100644 --- a/AssetChecker.podspec +++ b/AssetChecker.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'AssetChecker' - s.version = '0.3.0' + 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'