Skip to content

Add Codable conformance to common CG types #10343

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

Merged
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
83 changes: 83 additions & 0 deletions stdlib/public/SDK/CoreGraphics/CoreGraphics.swift
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,21 @@ extension CGPoint : Equatable {
}
}

extension CGPoint : Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let x = try container.decode(CGFloat.self)
let y = try container.decode(CGFloat.self)
self.init(x: x, y: y)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(x)
try container.encode(y)
}
}

public extension CGSize {
static var zero: CGSize {
@_transparent // @fragile
Expand Down Expand Up @@ -302,6 +317,21 @@ extension CGSize : Equatable {
}
}

extension CGSize : Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let width = try container.decode(CGFloat.self)
let height = try container.decode(CGFloat.self)
self.init(width: width, height: height)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(width)
try container.encode(height)
}
}

public extension CGVector {
static var zero: CGVector {
@_transparent // @fragile
Expand Down Expand Up @@ -332,6 +362,21 @@ extension CGVector : CustomDebugStringConvertible {
}
}

extension CGVector : Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let dx = try container.decode(CGFloat.self)
let dy = try container.decode(CGFloat.self)
self.init(dx: dx, dy: dy)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(dx)
try container.encode(dy)
}
}

public extension CGRect {
static var zero: CGRect {
@_transparent // @fragile
Expand Down Expand Up @@ -411,13 +456,51 @@ extension CGRect : Equatable {
}
}

extension CGRect : Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let origin = try container.decode(CGPoint.self)
let size = try container.decode(CGSize.self)
self.init(origin: origin, size: size)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(origin)
try container.encode(size)
}
}

extension CGAffineTransform {
public static var identity: CGAffineTransform {
@_transparent // @fragile
get { return CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0) }
}
}

extension CGAffineTransform : Codable {
public init(from decoder: Decoder) throws {
var container = try decoder.unkeyedContainer()
let a = try container.decode(CGFloat.self)
let b = try container.decode(CGFloat.self)
let c = try container.decode(CGFloat.self)
let d = try container.decode(CGFloat.self)
let tx = try container.decode(CGFloat.self)
let ty = try container.decode(CGFloat.self)
self.init(a: a, b: b, c: c, d: d, tx: tx, ty: ty)
}

public func encode(to encoder: Encoder) throws {
var container = encoder.unkeyedContainer()
try container.encode(a)
try container.encode(b)
try container.encode(c)
try container.encode(d)
try container.encode(tx)
try container.encode(ty)
}
}

//===----------------------------------------------------------------------===//
// CGImage
//===----------------------------------------------------------------------===//
Expand Down
169 changes: 167 additions & 2 deletions test/stdlib/CodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// REQUIRES: objc_interop

import Foundation
import CoreGraphics

#if FOUNDATION_XCTEST
import XCTest
Expand Down Expand Up @@ -57,12 +58,21 @@ func expectRoundTripEquality<T : Codable>(of value: T, encode: (T) throws -> Dat
}

func expectRoundTripEqualityThroughJSON<T : Codable>(for value: T) where T : Equatable {
let inf = "INF", negInf = "-INF", nan = "NaN"
let encode = { (_ value: T) throws -> Data in
return try JSONEncoder().encode(value)
let encoder = JSONEncoder()
encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: inf,
negativeInfinity: negInf,
nan: nan)
return try encoder.encode(value)
}

let decode = { (_ data: Data) throws -> T in
return try JSONDecoder().decode(T.self, from: data)
let decoder = JSONDecoder()
decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: inf,
negativeInfinity: negInf,
nan: nan)
return try decoder.decode(T.self, from: data)
}

expectRoundTripEquality(of: value, encode: encode, decode: decode)
Expand Down Expand Up @@ -186,6 +196,151 @@ class TestCodable : TestCodableSuper {
}
}

// MARK: - CGAffineTransform
lazy var cg_affineTransformValues: [CGAffineTransform] = {
var values = [
CGAffineTransform.identity,
CGAffineTransform(),
CGAffineTransform(translationX: 2.0, y: 2.0),
CGAffineTransform(scaleX: 2.0, y: 2.0),
CGAffineTransform(a: 1.0, b: 2.5, c: 66.2, d: 40.2, tx: -5.5, ty: 3.7),
CGAffineTransform(a: -55.66, b: 22.7, c: 1.5, d: 0.0, tx: -22, ty: -33),
CGAffineTransform(a: 4.5, b: 1.1, c: 0.025, d: 0.077, tx: -0.55, ty: 33.2),
CGAffineTransform(a: 7.0, b: -2.3, c: 6.7, d: 0.25, tx: 0.556, ty: 0.99),
CGAffineTransform(a: 0.498, b: -0.284, c: -0.742, d: 0.3248, tx: 12, ty: 44)
]

if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
values.append(CGAffineTransform(rotationAngle: .pi / 2))
}

return values
}()

func test_CGAffineTransform_JSON() {
for transform in cg_affineTransformValues {
expectRoundTripEqualityThroughJSON(for: transform)
}
}

func test_CGAffineTransform_Plist() {
for transform in cg_affineTransformValues {
expectRoundTripEqualityThroughPlist(for: transform)
}
}

// MARK: - CGPoint
lazy var cg_pointValues: [CGPoint] = {
var values = [
CGPoint.zero,
CGPoint(x: 10, y: 20)
]

if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
// Limit on magnitude in JSON. See rdar://problem/12717407
values.append(CGPoint(x: CGFloat.greatestFiniteMagnitude,
y: CGFloat.greatestFiniteMagnitude))
}

return values
}()

func test_CGPoint_JSON() {
for point in cg_pointValues {
expectRoundTripEqualityThroughJSON(for: point)
}
}

func test_CGPoint_Plist() {
for point in cg_pointValues {
expectRoundTripEqualityThroughPlist(for: point)
}
}

// MARK: - CGSize
lazy var cg_sizeValues: [CGSize] = {
var values = [
CGSize.zero,
CGSize(width: 30, height: 40)
]

if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
// Limit on magnitude in JSON. See rdar://problem/12717407
values.append(CGSize(width: CGFloat.greatestFiniteMagnitude,
height: CGFloat.greatestFiniteMagnitude))
}

return values
}()

func test_CGSize_JSON() {
for size in cg_sizeValues {
expectRoundTripEqualityThroughJSON(for: size)
}
}

func test_CGSize_Plist() {
for size in cg_sizeValues {
expectRoundTripEqualityThroughPlist(for: size)
}
}

// MARK: - CGRect
lazy var cg_rectValues: [CGRect] = {
var values = [
CGRect.zero,
CGRect.null,
CGRect(x: 10, y: 20, width: 30, height: 40)
]

if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
// Limit on magnitude in JSON. See rdar://problem/12717407
values.append(CGRect.infinite)
}

return values
}()

func test_CGRect_JSON() {
for rect in cg_rectValues {
expectRoundTripEqualityThroughJSON(for: rect)
}
}

func test_CGRect_Plist() {
for rect in cg_rectValues {
expectRoundTripEqualityThroughPlist(for: rect)
}
}

// MARK: - CGVector
lazy var cg_vectorValues: [CGVector] = {
var values = [
CGVector.zero,
CGVector(dx: 0.0, dy: -9.81)
]

if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
// Limit on magnitude in JSON. See rdar://problem/12717407
values.append(CGVector(dx: CGFloat.greatestFiniteMagnitude,
dy: CGFloat.greatestFiniteMagnitude))
}

return values
}()

func test_CGVector_JSON() {
for vector in cg_vectorValues {
expectRoundTripEqualityThroughJSON(for: vector)
}
}

func test_CGVector_Plist() {
for vector in cg_vectorValues {
expectRoundTripEqualityThroughPlist(for: vector)
}
}

// MARK: - DateComponents
lazy var dateComponents: Set<Calendar.Component> = [
.era, .year, .month, .day, .hour, .minute, .second, .nanosecond,
Expand Down Expand Up @@ -458,6 +613,16 @@ CodableTests.test("test_Calendar_JSON") { TestCodable().test_Calendar_JSON() }
CodableTests.test("test_Calendar_Plist") { TestCodable().test_Calendar_Plist() }
CodableTests.test("test_CharacterSet_JSON") { TestCodable().test_CharacterSet_JSON() }
CodableTests.test("test_CharacterSet_Plist") { TestCodable().test_CharacterSet_Plist() }
CodableTests.test("test_CGAffineTransform_JSON") { TestCodable().test_CGAffineTransform_JSON() }
CodableTests.test("test_CGAffineTransform_Plist") { TestCodable().test_CGAffineTransform_Plist() }
CodableTests.test("test_CGPoint_JSON") { TestCodable().test_CGPoint_JSON() }
CodableTests.test("test_CGPoint_Plist") { TestCodable().test_CGPoint_Plist() }
CodableTests.test("test_CGSize_JSON") { TestCodable().test_CGSize_JSON() }
CodableTests.test("test_CGSize_Plist") { TestCodable().test_CGSize_Plist() }
CodableTests.test("test_CGRect_JSON") { TestCodable().test_CGRect_JSON() }
CodableTests.test("test_CGRect_Plist") { TestCodable().test_CGRect_Plist() }
CodableTests.test("test_CGVector_JSON") { TestCodable().test_CGVector_JSON() }
CodableTests.test("test_CGVector_Plist") { TestCodable().test_CGVector_Plist() }
CodableTests.test("test_DateComponents_JSON") { TestCodable().test_DateComponents_JSON() }
CodableTests.test("test_DateComponents_Plist") { TestCodable().test_DateComponents_Plist() }

Expand Down