Skip to content

Commit 6274af4

Browse files
committed
Merge release/v2.0.1 into master
2 parents 4dc15d1 + 57d1554 commit 6274af4

File tree

10 files changed

+190
-71
lines changed

10 files changed

+190
-71
lines changed

IBMigrationTool/IBMigrationTool

4.67 KB
Binary file not shown.

IBMigrationTool/InterfaceBuilderFileMigrator.swift

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,45 @@ import Foundation
1212
/// when upgrading to `ThunderBasics` v2.0.0
1313
final class InterfaceBuilderFileMigrator {
1414

15-
static let directlyMappableAttributes: [(String, String)] = [
16-
("color", "borderColor"),
17-
("number", "borderWidth"),
18-
("number", "cornerRadius"),
19-
("color", "shadowColor"),
20-
("number", "shadowOpacity"),
21-
("number", "shadowRadius")
15+
/// A structural representation of a user-defined IB attribute
16+
struct UserDefinedAttribute {
17+
18+
/// A type for the given attribute
19+
enum AttributeType: String {
20+
21+
case color
22+
23+
case number
24+
25+
var isMigratable: Bool {
26+
switch self {
27+
case .color:
28+
return false
29+
default:
30+
return true
31+
}
32+
}
33+
}
34+
35+
/// The type of the attribute
36+
let type: AttributeType
37+
38+
/// The key for the attribute
39+
let key: String
40+
41+
/// Returns whether the attribute is migratable
42+
var isMigratable: Bool {
43+
return type.isMigratable
44+
}
45+
}
46+
47+
static let directlyMappableAttributes: [UserDefinedAttribute] = [
48+
.init(type: .color, key: "borderColor"),
49+
.init(type: .number, key: "borderWidth"),
50+
.init(type: .number, key: "cornerRadius"),
51+
.init(type: .color, key: "shadowColor"),
52+
.init(type: .number, key: "shadowOpacity"),
53+
.init(type: .number, key: "shadowRadius")
2254
]
2355

2456
static let removedCustomClasses: [String] = [
@@ -49,9 +81,11 @@ final class InterfaceBuilderFileMigrator {
4981
}
5082

5183
/// Performs migration of the current file, storing the result in `string`
52-
func migrate() {
53-
migrateUserDefinedRuntimeAttributes()
84+
/// - Returns: An object mapping from lines that were unmigratable to the contents of the line
85+
@discardableResult func migrate() -> [Int: String] {
86+
let unmigratable = migrateUserDefinedRuntimeAttributes()
5487
migrateRemovedCustomClasses()
88+
return unmigratable
5589
}
5690

5791
private func migrateRemovedCustomClasses() {
@@ -71,18 +105,37 @@ final class InterfaceBuilderFileMigrator {
71105
}
72106
}
73107

74-
private func migrateUserDefinedRuntimeAttributes() {
108+
private func migrateUserDefinedRuntimeAttributes() -> [Int: String] {
109+
110+
var unmigratable: [Int: String] = [:]
75111

76112
// These use the same type as `CALayer` equivalents, so can be
77113
// mapped simply using the same regex
78114
Self.directlyMappableAttributes.forEach { (attribute) in
79-
80-
string = string.replacingOccurrences(
81-
of: "<userDefinedRuntimeAttribute(\\s+)type=\"\(attribute.0)\"(\\s+)keyPath=\"(\(attribute.1))\">",
82-
with: "<userDefinedRuntimeAttribute$1type=\"\(attribute.0)\"$2keyPath=\"layer.$3\">",
83-
options: .regularExpression,
84-
range: nil
85-
)
115+
116+
if attribute.isMigratable {
117+
string = string.replacingOccurrences(
118+
of: "<userDefinedRuntimeAttribute(\\s+)type=\"\(attribute.type.rawValue)\"(\\s+)keyPath=\"(\(attribute.key))\">",
119+
with: "<userDefinedRuntimeAttribute$1type=\"\(attribute.type.rawValue)\"$2keyPath=\"layer.$3\">",
120+
options: .regularExpression,
121+
range: nil
122+
)
123+
} else if let matchRegex = try? NSRegularExpression(
124+
pattern: "<userDefinedRuntimeAttribute\\s+type=\"\(attribute.type.rawValue)\"\\s+keyPath=\"(\(attribute.key))\">",
125+
options: []
126+
) {
127+
var lineNumber: Int = 0
128+
string.enumerateLines { (line, _) in
129+
if matchRegex.numberOfMatches(
130+
in: line,
131+
options: [],
132+
range: NSRange(line.startIndex..<line.endIndex, in: line)
133+
) > 0 {
134+
unmigratable[lineNumber] = line.trimmingCharacters(in: .whitespaces)
135+
}
136+
lineNumber += 1
137+
}
138+
}
86139
}
87140

88141
// shadowOffset is a bit more complex because we used `CGPoint` but
@@ -97,5 +150,7 @@ final class InterfaceBuilderFileMigrator {
97150
options: .regularExpression,
98151
range: nil
99152
)
153+
154+
return unmigratable
100155
}
101156
}

IBMigrationTool/main.swift

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,42 +34,96 @@ extension FileManager {
3434
/// Converts all `UIView+Displayable` runtime attributes to use the
3535
/// `CALayer` equivalents in the file at the given path
3636
/// - Parameter path: The file path to convert
37-
func migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at path: String) {
37+
/// - Returns: Whether there were un-migratable attributes in the file.
38+
func migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at path: String) -> Bool {
3839

39-
print("Migrating user defined runtime attributes in \(path)")
40+
print("=> Migrating user defined runtime attributes in \(path)")
4041

4142
guard let migrator = InterfaceBuilderFileMigrator(filePath: path) else {
42-
print("Failed to read data as string from: \(path)")
43-
return
43+
print("=> Failed to read data as string from: \(path)")
44+
return false
45+
}
46+
let unmigratableMatches = migrator.migrate()
47+
48+
if !unmigratableMatches.isEmpty {
49+
print("""
50+
=> Found un-migratable properties in \(path):
51+
52+
\(unmigratableMatches.compactMap({ (keyValue) -> String in
53+
return "Line \(keyValue.key): \(keyValue.value)"
54+
}).joined(separator: "\n"))
55+
"""
56+
)
4457
}
45-
migrator.migrate()
4658

4759
// Save file to disk!
4860
let newData = migrator.string.data(using: .utf8)
4961
do {
5062
let fileURL = URL(fileURLWithPath: path)
5163
try newData?.write(to: fileURL)
52-
print("Wrote migrated file to \(path)")
64+
print("=> Wrote migrated file to \(path)")
5365
} catch {
54-
print("Failed to write migrated file to \(path)")
66+
print("=> Failed to write migrated file to \(path)")
67+
}
68+
69+
return !migrator.unmigratableMatches.isEmpty
70+
}
71+
72+
var pValue: String?
73+
let yesStrings = ["y", "yes"]
74+
75+
while case let option = getopt(CommandLine.argc, CommandLine.unsafeArgv, "p:"), option != -1 {
76+
switch UnicodeScalar(CUnsignedChar(option)) {
77+
case "p":
78+
pValue = String(cString: optarg)
79+
default:
80+
fatalError("Unknown option")
5581
}
5682
}
5783

58-
print("This tool will make changes to the Interface Builder (.xib/.storyboard) files in the chosen directory. Please make sure you have no changes in your index before continuing")
59-
print("Please enter the file path to the Project you want to migrate Interface Builder files to ThunderBasics 2.0.0")
60-
var filePath = readLine(strippingNewline: true)
61-
while filePath == nil {
62-
filePath = readLine(strippingNewline: true)
84+
if pValue == nil {
85+
print("=> Running IBMigrationTool in your current working directory, to run in a different directory, provide the -p command line argument")
6386
}
6487

65-
filePath = filePath?.trimmingCharacters(in: .whitespaces).replacingOccurrences(of: "\\ ", with: " ")
66-
print("Parsing contents of \(filePath!) for IB Files")
88+
// See https://stackoverflow.com/questions/44193114/swift-3-read-terminal-current-working-directory for why we use `FileManager` here
89+
let filePath = pValue ?? FileManager.default.currentDirectoryPath
6790

68-
FileManager.default.recursivePathsForResource("xib", directory: filePath!).forEach { (xibPath) in
69-
migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at: xibPath)
91+
print("=> This tool will make changes to the Interface Builder (.xib/.storyboard) files in the chosen directory. Please make sure you have no changes in your index before continuing. Please type \"yes\" when you have done this.")
92+
var yesInput = readLine(strippingNewline: true)
93+
while !yesStrings.contains(yesInput?.lowercased() ?? "") {
94+
yesInput = readLine(strippingNewline: true)
7095
}
7196

72-
FileManager.default.recursivePathsForResource("storyboard", directory: filePath!).forEach { (storyboardPath) in
73-
migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at: storyboardPath)
97+
print("=> Finding Interface Builder files in \(filePath)")
98+
99+
var seenUnmigratableProperties: Bool = false
100+
var noFilesFound: Bool = true
101+
102+
FileManager.default.recursivePathsForResource("xib", directory: filePath).forEach { (xibPath) in
103+
noFilesFound = false
104+
let unmigratable = migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at: xibPath)
105+
seenUnmigratableProperties = unmigratable || seenUnmigratableProperties
106+
}
107+
108+
FileManager.default.recursivePathsForResource("storyboard", directory: filePath).forEach { (storyboardPath) in
109+
noFilesFound = false
110+
let unmigratable = migrateUserDefinedRuntimeAttributesInInterfaceBuilderFile(at: storyboardPath)
111+
seenUnmigratableProperties = unmigratable || seenUnmigratableProperties
74112
}
75113

114+
if noFilesFound {
115+
print("=> No Interface Builder files found in \(filePath) or any of it's sub-directories")
116+
}
117+
118+
guard seenUnmigratableProperties else { exit(0) }
119+
120+
print("""
121+
=> For all un-migratable properties found above you will need to perform manual migration.
122+
123+
The suggested steps for this are as follows:
124+
1. Search for the property in Xcode's search functionality.
125+
2. If the view in question doesn't have an IBOutlet, then create one.
126+
3. In the IBOutlet property for the view, add a `didSet` (If one doesn't already exist)
127+
4. Set the un-migratable property manually in the `didSet` method
128+
"""
129+
)

IBMigrationTool/migrate.sh

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
bin_path="IBMigrationTool"
3+
migrationtool_binary_download_url="https://raw.githubusercontent.com/3sidedcube/ThunderBasics/develop/IBMigrationTool/IBMigrationTool"
4+
5+
echo "=> Downloading IBMigrationTool from (${migrationtool_binary_download_url})"
6+
7+
curl -fL --progress-bar --output "IBMigrationTool" "${migrationtool_binary_download_url}"
8+
9+
echo "=> Making it executable ..."
10+
chmod +x "${bin_path}"
11+
12+
./${bin_path}
13+
14+
rm -rf ${bin_path}

IBMigrationToolTests/IBMigrationToolTests.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,16 @@ class IBMigrationToolTests: XCTestCase {
3939

4040
guard let fileMigrator = migrator else { return }
4141

42-
fileMigrator.migrate()
42+
let unmigratable = fileMigrator.migrate()
4343

4444
// Remove whitespace when comparing because it's irrelevant and seems to cause issues when comparing!
45+
4546
XCTAssertEqual(
4647
fileMigrator.string.replacingOccurrences(of: "\\s", with: "", options: .regularExpression, range: nil),
4748
finalString.replacingOccurrences(of: "\\s", with: "", options: .regularExpression, range: nil)
4849
)
50+
51+
XCTAssertEqual(unmigratable[24], "<userDefinedRuntimeAttribute type=\"color\" keyPath=\"borderColor\">")
52+
XCTAssertEqual(unmigratable[33], "<userDefinedRuntimeAttribute type=\"color\" keyPath=\"shadowColor\">")
4953
}
5054
}

IBMigrationToolTests/TestStoryboard-Migrated.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
2323
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
2424
<userDefinedRuntimeAttributes>
25-
<userDefinedRuntimeAttribute type="color" keyPath="layer.borderColor">
25+
<userDefinedRuntimeAttribute type="color" keyPath="borderColor">
2626
<color key="value" systemColor="systemRedColor"/>
2727
</userDefinedRuntimeAttribute>
2828
<userDefinedRuntimeAttribute type="number" keyPath="layer.borderWidth">
@@ -31,7 +31,7 @@
3131
<userDefinedRuntimeAttribute type="number" keyPath="layer.cornerRadius">
3232
<real key="value" value="5"/>
3333
</userDefinedRuntimeAttribute>
34-
<userDefinedRuntimeAttribute type="color" keyPath="layer.shadowColor">
34+
<userDefinedRuntimeAttribute type="color" keyPath="shadowColor">
3535
<color key="value" systemColor="systemYellowColor"/>
3636
</userDefinedRuntimeAttribute>
3737
<userDefinedRuntimeAttribute type="number" keyPath="layer.shadowOpacity">

ThunderBasics.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -992,7 +992,7 @@
992992
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
993993
MACH_O_TYPE = mh_dylib;
994994
MACOSX_DEPLOYMENT_TARGET = 10.12;
995-
MARKETING_VERSION = 2.0.0;
995+
MARKETING_VERSION = 2.0.1;
996996
PRODUCT_BUNDLE_IDENTIFIER = com.threesidedcube.ThunderBasicsMac;
997997
PRODUCT_NAME = ThunderBasics;
998998
SDKROOT = macosx;
@@ -1022,7 +1022,7 @@
10221022
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks";
10231023
MACH_O_TYPE = mh_dylib;
10241024
MACOSX_DEPLOYMENT_TARGET = 10.12;
1025-
MARKETING_VERSION = 2.0.0;
1025+
MARKETING_VERSION = 2.0.1;
10261026
PRODUCT_BUNDLE_IDENTIFIER = com.threesidedcube.ThunderBasicsMac;
10271027
PRODUCT_NAME = ThunderBasics;
10281028
SDKROOT = macosx;
@@ -1322,7 +1322,7 @@
13221322
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
13231323
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
13241324
MACH_O_TYPE = mh_dylib;
1325-
MARKETING_VERSION = 2.0.0;
1325+
MARKETING_VERSION = 2.0.1;
13261326
ONLY_ACTIVE_ARCH = NO;
13271327
PRODUCT_BUNDLE_IDENTIFIER = "com.threesidedcube.$(PRODUCT_NAME:rfc1034identifier)";
13281328
PRODUCT_NAME = ThunderBasics;
@@ -1351,7 +1351,7 @@
13511351
IPHONEOS_DEPLOYMENT_TARGET = 10.0;
13521352
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
13531353
MACH_O_TYPE = mh_dylib;
1354-
MARKETING_VERSION = 2.0.0;
1354+
MARKETING_VERSION = 2.0.1;
13551355
PRODUCT_BUNDLE_IDENTIFIER = "com.threesidedcube.$(PRODUCT_NAME:rfc1034identifier)";
13561356
PRODUCT_NAME = ThunderBasics;
13571357
SKIP_INSTALL = YES;

ThunderBasics/UIView+MultipleShadows.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@ internal class ShadowLayer: CALayer {
2121
self.cornerRadius = cornerRadius
2222
self.viewCornerCurve = cornerCurve
2323
}
24+
25+
override init(layer: Any) {
26+
super.init(layer: layer)
27+
}
2428

2529
required init?(coder: NSCoder) {
2630
fatalError("init(coder:) has not been implemented")

carthage-build.sh

Lines changed: 0 additions & 29 deletions
This file was deleted.

carthage.sh

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#!/usr/bin/env bash
2+
3+
# carthage.sh
4+
# Usage example: ./carthage.sh build --platform iOS
5+
6+
set -euo pipefail
7+
8+
xcconfig=$(mktemp /tmp/static.xcconfig.XXXXXX)
9+
trap 'rm -f "$xcconfig"' INT TERM HUP EXIT
10+
11+
# For Xcode 12 make sure EXCLUDED_ARCHS is set to arm architectures otherwise
12+
# the build will fail on lipo due to duplicate architectures.
13+
echo 'EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_simulator__NATIVE_ARCH_64_BIT_x86_64__XCODE_1200 = arm64 arm64e armv7 armv7s armv6 armv8' >> $xcconfig
14+
echo 'EXCLUDED_ARCHS = $(inherited) $(EXCLUDED_ARCHS__EFFECTIVE_PLATFORM_SUFFIX_$(EFFECTIVE_PLATFORM_SUFFIX)__NATIVE_ARCH_64_BIT_$(NATIVE_ARCH_64_BIT)__XCODE_$(XCODE_VERSION_MAJOR))' >> $xcconfig
15+
16+
export XCODE_XCCONFIG_FILE="$xcconfig"
17+
carthage "$@"

0 commit comments

Comments
 (0)