Skip to content

Commit

Permalink
Merge pull request mac-cain13#108 from mac-cain13/develop
Browse files Browse the repository at this point in the history
Release 0.11
  • Loading branch information
mac-cain13 committed Nov 1, 2015
2 parents 708c3dd + 6797c8a commit 4235beb
Show file tree
Hide file tree
Showing 18 changed files with 221 additions and 55 deletions.
5 changes: 3 additions & 2 deletions R.swift.podspec
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Pod::Spec.new do |s|

s.name = "R.swift"
s.version = "0.10.0"
s.version = "0.11.0"
s.summary = "Get strong typed, autocompleted resources like images, fonts and segues in Swift projects"

s.description = <<-DESC
Expand All @@ -20,9 +20,10 @@ Pod::Spec.new do |s|
s.social_media_url = "https://twitter.com/mac_cain13"

s.ios.deployment_target = '7.0'
s.tvos.deployment_target = '9.0'
s.watchos.deployment_target = '1.0'

s.source = { :http => "https://github.com/mac-cain13/R.swift/releases/download/v0.10.0/rswift-0.10.0.zip" }
s.source = { :http => "https://github.com/mac-cain13/R.swift/releases/download/v0.11.0/rswift-0.11.0.zip" }

s.preserve_paths = "rswift"

Expand Down
46 changes: 41 additions & 5 deletions R.swift.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,37 @@

/* Begin PBXBuildFile section */
D5032B691A7C1542007D1107 /* types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5032B681A7C1542007D1107 /* types.swift */; };
D579466B1B9347C20044D2FC /* XCProjectFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D579466A1B9347C20044D2FC /* XCProjectFile.swift */; settings = {ASSET_TAGS = (); }; };
D5646DE41BE2016E0034F4D7 /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE11BE2016E0034F4D7 /* Extensions.swift */; };
D5646DE51BE2016E0034F4D7 /* PBXObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE21BE2016E0034F4D7 /* PBXObject.swift */; };
D5646DE61BE2016E0034F4D7 /* Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE31BE2016E0034F4D7 /* Serialization.swift */; };
D579466B1B9347C20044D2FC /* XCProjectFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D579466A1B9347C20044D2FC /* XCProjectFile.swift */; };
D586D1C61BE20D8600F18FEC /* Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE11BE2016E0034F4D7 /* Extensions.swift */; };
D586D1C71BE20D8600F18FEC /* PBXObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE21BE2016E0034F4D7 /* PBXObject.swift */; };
D586D1C81BE20D8600F18FEC /* Serialization.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5646DE31BE2016E0034F4D7 /* Serialization.swift */; };
D5B53A171A7D2A9B00F64418 /* values.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B53A161A7D2A9B00F64418 /* values.swift */; };
D5C5A8EF1BB7196000163E71 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C5A8EE1BB7196000163E71 /* Core.swift */; settings = {ASSET_TAGS = (); }; };
D5CD9B3C1B98258700C85F8C /* input.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CD9B3B1B98258700C85F8C /* input.swift */; settings = {ASSET_TAGS = (); }; };
D5DA249D1B9CB1BF00AAA43E /* processing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DA249C1B9CB1BF00AAA43E /* processing.swift */; settings = {ASSET_TAGS = (); }; };
D5C5A8EF1BB7196000163E71 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C5A8EE1BB7196000163E71 /* Core.swift */; };
D5CD9B3C1B98258700C85F8C /* input.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CD9B3B1B98258700C85F8C /* input.swift */; };
D5DA249D1B9CB1BF00AAA43E /* processing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DA249C1B9CB1BF00AAA43E /* processing.swift */; };
D5EA0DF81A3DF45600FFEBC4 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0DF71A3DF45600FFEBC4 /* main.swift */; };
D5EA0DFF1A3DF4E300FFEBC4 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0DFE1A3DF4E300FFEBC4 /* util.swift */; };
D5F105FF1A3E2BC30077263A /* func.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F105FE1A3E2BC30077263A /* func.swift */; };
D5F795C11BB9983900844EA2 /* MainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F795C01BB9983900844EA2 /* MainTests.swift */; settings = {ASSET_TAGS = (); }; };
D5F12D411BDACB87009A2C88 /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0DF71A3DF45600FFEBC4 /* main.swift */; };
D5F12D421BDACB87009A2C88 /* input.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5CD9B3B1B98258700C85F8C /* input.swift */; };
D5F12D431BDACB87009A2C88 /* processing.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5DA249C1B9CB1BF00AAA43E /* processing.swift */; };
D5F12D441BDACB87009A2C88 /* func.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F105FE1A3E2BC30077263A /* func.swift */; };
D5F12D451BDACB87009A2C88 /* types.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5032B681A7C1542007D1107 /* types.swift */; };
D5F12D461BDACB87009A2C88 /* util.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5EA0DFE1A3DF4E300FFEBC4 /* util.swift */; };
D5F12D471BDACB87009A2C88 /* values.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5B53A161A7D2A9B00F64418 /* values.swift */; };
D5F12D481BDACB8E009A2C88 /* XCProjectFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = D579466A1B9347C20044D2FC /* XCProjectFile.swift */; };
D5F12D491BDACB90009A2C88 /* Core.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5C5A8EE1BB7196000163E71 /* Core.swift */; };
D5F795C11BB9983900844EA2 /* MainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5F795C01BB9983900844EA2 /* MainTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
D5032B681A7C1542007D1107 /* types.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = types.swift; sourceTree = "<group>"; };
D5646DE11BE2016E0034F4D7 /* Extensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Extensions.swift; sourceTree = "<group>"; };
D5646DE21BE2016E0034F4D7 /* PBXObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PBXObject.swift; sourceTree = "<group>"; };
D5646DE31BE2016E0034F4D7 /* Serialization.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Serialization.swift; sourceTree = "<group>"; };
D579466A1B9347C20044D2FC /* XCProjectFile.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = XCProjectFile.swift; sourceTree = "<group>"; };
D5B53A161A7D2A9B00F64418 /* values.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = values.swift; sourceTree = "<group>"; };
D5C4227D1B711FDF004EA9B9 /* rswiftTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = rswiftTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -50,6 +68,9 @@
isa = PBXGroup;
children = (
D579466A1B9347C20044D2FC /* XCProjectFile.swift */,
D5646DE11BE2016E0034F4D7 /* Extensions.swift */,
D5646DE21BE2016E0034F4D7 /* PBXObject.swift */,
D5646DE31BE2016E0034F4D7 /* Serialization.swift */,
);
name = Xcode.swift;
path = Xcode.swift/Xcode/Xcode;
Expand Down Expand Up @@ -212,7 +233,19 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
D5F12D461BDACB87009A2C88 /* util.swift in Sources */,
D5F12D441BDACB87009A2C88 /* func.swift in Sources */,
D5F12D431BDACB87009A2C88 /* processing.swift in Sources */,
D586D1C71BE20D8600F18FEC /* PBXObject.swift in Sources */,
D5F12D411BDACB87009A2C88 /* main.swift in Sources */,
D5F12D451BDACB87009A2C88 /* types.swift in Sources */,
D5F12D421BDACB87009A2C88 /* input.swift in Sources */,
D5F795C11BB9983900844EA2 /* MainTests.swift in Sources */,
D5F12D481BDACB8E009A2C88 /* XCProjectFile.swift in Sources */,
D5F12D491BDACB90009A2C88 /* Core.swift in Sources */,
D586D1C61BE20D8600F18FEC /* Extensions.swift in Sources */,
D5F12D471BDACB87009A2C88 /* values.swift in Sources */,
D586D1C81BE20D8600F18FEC /* Serialization.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand All @@ -224,8 +257,11 @@
D5DA249D1B9CB1BF00AAA43E /* processing.swift in Sources */,
D5EA0DF81A3DF45600FFEBC4 /* main.swift in Sources */,
D5EA0DFF1A3DF4E300FFEBC4 /* util.swift in Sources */,
D5646DE61BE2016E0034F4D7 /* Serialization.swift in Sources */,
D5F105FF1A3E2BC30077263A /* func.swift in Sources */,
D5646DE41BE2016E0034F4D7 /* Extensions.swift in Sources */,
D5B53A171A7D2A9B00F64418 /* values.swift in Sources */,
D5646DE51BE2016E0034F4D7 /* PBXObject.swift in Sources */,
D579466B1B9347C20044D2FC /* XCProjectFile.swift in Sources */,
D5CD9B3C1B98258700C85F8C /* input.swift in Sources */,
D5032B691A7C1542007D1107 /* types.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>classNames</key>
<dict>
<key>MainTests</key>
<dict>
<key>testPerformanceExample()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.092011</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
<key>testPerformanceSwiftNameSanitization()</key>
<dict>
<key>com.apple.XCTPerformanceMetric_WallClockTime</key>
<dict>
<key>baselineAverage</key>
<real>0.11421</real>
<key>baselineIntegrationDisplayName</key>
<string>Local Baseline</string>
</dict>
</dict>
</dict>
</dict>
</dict>
</plist>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>runDestinationsByUUID</key>
<dict>
<key>14F5F524-DCA5-4BC2-96F0-91274CB940AF</key>
<dict>
<key>localComputer</key>
<dict>
<key>busSpeedInMHz</key>
<integer>100</integer>
<key>cpuCount</key>
<integer>1</integer>
<key>cpuKind</key>
<string>Intel Core i7</string>
<key>cpuSpeedInMHz</key>
<integer>2800</integer>
<key>logicalCPUCoresPerPackage</key>
<integer>4</integer>
<key>modelCode</key>
<string>MacBookPro11,1</string>
<key>physicalCPUCoresPerPackage</key>
<integer>2</integer>
<key>platformIdentifier</key>
<string>com.apple.platform.macosx</string>
</dict>
<key>targetArchitecture</key>
<string>x86_64</string>
</dict>
</dict>
</dict>
</plist>
6 changes: 3 additions & 3 deletions R.swift.xcodeproj/xcshareddata/xcschemes/rswift.xcscheme
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForTesting = "NO"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
Expand All @@ -22,10 +22,10 @@
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "YES">
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "D5C4227C1B711FDF004EA9B9"
Expand Down
17 changes: 13 additions & 4 deletions R.swift/func.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,21 @@ func filterDirectoryContentsRecursively(fileManager: NSFileManager, filter: (NSU
return assetFolders
}

/*
Disallowed characters: whitespace, mathematical symbols, arrows, private-use and invalid Unicode points, line- and boxdrawing characters
Special rules: Can't begin with a number
*/
func sanitizedSwiftName(name: String, lowercaseFirstCharacter: Bool = true) -> String {
var components = name.componentsSeparatedByCharactersInSet(NSCharacterSet(charactersInString: " -.@"))
let firstComponent = components.removeAtIndex(0)
let swiftName = components.reduce(firstComponent) { $0 + $1.capitalizedString }
let capitalizedSwiftName = lowercaseFirstCharacter ? swiftName.lowercaseFirstCharacter : swiftName
var nameComponents = name.componentsSeparatedByCharactersInSet(BlacklistedCharacters)

let firstComponent = nameComponents.removeAtIndex(0)
let cleanedSwiftName = nameComponents.reduce(firstComponent) { $0 + $1.uppercaseFirstCharacter }

let regex = try! NSRegularExpression(pattern: "^[0-9]+", options: .CaseInsensitive)
let fullRange = NSRange(location: 0, length: cleanedSwiftName.characters.count)
let sanitizedSwiftName = regex.stringByReplacingMatchesInString(cleanedSwiftName, options: NSMatchingOptions(rawValue: 0), range: fullRange, withTemplate: "")

let capitalizedSwiftName = lowercaseFirstCharacter ? sanitizedSwiftName.lowercaseFirstCharacter : sanitizedSwiftName
return SwiftKeywords.contains(capitalizedSwiftName) ? "`\(capitalizedSwiftName)`" : capitalizedSwiftName
}

Expand Down
10 changes: 6 additions & 4 deletions R.swift/types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ struct Xcodeproj {
func resourceURLsForTarget(targetName: String, pathResolver: Path -> NSURL) throws -> [NSURL] {
// Look for target in project file
let allTargets = projectFile.project.targets
guard let target = allTargets.filter({ $0.productName == targetName }).first else {
let availableTargets = allTargets.map { $0.productName }.joinWithSeparator(", ")
guard let target = allTargets.filter({ $0.name == targetName }).first else {
let availableTargets = allTargets.map { $0.name }.joinWithSeparator(", ")
throw ResourceParsingError.ParsingFailed("Target '\(targetName)' not found in project file, available targets are: \(availableTargets)")
}

Expand Down Expand Up @@ -401,9 +401,11 @@ struct Image {
throw ResourceParsingError.ParsingFailed("Filename could not be parsed from URL: \(url.absoluteString)")
}

let regex = try! NSRegularExpression(pattern: "(@[2,3]x)?\\.png$", options: .CaseInsensitive)
let extensions = ImageExtensions.joinWithSeparator("|")
let regex = try! NSRegularExpression(pattern: "(~(ipad|iphone))?(@[2,3]x)?\\.(\(extensions))$", options: .CaseInsensitive)
let fullFileNameRange = NSRange(location: 0, length: filename.characters.count)
name = regex.stringByReplacingMatchesInString(filename, options: NSMatchingOptions(rawValue: 0), range: fullFileNameRange, withTemplate: "")
let pathExtensionToUse = (pathExtension == "png") ? "" : ".\(pathExtension)"
name = regex.stringByReplacingMatchesInString(filename, options: NSMatchingOptions(rawValue: 0), range: fullFileNameRange, withTemplate: pathExtensionToUse)
}
}

Expand Down
21 changes: 6 additions & 15 deletions R.swift/util.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,6 @@ extension Array {
}

extension SequenceType {
func groupBy<U: Hashable>(keySelector: Generator.Element -> U) -> [U: [Generator.Element]] {
var groupedBy = Dictionary<U, [Generator.Element]>()

for element in self {
let key = keySelector(element)
if let group = groupedBy[key] {
groupedBy[key] = group + [element]
} else {
groupedBy[key] = [element]
}
}

return groupedBy
}

func groupUniquesAndDuplicates<U: Hashable>(keySelector: Generator.Element -> U) -> (uniques: [Generator.Element], duplicates: [[Generator.Element]]) {
let groupedBy = Array(groupBy(keySelector).values)
let uniques = groupedBy.filter { $0.count == 1 }.reduce([], combine: +)
Expand All @@ -57,6 +42,12 @@ extension String {
let index = startIndex.advancedBy(1)
return substringToIndex(index).lowercaseString + substringFromIndex(index)
}

var uppercaseFirstCharacter: String {
if self.characters.count <= 1 { return self.uppercaseString }
let index = startIndex.advancedBy(1)
return substringToIndex(index).uppercaseString + substringFromIndex(index)
}
}

func indentWithString(indentation: String) -> String -> String {
Expand Down
27 changes: 27 additions & 0 deletions R.swift/values.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
// License: MIT License
//

import Foundation

let ResourceFilename = "R.generated.swift"

let Header = [
Expand Down Expand Up @@ -245,6 +247,31 @@ let Ordinals = [
(number: 20, word: "twentieth"),
]

// Roughly based on http://www.unicode.org/Public/emoji/1.0//emoji-data.txt
let emojiRanges = [
0x2600...0x27BF,
0x1F300...0x1F6FF,
0x1F900...0x1F9FF,
0x1F1E6...0x1F1FF,
]

let BlacklistedCharacters = { () -> NSCharacterSet in
let blacklist = NSMutableCharacterSet(charactersInString: "")
blacklist.formUnionWithCharacterSet(NSCharacterSet.whitespaceAndNewlineCharacterSet())
blacklist.formUnionWithCharacterSet(NSCharacterSet.punctuationCharacterSet())
blacklist.formUnionWithCharacterSet(NSCharacterSet.symbolCharacterSet())
blacklist.formUnionWithCharacterSet(NSCharacterSet.illegalCharacterSet())
blacklist.formUnionWithCharacterSet(NSCharacterSet.controlCharacterSet())
blacklist.removeCharactersInString("_")

emojiRanges.forEach {
let range = NSRange(location: $0.startIndex, length: $0.endIndex - $0.startIndex)
blacklist.removeCharactersInRange(range)
}

return blacklist
}()

// Extensions
let AssetFolderExtensions: Set<String> = ["xcassets"]
let AssetExtensions: Set<String> = ["launchimage", "imageset"] // Note: "appiconset" is not loadable by default, so it's not included here
Expand Down
61 changes: 42 additions & 19 deletions R.swiftTests/MainTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,26 +10,49 @@ import XCTest

class MainTests: XCTestCase {

override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}

func testExample() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}

override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}

let swiftNameData = [
"easy": "easy",
"easyAndSimple": "easyAndSimple",
"easy with some spaces": "easyWithSomeSpaces",
"(looks) easy": "looksEasy",
"looks-easy": "looksEasy",
"looks+like^some-kind*of%easy": "looksLikeSomeKindOfEasy",
"(looks) easy, but it's not really NeXT that easy!": "looksEasyButItSNotReallyNeXTThatEasy",
"easy 123 and done...": "easy123AndDone",
"123 easy!": "easy",
"123 456easy": "easy",
"123 😄": "😄",
"🇳🇱": "🇳🇱",
"🌂MakeItRain!": "🌂MakeItRain",
]

func testSwiftNameSanitization() {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.

swiftNameData.forEach {
let sanitizedResult = sanitizedSwiftName($0.0, lowercaseFirstCharacter: true)
XCTAssertEqual(sanitizedResult, $0.1)
}

func testPerformanceExample() {
// This is an example of a performance test case.
self.measureBlock {
// Put the code you want to measure the time of here.
}
}

func testPerformanceSwiftNameSanitization() {
// This is an example of a performance test case.
self.measureBlock {
(0...1000).forEach { _ in
sanitizedSwiftName("(looks) easy, but it's not reallY that easy!", lowercaseFirstCharacter: true)
}
}
}

}
Loading

0 comments on commit 4235beb

Please sign in to comment.