Skip to content

Commit 82adb80

Browse files
authored
Merge pull request #21 from xzeror/master
Swift 5, SwiftLint, Refactoring
2 parents 88a69ce + 5a8ea44 commit 82adb80

File tree

5 files changed

+183
-66
lines changed

5 files changed

+183
-66
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,6 @@ fastlane/report.xml
6565
fastlane/Preview.html
6666
fastlane/screenshots
6767
fastlane/test_output
68+
69+
# macos
70+
.DS_Store

KeyboardLayoutGuide/.swiftlint.yml

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
excluded: # paths to ignore during linting. Takes precedence over `included`.
2+
- Carthage
3+
- Pods
4+
5+
disabled_rules: # rule identifiers to exclude from running
6+
- block_based_kvo # Swift implementatoin has bugs which lead to a crash - https://bugs.swift.org/browse/SR-5116, https://bugs.swift.org/browse/SR-5117
7+
- trailing_comma # There should be a com after the last item in the list
8+
9+
opt_in_rules: # some rules are only opt-in
10+
- attributes
11+
- anyobject_protocol
12+
- discouraged_optional_boolean
13+
- closure_body_length
14+
- closure_end_indentation
15+
- closure_spacing
16+
- collection_alignment
17+
- contains_over_first_not_nil
18+
- convenience_type
19+
- discouraged_object_literal
20+
- discouraged_optional_collection
21+
- empty_count
22+
- empty_string
23+
- empty_xctest_method
24+
- explicit_init
25+
- extension_access_modifier
26+
- fallthrough
27+
- fatal_error_message
28+
- first_where
29+
# - file_header
30+
- file_name
31+
- force_unwrapping
32+
- function_default_parameter_at_end
33+
- identical_operands
34+
- implicit_return
35+
- implicitly_unwrapped_optional
36+
- joined_default_parameter
37+
- last_where
38+
- legacy_random
39+
- let_var_whitespace
40+
- literal_expression_end_indentation
41+
- lower_acl_than_parent
42+
- missing_docs
43+
- modifier_order
44+
- multiline_arguments
45+
- multiline_arguments_brackets
46+
- multiline_literal_brackets
47+
- multiline_parameters
48+
- multiline_parameters_brackets
49+
- nimble_operator
50+
- number_separator
51+
- operator_usage_whitespace
52+
- override_in_extension
53+
- overridden_super_call
54+
- pattern_matching_keywords
55+
- prefixed_toplevel_constant
56+
- private_action
57+
- private_outlet
58+
- prohibited_interface_builder
59+
- prohibited_super_call
60+
- quick_discouraged_call
61+
- redundant_nil_coalescing
62+
- redundant_type_annotation
63+
- required_enum_case
64+
- single_test_class
65+
- sorted_first_last
66+
- sorted_imports
67+
- static_operator
68+
- strict_fileprivate
69+
- switch_case_on_newline
70+
- toggle_bool
71+
- trailing_closure
72+
- unavailable_function
73+
- unneeded_parentheses_in_closure_argument
74+
- untyped_error_in_catch
75+
- unused_import
76+
- unused_private_declaration
77+
- vertical_parameter_alignment_on_call
78+
- vertical_whitespace_closing_braces
79+
- vertical_whitespace_opening_braces
80+
- xct_specific_matcher
81+
- yoda_condition
82+
83+
line_length: 200
84+
85+
identifier_name:
86+
severity: warning
87+
min_length: 3
88+
max_length: 40
89+
excluded:
90+
- id
91+
- ID
92+
- rx
93+
validates_start_with_lowercase: true
94+
95+
function_body_length: 50
96+
97+
cyclomatic_complexity:
98+
warning: 8
99+
error: 10
100+
101+
force_unwrapping:
102+
severity: error
103+
104+
redundant_type_annotation:
105+
severity: error
106+
107+
custom_rules:
108+
empty_lines_after_type_declarations:
109+
included: ".*.swift"
110+
name: "Empty lines after type declarations"
111+
regex: '(struct|class|enum|protocol|extension) ([\w]+(:\s*[\w\s,]+)* )\{\n\n'
112+
message: "There should be no empty lines after type declarations"
113+
severity: error
114+
115+
warning_threshold: 1

KeyboardLayoutGuide/KeyboardLayoutGuide.xcodeproj/project.pbxproj

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@
111111
9902DE071FBB24A5009E0D48 /* Frameworks */,
112112
9902DE081FBB24A5009E0D48 /* Headers */,
113113
9902DE091FBB24A5009E0D48 /* Resources */,
114+
10B2D35122645E990028548F /* SwiftLint */,
114115
);
115116
buildRules = (
116117
);
@@ -197,6 +198,27 @@
197198
};
198199
/* End PBXResourcesBuildPhase section */
199200

201+
/* Begin PBXShellScriptBuildPhase section */
202+
10B2D35122645E990028548F /* SwiftLint */ = {
203+
isa = PBXShellScriptBuildPhase;
204+
buildActionMask = 2147483647;
205+
files = (
206+
);
207+
inputFileListPaths = (
208+
);
209+
inputPaths = (
210+
);
211+
name = SwiftLint;
212+
outputFileListPaths = (
213+
);
214+
outputPaths = (
215+
);
216+
runOnlyForDeploymentPostprocessing = 0;
217+
shellPath = /bin/sh;
218+
shellScript = "if which swiftlint >/dev/null; then\n/usr/local/bin/swiftlint autocorrect --quiet\n/usr/local/bin/swiftlint lint --reporter \"xcode\" --quiet \nelse\necho \"error: SwiftLint not installed, 'brew install swiftlint' or download from https://github.com/realm/SwiftLint\"\nexit -1\nfi\n";
219+
};
220+
/* End PBXShellScriptBuildPhase section */
221+
200222
/* Begin PBXSourcesBuildPhase section */
201223
9902DE061FBB24A5009E0D48 /* Sources */ = {
202224
isa = PBXSourcesBuildPhase;

KeyboardLayoutGuide/KeyboardLayoutGuide/Keyboard+LayoutGuide.swift

Lines changed: 41 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,54 +13,51 @@ private class Keyboard {
1313
var currentHeight: CGFloat = 0
1414
}
1515

16-
public extension UIView {
17-
18-
private struct AssociatedKeys {
16+
extension UIView {
17+
private enum AssociatedKeys {
1918
static var keyboardLayoutGuide = "keyboardLayoutGuide"
2019
}
21-
20+
2221
/// A layout guide representing the inset for the keyboard.
2322
/// Use this layout guide’s top anchor to create constraints pinning to the top of the keyboard.
24-
var keyboardLayoutGuide: KeyboardLayoutGuide {
25-
get {
26-
if let obj = objc_getAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide) as? KeyboardLayoutGuide {
27-
return obj
28-
}
29-
let new = KeyboardLayoutGuide()
30-
addLayoutGuide(new)
31-
new.setUp()
32-
objc_setAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide, new as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
33-
return new
23+
public var keyboardLayoutGuide: KeyboardLayoutGuide {
24+
if let obj = objc_getAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide) as? KeyboardLayoutGuide {
25+
return obj
3426
}
27+
let new = KeyboardLayoutGuide()
28+
addLayoutGuide(new)
29+
new.setUp()
30+
objc_setAssociatedObject(self, &AssociatedKeys.keyboardLayoutGuide, new as Any, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
31+
return new
3532
}
3633
}
3734

3835
open class KeyboardLayoutGuide: UILayoutGuide {
39-
36+
@available(*, unavailable)
4037
public required init?(coder aDecoder: NSCoder) {
4138
fatalError("init(coder:) has not been implemented")
4239
}
43-
44-
public override init() {
40+
41+
public init(notificationCenter: NotificationCenter = NotificationCenter.default) {
4542
super.init()
46-
4743
// Observe keyboardWillChangeFrame notifications
48-
let nc = NotificationCenter.default
49-
nc.addObserver(self,
50-
selector: #selector(keyboardWillChangeFrame(_:)),
51-
name: UIResponder.keyboardWillChangeFrameNotification,
52-
object: nil)
44+
notificationCenter.addObserver(
45+
self,
46+
selector: #selector(keyboardWillChangeFrame(_:)),
47+
name: UIResponder.keyboardWillChangeFrameNotification,
48+
object: nil
49+
)
5350
}
54-
51+
5552
internal func setUp() {
56-
guard let view = owningView else {
57-
return
58-
}
59-
NSLayoutConstraint.activate([
60-
heightAnchor.constraint(equalToConstant: Keyboard.shared.currentHeight),
61-
leftAnchor.constraint(equalTo: view.leftAnchor),
62-
rightAnchor.constraint(equalTo: view.rightAnchor),
63-
])
53+
guard let view = owningView else { return }
54+
NSLayoutConstraint.activate(
55+
[
56+
heightAnchor.constraint(equalToConstant: Keyboard.shared.currentHeight),
57+
leftAnchor.constraint(equalTo: view.leftAnchor),
58+
rightAnchor.constraint(equalTo: view.rightAnchor),
59+
]
60+
)
6461
let viewBottomAnchor: NSLayoutYAxisAnchor
6562
if #available(iOS 11.0, *) {
6663
viewBottomAnchor = view.safeAreaLayoutGuide.bottomAnchor
@@ -69,57 +66,52 @@ open class KeyboardLayoutGuide: UILayoutGuide {
6966
}
7067
bottomAnchor.constraint(equalTo: viewBottomAnchor).isActive = true
7168
}
72-
69+
7370
@objc
7471
private func keyboardWillChangeFrame(_ note: Notification) {
7572
if var height = note.keyboardHeight {
76-
if #available(iOS 11.0, *), height > 0 {
77-
height -= (owningView?.safeAreaInsets.bottom)!
73+
if #available(iOS 11.0, *), height > 0, let bottom = owningView?.safeAreaInsets.bottom {
74+
height -= bottom
7875
}
7976
heightConstraint?.constant = height
8077
animate(note)
8178
Keyboard.shared.currentHeight = height
8279
}
8380
}
84-
81+
8582
private func animate(_ note: Notification) {
86-
if isVisible(view: self.owningView!) {
83+
if
84+
let owningView = self.owningView,
85+
isVisible(view: owningView)
86+
{
8787
self.owningView?.layoutIfNeeded()
8888
} else {
8989
UIView.performWithoutAnimation {
9090
self.owningView?.layoutIfNeeded()
9191
}
9292
}
9393
}
94-
95-
deinit {
96-
NotificationCenter.default.removeObserver(self)
97-
}
9894
}
9995

10096
// MARK: - Helpers
10197

10298
extension UILayoutGuide {
10399
internal var heightConstraint: NSLayoutConstraint? {
104-
guard let target = owningView else { return nil }
105-
for c in target.constraints {
106-
if let fi = c.firstItem as? UILayoutGuide, fi == self && c.firstAttribute == .height {
107-
return c
108-
}
100+
return owningView?.constraints.first {
101+
$0 == self && $0.firstAttribute == .height
109102
}
110-
return nil
111103
}
112104
}
113105

114106
extension Notification {
115107
var keyboardHeight: CGFloat? {
116-
guard let v = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
108+
guard let keyboardFrame = userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? NSValue else {
117109
return nil
118110
}
119111
// Weirdly enough UIKeyboardFrameEndUserInfoKey doesn't have the same behaviour
120112
// in ios 10 or iOS 11 so we can't rely on v.cgRectValue.width
121113
let screenHeight = UIApplication.shared.keyWindow?.bounds.height ?? UIScreen.main.bounds.height
122-
return screenHeight - v.cgRectValue.minY
114+
return screenHeight - keyboardFrame.cgRectValue.minY
123115
}
124116
}
125117

@@ -136,4 +128,3 @@ func isVisible(view: UIView) -> Bool {
136128
}
137129
return isVisible(view: view, inView: view.superview)
138130
}
139-

KeyboardLayoutGuide/KeyboardLayoutGuideTests/KeyboardLayoutGuideTests.swift

Lines changed: 2 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,17 @@
66
// Copyright © 2017 freshos. All rights reserved.
77
//
88

9-
import XCTest
109
@testable import KeyboardLayoutGuide
10+
import XCTest
1111

1212
class KeyboardLayoutGuideTests: XCTestCase {
13-
1413
override func setUp() {
1514
super.setUp()
1615
// Put setup code here. This method is called before the invocation of each test method in the class.
1716
}
18-
17+
1918
override func tearDown() {
2019
// Put teardown code here. This method is called after the invocation of each test method in the class.
2120
super.tearDown()
2221
}
23-
24-
func testExample() {
25-
// This is an example of a functional test case.
26-
// Use XCTAssert and related functions to verify your tests produce the correct results.
27-
}
28-
29-
func testPerformanceExample() {
30-
// This is an example of a performance test case.
31-
self.measure {
32-
// Put the code you want to measure the time of here.
33-
}
34-
}
35-
3622
}

0 commit comments

Comments
 (0)