Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UseTraitCollections rule #164

Merged
merged 4 commits into from
Sep 26, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -140,10 +140,19 @@ You can configure `view_as_device` rule by `ViewAsDeviceConfig`. If there are no
| `iPhone XR` | `retina6_1` |
| `iPhone XS Max` | `retina6_5` |

### UseTraitCollectionsConfig

You can configure `use_trait_collections rule by `UseTraitCollectionsConfig`. If there is no config then use_trait_collections is set to true

| key | description |
|:---------------:|:---------------:|
| enabled | `true` |


```yaml
enabled_rules:
- relative_to_margin
- use_trait_collections
disabled_rules:
- custom_class_name
excluded:
Expand All @@ -164,4 +173,6 @@ use_base_class_rule:
- SecondaryLabel
view_as_device_rule:
device_id: retina4_0
use_trait_collections_rule:
enabled: false
```
8 changes: 8 additions & 0 deletions Rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,3 +131,11 @@ Check that ReuseIdentifier same as class name.
| Default Rule | `false` |

Check if named color resources are valid.

## UseTraitCollectionsRule

| identifier | `use_trait_collections` |
|:---------------:|:---------------:|
| Default Rule | `false` |

Check if a document useTraitCollections is enabled or disabled
6 changes: 6 additions & 0 deletions Sources/IBLinterKit/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public struct Config: Codable {
public let customModuleRule: [CustomModuleConfig]
public let useBaseClassRule: [UseBaseClassConfig]
public let viewAsDeviceRule: ViewAsDeviceConfig?
public let useTraitCollectionsRule: UseTraitCollectionsConfig?
public let reporter: String
public let disableWhileBuildingForIB: Bool
public let ignoreCache: Bool
Expand All @@ -28,6 +29,7 @@ public struct Config: Codable {
case customModuleRule = "custom_module_rule"
case useBaseClassRule = "use_base_class_rule"
case viewAsDeviceRule = "view_as_device_rule"
case useTraitCollectionsRule = "use_trait_collections_rule"
case reporter = "reporter"
case disableWhileBuildingForIB = "disable_while_building_for_ib"
case ignoreCache = "ignore_cache"
Expand All @@ -44,6 +46,7 @@ public struct Config: Codable {
customModuleRule = []
useBaseClassRule = []
viewAsDeviceRule = nil
useTraitCollectionsRule = nil
reporter = "xcode"
disableWhileBuildingForIB = true
ignoreCache = false
Expand All @@ -54,6 +57,7 @@ public struct Config: Codable {
customModuleRule: [CustomModuleConfig] = [],
baseClassRule: [UseBaseClassConfig] = [],
viewAsDeviceRule: ViewAsDeviceConfig? = nil,
useTraitCollectionsRule: UseTraitCollectionsConfig? = nil,
reporter: String = "xcode", disableWhileBuildingForIB: Bool = true,
ignoreCache: Bool = false) {
self.disabledRules = disabledRules
Expand All @@ -62,6 +66,7 @@ public struct Config: Codable {
self.included = included
self.customModuleRule = customModuleRule
self.useBaseClassRule = baseClassRule
self.useTraitCollectionsRule = useTraitCollectionsRule
self.viewAsDeviceRule = viewAsDeviceRule
self.reporter = reporter
self.disableWhileBuildingForIB = disableWhileBuildingForIB
Expand All @@ -76,6 +81,7 @@ public struct Config: Codable {
included = try container.decodeIfPresent(Optional<[String]>.self, forKey: .included).flatMap { $0 } ?? []
customModuleRule = try container.decodeIfPresent(Optional<[CustomModuleConfig]>.self, forKey: .customModuleRule).flatMap { $0 } ?? []
useBaseClassRule = try container.decodeIfPresent(Optional<[UseBaseClassConfig]>.self, forKey: .useBaseClassRule)?.flatMap { $0 } ?? []
useTraitCollectionsRule = try container.decodeIfPresent(Optional<UseTraitCollectionsConfig>.self, forKey: .useTraitCollectionsRule) ?? nil
viewAsDeviceRule = try container.decodeIfPresent(Optional<ViewAsDeviceConfig>.self, forKey: .viewAsDeviceRule) ?? nil
reporter = try container.decodeIfPresent(String.self, forKey: .reporter) ?? "xcode"
disableWhileBuildingForIB = try container.decodeIfPresent(Bool.self, forKey: .disableWhileBuildingForIB) ?? true
Expand Down
25 changes: 25 additions & 0 deletions Sources/IBLinterKit/Config/UseTraitCollectionsConfig.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//
// UseTraitCollectionsConfig.swift
// IBLinterKit
//
// Created by Blazej SLEBODA on 13/09/2020
//

import Foundation

public struct UseTraitCollectionsConfig: Codable {
public let enabled: Bool

enum CodingKeys: String, CodingKey {
case enabled
}

init(enabled: Bool) {
self.enabled = enabled
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
enabled = try container.decode(Bool.self, forKey: .enabled)
}
}
1 change: 1 addition & 0 deletions Sources/IBLinterKit/Rule.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public struct Rules {
CustomModuleRule.self,
UseBaseClassRule.self,
AmbiguousViewRule.self,
UseTraitCollections.self,
ViewAsDeviceRule.self,
ReuseIdentifierRule.self,
ColorResourcesRule.self
Expand Down
57 changes: 57 additions & 0 deletions Sources/IBLinterKit/Rules/UseTraitCollections.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
//
// UseTraitCollections.swift
// IBLinterKit
//
// Created by Blazej SLEBODA on 13/09/2020
//

import IBDecodable

extension Rules {

struct UseTraitCollections: Rule {

static var identifier = "use_trait_collections"
static var description = "Check id document useTraitCollections is enabled or diasbled"
let enabled: Bool

init(context: Context) {
if let configUseTraitCollectionRule = context.config.useTraitCollectionsRule {
enabled = configUseTraitCollectionRule.enabled
} else {
enabled = true
}
}

func validate(storyboard: StoryboardFile) -> [Violation] {
Rules.violation(enabled, file: storyboard)
}

func validate(xib: XibFile) -> [Violation] {
Rules.violation(enabled, file: xib)
}

}

fileprivate static func violation<T: InterfaceBuilderFile>(_ enabled: Bool, file: T) -> [Violation] {

guard let document = file.document as? InterfaceBuilderDocument else {
return []
}

if enabled {
if document.useTraitCollections == .some(true) {
return []
} else {
return [Violation(pathString: file.pathString, message: "useTraitCollection must be enabled", level: .warning)]
}
} else {
if [Bool?(false), .none].contains(document.useTraitCollections) {
return []
} else {
return [Violation(pathString: file.pathString, message: "useTraitCollection must be disabled", level: .warning)]
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_5" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
</dependencies>
<scenes/>
</document>
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="16097.3" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_5" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="16087"/>
</dependencies>
<scenes/>
</document>
64 changes: 64 additions & 0 deletions Tests/IBLinterKitTest/Rules/UseTraitCollectionsTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//
// UseTraitCollectionsTests.swift
// IBLinterKitTest
//
// Created by Blazej SLEBODA on 13/09/2020
//

@testable import IBLinterKit
import XCTest
import IBDecodable

class UseTraitCollectionsTests: XCTestCase {

let fixture = Fixture()

var storyboardUseTraitCollectionsYes: StoryboardFile!
var storyboardUseTraitCollectionsNo: StoryboardFile!

override func setUp() {
super.setUp()

let disabledUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsNoTest.storyboard")
let enabledUrl = fixture.path("Resources/Rules/UseTraitCollectionsRule/UseTraitCollectionsYesTest.storyboard")

storyboardUseTraitCollectionsYes = try! .init(url: enabledUrl)
storyboardUseTraitCollectionsNo = try! .init(url: disabledUrl)

}

func testUseTraitCollectionsNoConfig() {
let ibLinterConfig = Config.default
let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig))

let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo)
XCTAssertEqual(noViolations.count, 1)

let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes)
XCTAssertTrue(yesViolations.isEmpty)
}

func testUseTraitCollectionsConfigWithTrue() {
let useTraitCollectionsConfig = UseTraitCollectionsConfig(enabled: true)
let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig)
let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig))

let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo)
XCTAssertEqual(noViolations.count, 1)

let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes)
XCTAssertTrue(yesViolations.isEmpty)
}

func testUseTraitCollectionsConfigWithFalse() {
let useTraitCollectionsConfig = UseTraitCollectionsConfig(enabled: false)
let ibLinterConfig = Config(useTraitCollectionsRule: useTraitCollectionsConfig)
let rule = Rules.UseTraitCollections(context: .mock(from: ibLinterConfig))

let noViolations = rule.validate(storyboard: storyboardUseTraitCollectionsNo)
XCTAssertTrue(noViolations.isEmpty)

let yesViolations = rule.validate(storyboard: storyboardUseTraitCollectionsYes)
XCTAssertEqual(yesViolations.count, 1)
}
}