Skip to content

Commit

Permalink
Add AbstractAnyAccessibilityValue
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Dec 3, 2023
1 parent 5e8382c commit 12f65b5
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,90 @@

import Foundation

// MARK: - AbstractAnyAccessibilityValue

private protocol AbstractAnyAccessibilityValue: Codable {
var localizedDescription: String? { get }
var displayDescription: String? { get }
var value: Any { get }
var minValue: Any? { get }
var maxValue: Any? { get }
var step: Any? { get }
var type: AnyAccessibilityValueType { get }
func `as`<Value: AccessibilityValue>(_ type: Value.Type) -> Value?
func isEqual(to value: AbstractAnyAccessibilityValue) -> Bool
}

// MARK: - AnyAccessibilityValue

struct AnyAccessibilityValue/*: AbstractAnyAccessibilityValue*/ {
private var base: AbstractAnyAccessibilityValue

init<Value: Codable & AccessibilityValue>(_ base: Value) {
self.base = ConcreteBase(base: base)
}
}

extension AnyAccessibilityValue: Equatable {
static func == (lhs: AnyAccessibilityValue, rhs: AnyAccessibilityValue) -> Bool {
lhs.base.isEqual(to: rhs.base)
}
}

extension AnyAccessibilityValue: Codable {
private enum Keys: CodingKey {
case type
case value
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Keys.self)
let type = try container.decode(AnyAccessibilityValueType.self, forKey: .type)
switch type {
case .int: base = try container.decode(ConcreteBase<Int>.self, forKey: .value)
case .double: base = try container.decode(ConcreteBase<Double>.self, forKey: .value)
case .bool: base = try container.decode(ConcreteBase<Bool>.self, forKey: .value)
case .string: base = try container.decode(ConcreteBase<String>.self, forKey: .value)
case .boundedNumber: base = try container.decode(ConcreteBase<AccessibilityBoundedNumber>.self, forKey: .value)
case .number: base = try container.decode(ConcreteBase<AccessibilityNumber>.self, forKey: .value)
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Keys.self)
try container.encode(base.type, forKey: .type)
try base.encode(to: container.superEncoder(forKey: .value))
}
}

// MARK: AnyAccessibilityValue.ConcreateBase

extension AnyAccessibilityValue {
fileprivate struct ConcreteBase<Base> where Base: Codable, Base: AccessibilityValue {
var base: Base
}
}

extension AnyAccessibilityValue.ConcreteBase: Codable {}
extension AnyAccessibilityValue.ConcreteBase: Equatable {}
extension AnyAccessibilityValue.ConcreteBase: AbstractAnyAccessibilityValue {
var localizedDescription: String? { base.localizedDescription }
var displayDescription: String? { base.displayDescription }
var value: Any { base.value }
var minValue: Any? { base.minValue }
var maxValue: Any? { base.maxValue }
var step: Any? { base.step }
var type: AnyAccessibilityValueType { Base.type }
func `as`<Value>(_ type: Value.Type) -> Value? where Value : AccessibilityValue {
base as? Value
}
func isEqual(to value: AbstractAnyAccessibilityValue) -> Bool {
base == (value as? Self)?.base
}
}

// MARK: - AccessibilityBoundedNumber

struct AccessibilityBoundedNumber {
var number: AccessibilityNumber
var lowerBound: AccessibilityNumber?
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//
// AccessibilityNumber.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/3.
// Lastest Version: iOS 15.5
// Status: Complete

import Foundation

protocol AccessibilityNumeric {
var isValidMinValue: Bool { get }
var isValidMaxValue: Bool { get }
func asNumber() -> AccessibilityNumber?
}

extension AccessibilityNumeric where Self: FixedWidthInteger {
var isValidMinValue: Bool {
// TODO: Add Unit Test and check usage
if Self.bitWidth == 8 || !Self.isSigned {
true
} else {
self != .min
}
}

var isValidMaxValue: Bool { self != .max }
}

extension Int: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int8: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int16: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int32: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Int64: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt8: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt16: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt32: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension UInt64: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension AccessibilityNumeric where Self: BinaryFloatingPoint {
var isValidMinValue: Bool {
isFinite && self > -Self.greatestFiniteMagnitude
}

var isValidMaxValue: Bool {
isFinite && self < Self.greatestFiniteMagnitude
}
}

extension Float: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}

extension Double: AccessibilityNumeric {
func asNumber() -> AccessibilityNumber? { AccessibilityNumber(base: .init(value: self)) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,5 +20,36 @@ protocol AccessibilityValue: Equatable {
}

extension AccessibilityValue {
var step: NSNumber? { nil }
var minValue: PlatformValue? { nil }
var maxValue: PlatformValue? { nil }
var step: PlatformValue? { nil }
}

extension AccessibilityValue where PlatformValue: CustomStringConvertible {
var localizedDescription: String? { value.description }
var displayDescription: String? { value.description }
}

extension AccessibilityValue where Self == Self.PlatformValue {
var value: PlatformValue { self }
}

extension Int: AccessibilityValue {
typealias PlatformValue = Int
static var type: AnyAccessibilityValueType { .int }
}

extension Double: AccessibilityValue {
typealias PlatformValue = Double
static var type: AnyAccessibilityValueType { .number }
}

extension Bool: AccessibilityValue {
typealias PlatformValue = Bool
static var type: AnyAccessibilityValueType { .bool }
}

extension String: AccessibilityValue {
typealias PlatformValue = String
static var type: AnyAccessibilityValueType { .string }
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
// Lastest Version: iOS 15.5
// Status: Complete

enum AnyAccessibilityValueType: UInt {
enum AnyAccessibilityValueType: UInt, Codable {
case int
case double
case bool
case string
case disclosure
case toggle
case slider
case stepper
case progress
// case disclosure
// case toggle
// case slider
// case stepper
// case progress
case boundedNumber
case headingLevel
// case headingLevel
case number
}
21 changes: 21 additions & 0 deletions Sources/OpenSwiftUI/Internal/Other/Comparable+Extension.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
extension Comparable {
func clamped(to range: ClosedRange<Self>) -> Self {
var value = self
value.clamp(to: range)
return value
}

mutating func clamp(to range: ClosedRange<Self>) {
self = OpenSwiftUI.clamp(self, min: range.lowerBound, max: range.upperBound)
}
}

func clamp<Value: Comparable>(_ value: Value, min minValue: Value, max maxValue: Value) -> Value {
if value < minValue {
minValue
} else if value > maxValue {
maxValue
} else {
value
}
}

0 comments on commit 12f65b5

Please sign in to comment.