Skip to content

Commit

Permalink
Add AccessibilityBoundedNumber.swift implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
Kyle-Ye committed Dec 2, 2023
1 parent e00092b commit 5e8382c
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//
// AccessibilityBoundedNumber.swift
// OpenSwiftUI
//
// Created by Kyle on 2023/12/2.
// Lastest Version: iOS 15.5
// Status: WIP
// ID: 333660CD735494DA92CEC2878E6C8CC5

import Foundation

struct AccessibilityBoundedNumber {
var number: AccessibilityNumber
var lowerBound: AccessibilityNumber?
var upperBound: AccessibilityNumber?
var stride: AccessibilityNumber?

// TODO
init?<S: Strideable>(for value: S, in range: ClosedRange<S>?, by stride: S.Stride?) {
return nil
}
}

extension AccessibilityBoundedNumber: AccessibilityValue {
// This kind of description logic is very strange
// But that's how Apple's implementation even on iOS 17 :)
// eg.
// For 1.5 and [1.0, 2.0], the accessiblity output would be 150%
// For 1.5 and [1.3, 2.3], the accessiblity output would be 1.5
var localizedDescription: String? {
let range: Double = if let lowerBound, let upperBound {
upperBound.base.doubleValue - lowerBound.base.doubleValue
} else {
.zero
}
if abs(range - 100) >= .ulpOfOne {
let style: NumberFormatter.Style = (abs(range - 1.0) < .ulpOfOne) ? .percent : .decimal
return NumberFormatter.localizedString(from: number.base, number: style)
} else {
return NumberFormatter.localizedString(from: NSNumber(value: number.base.doubleValue / 100), number: .percent)
}
}

var displayDescription: String? {
localizedDescription
}

var value: NSNumber { number.value }
var minValue: NSNumber? { lowerBound?.value }
var maxValue: NSNumber? { upperBound?.value }
static var type: AnyAccessibilityValueType { .boundedNumber }
}

extension AccessibilityBoundedNumber: Codable {
private enum CodingKeys: CodingKey {
case number
case lowerBound
case upperBound
case stride
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
number = try container.decode(AccessibilityNumber.self, forKey: .number)
lowerBound = try container.decodeIfPresent(AccessibilityNumber.self, forKey: .lowerBound)
upperBound = try container.decodeIfPresent(AccessibilityNumber.self, forKey: .upperBound)
stride = try container.decodeIfPresent(AccessibilityNumber.self, forKey: .lowerBound)
}

func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(number, forKey: .number)
try container.encodeIfPresent(lowerBound, forKey: .lowerBound)
try container.encodeIfPresent(upperBound, forKey: .upperBound)
try container.encodeIfPresent(stride, forKey: .stride)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ struct AccessibilityNumber {
}

extension AccessibilityNumber: AccessibilityValue {
var localizedDescription: String? {
NumberFormatter.localizedString(from: value, number: .decimal)
}
var displayDescription: String? { localizedDescription }
var value: NSNumber { base }
var minValue: NSNumber? { nil }
var maxValue: NSNumber? { nil }
static var type: AnyAccessibilityValueType { .number }
}

extension AccessibilityNumber: ExpressibleByFloatLiteral {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@ protocol AccessibilityValue: Equatable {
static var type: AnyAccessibilityValueType { get }
}

extension AccessibilityValue where PlatformValue: NSNumber {
var localizedDescription: String? {
NumberFormatter.localizedString(from: value, number: .decimal)
}

var displayDescription: String? {
NumberFormatter.localizedString(from: value, number: .decimal)
}

var minValue: NSNumber? { nil }
var maxValue: NSNumber? { nil }
extension AccessibilityValue {
var step: NSNumber? { nil }
static var type: AnyAccessibilityValueType { .number }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// AccessibilityBoundedNumberTests.swift
//
//
// Created by Kyle on 2023/12/3.
//

@testable import OpenSwiftUI
import XCTest

final class AccessibilityBoundedNumberTests: XCTestCase {
func testBoundedNumberLocalizedDescription() throws {
if let boundedNumber = AccessibilityBoundedNumber(for: 4.5, in: 3.0...16.0, by: 0.1) {
XCTAssertEqual(boundedNumber.localizedDescription, "4.503") //decimal case
} else {
XCTFail("Failed to init bounded number")
}
if let boundedNumber = AccessibilityBoundedNumber(for: 4.5, in: 1.0...101.0, by: 0.1) {
XCTAssertEqual(boundedNumber.localizedDescription, "5%") // .percent case
} else {
XCTFail("Failed to init bounded number")
}
if let boundedNumber = AccessibilityBoundedNumber(for: 1.5, in: 1.3...2.3, by: 0.1) {
XCTAssertEqual(boundedNumber.localizedDescription, "1.5") // .decimal case
} else {
XCTFail("Failed to init bounded number")
}
if let boundedNumber = AccessibilityBoundedNumber(for: 1.5, in: 1.0...2.0, by: 0.1) {
XCTAssertEqual(boundedNumber.localizedDescription, "150%") // .percent case
} else {
XCTFail("Failed to init bounded number")
}
}
}

0 comments on commit 5e8382c

Please sign in to comment.