mirror of
https://github.com/wnagrodzki/UserInterfaceVariations.git
synced 2025-05-06 18:51:42 +02:00
Add UIVariation, UIVariationApplying and unit tests
This commit is contained in:
parent
7886f972fe
commit
008ae67343
3 changed files with 232 additions and 0 deletions
90
Sources/UserInterfaceVariations/UIVariation.swift
Normal file
90
Sources/UserInterfaceVariations/UIVariation.swift
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
public enum SizeClassDimension {
|
||||||
|
case horizontal, vertical
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UIVariation<Object: AnyObject, Value>: NSObject {
|
||||||
|
|
||||||
|
public weak var traitEnvironment: UITraitEnvironment?
|
||||||
|
|
||||||
|
public let object: Object
|
||||||
|
public let property: ReferenceWritableKeyPath<Object, Value>
|
||||||
|
public var value: Value {
|
||||||
|
didSet { applyIfMatchesTraitEnvironment() }
|
||||||
|
}
|
||||||
|
public let horizontalSizeClass: UIUserInterfaceSizeClass?
|
||||||
|
public let verticalSizeClass: UIUserInterfaceSizeClass?
|
||||||
|
|
||||||
|
public static func make(for object: Object,
|
||||||
|
property: ReferenceWritableKeyPath<Object, Value>,
|
||||||
|
sizeClassDimension: SizeClassDimension,
|
||||||
|
whenCompact: Value,
|
||||||
|
whenRegular: Value) -> [UIVariation<Object, Value>] {
|
||||||
|
switch sizeClassDimension {
|
||||||
|
case .horizontal:
|
||||||
|
return [
|
||||||
|
UIVariation<Object, Value>(object: object, keyPath: property, value: whenCompact, horizontalSizeClass: .compact, verticalSizeClass: nil),
|
||||||
|
UIVariation<Object, Value>(object: object, keyPath: property, value: whenRegular, horizontalSizeClass: .regular, verticalSizeClass: nil),
|
||||||
|
]
|
||||||
|
case .vertical:
|
||||||
|
return [
|
||||||
|
UIVariation<Object, Value>(object: object, keyPath: property, value: whenCompact, horizontalSizeClass: nil, verticalSizeClass: .compact),
|
||||||
|
UIVariation<Object, Value>(object: object, keyPath: property, value: whenRegular, horizontalSizeClass: nil, verticalSizeClass: .regular),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(object: Object,
|
||||||
|
keyPath: ReferenceWritableKeyPath<Object, Value>,
|
||||||
|
value: Value,
|
||||||
|
horizontalSizeClass: UIUserInterfaceSizeClass? = nil,
|
||||||
|
verticalSizeClass: UIUserInterfaceSizeClass? = nil) {
|
||||||
|
self.object = object
|
||||||
|
self.property = keyPath
|
||||||
|
self.value = value
|
||||||
|
self.horizontalSizeClass = horizontalSizeClass
|
||||||
|
self.verticalSizeClass = verticalSizeClass
|
||||||
|
}
|
||||||
|
|
||||||
|
public override var description: String {
|
||||||
|
"""
|
||||||
|
<Variation<\(Object.self), \(Value.self): \(Unmanaged.passUnretained(self).toOpaque()))> {
|
||||||
|
object: \(object)
|
||||||
|
keyPath: \(property._kvcKeyPathString ?? String(describing: property))
|
||||||
|
value: \(value)
|
||||||
|
horizontalSizeClass: \(horizontalSizeClass?.name ?? "nil")
|
||||||
|
verticalSizeClass: \(verticalSizeClass?.name ?? "nil")
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIVariation: UIVariationApplying {
|
||||||
|
|
||||||
|
func applyIfMatchesTraitEnvironment() {
|
||||||
|
guard let traitCollection = traitEnvironment?.traitCollection else {
|
||||||
|
print("Missing traitEnvironment when trying to apply \(self)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if let horizontalSizeClass = horizontalSizeClass {
|
||||||
|
if traitCollection.horizontalSizeClass != horizontalSizeClass { return }
|
||||||
|
}
|
||||||
|
if let verticalSizeClass = verticalSizeClass {
|
||||||
|
if traitCollection.verticalSizeClass != verticalSizeClass { return }
|
||||||
|
}
|
||||||
|
object[keyPath: property] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIUserInterfaceSizeClass {
|
||||||
|
|
||||||
|
fileprivate var name: String {
|
||||||
|
switch self {
|
||||||
|
case .unspecified: return "unspecified"
|
||||||
|
case .compact: return "compact"
|
||||||
|
case .regular: return "regular"
|
||||||
|
@unknown default: return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
protocol UIVariationApplying: NSObject {
|
||||||
|
|
||||||
|
func applyIfMatchesTraitEnvironment()
|
||||||
|
}
|
136
Tests/UserInterfaceVariationsTests/UIVariationTests.swift
Normal file
136
Tests/UserInterfaceVariationsTests/UIVariationTests.swift
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import XCTest
|
||||||
|
@testable import UserInterfaceVariations
|
||||||
|
|
||||||
|
final class UIVariationTests: XCTestCase {
|
||||||
|
|
||||||
|
let label = UILabel()
|
||||||
|
let traitEnvironment = TraitEnvironmentStub()
|
||||||
|
let valueWhenCompact = "value when compact"
|
||||||
|
let valueWhenRegular = "value when regular"
|
||||||
|
|
||||||
|
override func setUp() {
|
||||||
|
label.text = nil
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection()
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_horizontalSizeClass_matches_traitEnvironment_Then_variation_is_applied() {
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: valueWhenCompact,
|
||||||
|
horizontalSizeClass: .compact,
|
||||||
|
verticalSizeClass: nil)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(horizontalSizeClass: .compact)
|
||||||
|
sut.traitEnvironment = traitEnvironment
|
||||||
|
sut.applyIfMatchesTraitEnvironment()
|
||||||
|
XCTAssertEqual(label.text, sut.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_verticalSizeClass_matches_traitEnvironment_Then_variation_is_applied() {
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: valueWhenCompact,
|
||||||
|
horizontalSizeClass: nil,
|
||||||
|
verticalSizeClass: .compact)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(verticalSizeClass: .compact)
|
||||||
|
sut.traitEnvironment = traitEnvironment
|
||||||
|
sut.applyIfMatchesTraitEnvironment()
|
||||||
|
XCTAssertEqual(label.text, sut.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_horizontalSizeClass_do_not_match_traitEnvironment_Then_variation_is_NOT_applied() {
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: valueWhenCompact,
|
||||||
|
horizontalSizeClass: .compact,
|
||||||
|
verticalSizeClass: nil)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(horizontalSizeClass: .regular)
|
||||||
|
sut.traitEnvironment = traitEnvironment
|
||||||
|
sut.applyIfMatchesTraitEnvironment()
|
||||||
|
XCTAssertNotEqual(label.text, sut.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_verticalSizeClass_do_not_match_traitEnvironment_Then_variation_is_NOT_applied() {
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: valueWhenCompact,
|
||||||
|
horizontalSizeClass: nil,
|
||||||
|
verticalSizeClass: .compact)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(verticalSizeClass: .regular)
|
||||||
|
sut.traitEnvironment = traitEnvironment
|
||||||
|
sut.applyIfMatchesTraitEnvironment()
|
||||||
|
XCTAssertNotEqual(label.text, sut.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_horizontalSizeClass_matches_traitEnvironment_and_value_is_modified_Then_variation_is_applied() {
|
||||||
|
let newValue = "value when compact after modification"
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: "value when compact before modification",
|
||||||
|
horizontalSizeClass: .compact,
|
||||||
|
verticalSizeClass: nil)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(horizontalSizeClass: .compact)
|
||||||
|
sut.traitEnvironment = traitEnvironment
|
||||||
|
sut.value = newValue
|
||||||
|
XCTAssertEqual(label.text, newValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_When_traitEnvironment_is_nil_Then_variation_is_NOT_applied() {
|
||||||
|
let sut = UIVariation(object: label,
|
||||||
|
keyPath: \.text,
|
||||||
|
value: valueWhenCompact,
|
||||||
|
horizontalSizeClass: .compact,
|
||||||
|
verticalSizeClass: nil)
|
||||||
|
traitEnvironment.traitCollection = UITraitCollection(horizontalSizeClass: .compact)
|
||||||
|
sut.applyIfMatchesTraitEnvironment()
|
||||||
|
XCTAssertNotEqual(label.text, sut.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_factory_method_for_sizeClassDimension_horizontal() {
|
||||||
|
let variations = UIVariation.make(for: label,
|
||||||
|
property: \.text,
|
||||||
|
sizeClassDimension: .horizontal,
|
||||||
|
whenCompact: valueWhenCompact,
|
||||||
|
whenRegular: valueWhenRegular)
|
||||||
|
|
||||||
|
XCTAssertTrue(variations[0].object === label)
|
||||||
|
XCTAssertTrue(variations[0].property == \.text)
|
||||||
|
XCTAssertTrue(variations[0].value == valueWhenCompact)
|
||||||
|
XCTAssertTrue(variations[0].horizontalSizeClass == .compact)
|
||||||
|
XCTAssertTrue(variations[0].verticalSizeClass == nil)
|
||||||
|
|
||||||
|
XCTAssertTrue(variations[1].object === label)
|
||||||
|
XCTAssertTrue(variations[1].property == \.text)
|
||||||
|
XCTAssertTrue(variations[1].value == valueWhenRegular)
|
||||||
|
XCTAssertTrue(variations[1].horizontalSizeClass == .regular)
|
||||||
|
XCTAssertTrue(variations[1].verticalSizeClass == nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func test_factory_method_for_sizeClassDimension_vertical() {
|
||||||
|
let variations = UIVariation.make(for: label,
|
||||||
|
property: \.text,
|
||||||
|
sizeClassDimension: .vertical,
|
||||||
|
whenCompact: valueWhenCompact,
|
||||||
|
whenRegular: valueWhenRegular)
|
||||||
|
|
||||||
|
XCTAssertTrue(variations[0].object === label)
|
||||||
|
XCTAssertTrue(variations[0].property == \.text)
|
||||||
|
XCTAssertTrue(variations[0].value == valueWhenCompact)
|
||||||
|
XCTAssertTrue(variations[0].horizontalSizeClass == nil)
|
||||||
|
XCTAssertTrue(variations[0].verticalSizeClass == .compact)
|
||||||
|
|
||||||
|
XCTAssertTrue(variations[1].object === label)
|
||||||
|
XCTAssertTrue(variations[1].property == \.text)
|
||||||
|
XCTAssertTrue(variations[1].value == valueWhenRegular)
|
||||||
|
XCTAssertTrue(variations[1].horizontalSizeClass == nil)
|
||||||
|
XCTAssertTrue(variations[1].verticalSizeClass == .regular)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TraitEnvironmentStub: NSObject, UITraitEnvironment {
|
||||||
|
|
||||||
|
var traitCollection = UITraitCollection()
|
||||||
|
|
||||||
|
func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue