mirror of
https://github.com/wnagrodzki/UserInterfaceVariations.git
synced 2025-05-03 17:21:40 +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