Skip to content

Commit e0f75bf

Browse files
authored
Merge pull request #10343 from itaiferber/cg-types-codable-conformance
Add Codable conformance to common CG types
2 parents ad3c773 + fedf8e6 commit e0f75bf

File tree

2 files changed

+250
-2
lines changed

2 files changed

+250
-2
lines changed

stdlib/public/SDK/CoreGraphics/CoreGraphics.swift

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,21 @@ extension CGPoint : Equatable {
250250
}
251251
}
252252

253+
extension CGPoint : Codable {
254+
public init(from decoder: Decoder) throws {
255+
var container = try decoder.unkeyedContainer()
256+
let x = try container.decode(CGFloat.self)
257+
let y = try container.decode(CGFloat.self)
258+
self.init(x: x, y: y)
259+
}
260+
261+
public func encode(to encoder: Encoder) throws {
262+
var container = encoder.unkeyedContainer()
263+
try container.encode(x)
264+
try container.encode(y)
265+
}
266+
}
267+
253268
public extension CGSize {
254269
static var zero: CGSize {
255270
@_transparent // @fragile
@@ -302,6 +317,21 @@ extension CGSize : Equatable {
302317
}
303318
}
304319

320+
extension CGSize : Codable {
321+
public init(from decoder: Decoder) throws {
322+
var container = try decoder.unkeyedContainer()
323+
let width = try container.decode(CGFloat.self)
324+
let height = try container.decode(CGFloat.self)
325+
self.init(width: width, height: height)
326+
}
327+
328+
public func encode(to encoder: Encoder) throws {
329+
var container = encoder.unkeyedContainer()
330+
try container.encode(width)
331+
try container.encode(height)
332+
}
333+
}
334+
305335
public extension CGVector {
306336
static var zero: CGVector {
307337
@_transparent // @fragile
@@ -332,6 +362,21 @@ extension CGVector : CustomDebugStringConvertible {
332362
}
333363
}
334364

365+
extension CGVector : Codable {
366+
public init(from decoder: Decoder) throws {
367+
var container = try decoder.unkeyedContainer()
368+
let dx = try container.decode(CGFloat.self)
369+
let dy = try container.decode(CGFloat.self)
370+
self.init(dx: dx, dy: dy)
371+
}
372+
373+
public func encode(to encoder: Encoder) throws {
374+
var container = encoder.unkeyedContainer()
375+
try container.encode(dx)
376+
try container.encode(dy)
377+
}
378+
}
379+
335380
public extension CGRect {
336381
static var zero: CGRect {
337382
@_transparent // @fragile
@@ -411,13 +456,51 @@ extension CGRect : Equatable {
411456
}
412457
}
413458

459+
extension CGRect : Codable {
460+
public init(from decoder: Decoder) throws {
461+
var container = try decoder.unkeyedContainer()
462+
let origin = try container.decode(CGPoint.self)
463+
let size = try container.decode(CGSize.self)
464+
self.init(origin: origin, size: size)
465+
}
466+
467+
public func encode(to encoder: Encoder) throws {
468+
var container = encoder.unkeyedContainer()
469+
try container.encode(origin)
470+
try container.encode(size)
471+
}
472+
}
473+
414474
extension CGAffineTransform {
415475
public static var identity: CGAffineTransform {
416476
@_transparent // @fragile
417477
get { return CGAffineTransform(a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0) }
418478
}
419479
}
420480

481+
extension CGAffineTransform : Codable {
482+
public init(from decoder: Decoder) throws {
483+
var container = try decoder.unkeyedContainer()
484+
let a = try container.decode(CGFloat.self)
485+
let b = try container.decode(CGFloat.self)
486+
let c = try container.decode(CGFloat.self)
487+
let d = try container.decode(CGFloat.self)
488+
let tx = try container.decode(CGFloat.self)
489+
let ty = try container.decode(CGFloat.self)
490+
self.init(a: a, b: b, c: c, d: d, tx: tx, ty: ty)
491+
}
492+
493+
public func encode(to encoder: Encoder) throws {
494+
var container = encoder.unkeyedContainer()
495+
try container.encode(a)
496+
try container.encode(b)
497+
try container.encode(c)
498+
try container.encode(d)
499+
try container.encode(tx)
500+
try container.encode(ty)
501+
}
502+
}
503+
421504
//===----------------------------------------------------------------------===//
422505
// CGImage
423506
//===----------------------------------------------------------------------===//

test/stdlib/CodableTests.swift

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
// REQUIRES: objc_interop
1212

1313
import Foundation
14+
import CoreGraphics
1415

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

5960
func expectRoundTripEqualityThroughJSON<T : Codable>(for value: T) where T : Equatable {
61+
let inf = "INF", negInf = "-INF", nan = "NaN"
6062
let encode = { (_ value: T) throws -> Data in
61-
return try JSONEncoder().encode(value)
63+
let encoder = JSONEncoder()
64+
encoder.nonConformingFloatEncodingStrategy = .convertToString(positiveInfinity: inf,
65+
negativeInfinity: negInf,
66+
nan: nan)
67+
return try encoder.encode(value)
6268
}
6369

6470
let decode = { (_ data: Data) throws -> T in
65-
return try JSONDecoder().decode(T.self, from: data)
71+
let decoder = JSONDecoder()
72+
decoder.nonConformingFloatDecodingStrategy = .convertFromString(positiveInfinity: inf,
73+
negativeInfinity: negInf,
74+
nan: nan)
75+
return try decoder.decode(T.self, from: data)
6676
}
6777

6878
expectRoundTripEquality(of: value, encode: encode, decode: decode)
@@ -186,6 +196,151 @@ class TestCodable : TestCodableSuper {
186196
}
187197
}
188198

199+
// MARK: - CGAffineTransform
200+
lazy var cg_affineTransformValues: [CGAffineTransform] = {
201+
var values = [
202+
CGAffineTransform.identity,
203+
CGAffineTransform(),
204+
CGAffineTransform(translationX: 2.0, y: 2.0),
205+
CGAffineTransform(scaleX: 2.0, y: 2.0),
206+
CGAffineTransform(a: 1.0, b: 2.5, c: 66.2, d: 40.2, tx: -5.5, ty: 3.7),
207+
CGAffineTransform(a: -55.66, b: 22.7, c: 1.5, d: 0.0, tx: -22, ty: -33),
208+
CGAffineTransform(a: 4.5, b: 1.1, c: 0.025, d: 0.077, tx: -0.55, ty: 33.2),
209+
CGAffineTransform(a: 7.0, b: -2.3, c: 6.7, d: 0.25, tx: 0.556, ty: 0.99),
210+
CGAffineTransform(a: 0.498, b: -0.284, c: -0.742, d: 0.3248, tx: 12, ty: 44)
211+
]
212+
213+
if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
214+
values.append(CGAffineTransform(rotationAngle: .pi / 2))
215+
}
216+
217+
return values
218+
}()
219+
220+
func test_CGAffineTransform_JSON() {
221+
for transform in cg_affineTransformValues {
222+
expectRoundTripEqualityThroughJSON(for: transform)
223+
}
224+
}
225+
226+
func test_CGAffineTransform_Plist() {
227+
for transform in cg_affineTransformValues {
228+
expectRoundTripEqualityThroughPlist(for: transform)
229+
}
230+
}
231+
232+
// MARK: - CGPoint
233+
lazy var cg_pointValues: [CGPoint] = {
234+
var values = [
235+
CGPoint.zero,
236+
CGPoint(x: 10, y: 20)
237+
]
238+
239+
if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
240+
// Limit on magnitude in JSON. See rdar://problem/12717407
241+
values.append(CGPoint(x: CGFloat.greatestFiniteMagnitude,
242+
y: CGFloat.greatestFiniteMagnitude))
243+
}
244+
245+
return values
246+
}()
247+
248+
func test_CGPoint_JSON() {
249+
for point in cg_pointValues {
250+
expectRoundTripEqualityThroughJSON(for: point)
251+
}
252+
}
253+
254+
func test_CGPoint_Plist() {
255+
for point in cg_pointValues {
256+
expectRoundTripEqualityThroughPlist(for: point)
257+
}
258+
}
259+
260+
// MARK: - CGSize
261+
lazy var cg_sizeValues: [CGSize] = {
262+
var values = [
263+
CGSize.zero,
264+
CGSize(width: 30, height: 40)
265+
]
266+
267+
if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
268+
// Limit on magnitude in JSON. See rdar://problem/12717407
269+
values.append(CGSize(width: CGFloat.greatestFiniteMagnitude,
270+
height: CGFloat.greatestFiniteMagnitude))
271+
}
272+
273+
return values
274+
}()
275+
276+
func test_CGSize_JSON() {
277+
for size in cg_sizeValues {
278+
expectRoundTripEqualityThroughJSON(for: size)
279+
}
280+
}
281+
282+
func test_CGSize_Plist() {
283+
for size in cg_sizeValues {
284+
expectRoundTripEqualityThroughPlist(for: size)
285+
}
286+
}
287+
288+
// MARK: - CGRect
289+
lazy var cg_rectValues: [CGRect] = {
290+
var values = [
291+
CGRect.zero,
292+
CGRect.null,
293+
CGRect(x: 10, y: 20, width: 30, height: 40)
294+
]
295+
296+
if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
297+
// Limit on magnitude in JSON. See rdar://problem/12717407
298+
values.append(CGRect.infinite)
299+
}
300+
301+
return values
302+
}()
303+
304+
func test_CGRect_JSON() {
305+
for rect in cg_rectValues {
306+
expectRoundTripEqualityThroughJSON(for: rect)
307+
}
308+
}
309+
310+
func test_CGRect_Plist() {
311+
for rect in cg_rectValues {
312+
expectRoundTripEqualityThroughPlist(for: rect)
313+
}
314+
}
315+
316+
// MARK: - CGVector
317+
lazy var cg_vectorValues: [CGVector] = {
318+
var values = [
319+
CGVector.zero,
320+
CGVector(dx: 0.0, dy: -9.81)
321+
]
322+
323+
if #available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) {
324+
// Limit on magnitude in JSON. See rdar://problem/12717407
325+
values.append(CGVector(dx: CGFloat.greatestFiniteMagnitude,
326+
dy: CGFloat.greatestFiniteMagnitude))
327+
}
328+
329+
return values
330+
}()
331+
332+
func test_CGVector_JSON() {
333+
for vector in cg_vectorValues {
334+
expectRoundTripEqualityThroughJSON(for: vector)
335+
}
336+
}
337+
338+
func test_CGVector_Plist() {
339+
for vector in cg_vectorValues {
340+
expectRoundTripEqualityThroughPlist(for: vector)
341+
}
342+
}
343+
189344
// MARK: - DateComponents
190345
lazy var dateComponents: Set<Calendar.Component> = [
191346
.era, .year, .month, .day, .hour, .minute, .second, .nanosecond,
@@ -458,6 +613,16 @@ CodableTests.test("test_Calendar_JSON") { TestCodable().test_Calendar_JSON() }
458613
CodableTests.test("test_Calendar_Plist") { TestCodable().test_Calendar_Plist() }
459614
CodableTests.test("test_CharacterSet_JSON") { TestCodable().test_CharacterSet_JSON() }
460615
CodableTests.test("test_CharacterSet_Plist") { TestCodable().test_CharacterSet_Plist() }
616+
CodableTests.test("test_CGAffineTransform_JSON") { TestCodable().test_CGAffineTransform_JSON() }
617+
CodableTests.test("test_CGAffineTransform_Plist") { TestCodable().test_CGAffineTransform_Plist() }
618+
CodableTests.test("test_CGPoint_JSON") { TestCodable().test_CGPoint_JSON() }
619+
CodableTests.test("test_CGPoint_Plist") { TestCodable().test_CGPoint_Plist() }
620+
CodableTests.test("test_CGSize_JSON") { TestCodable().test_CGSize_JSON() }
621+
CodableTests.test("test_CGSize_Plist") { TestCodable().test_CGSize_Plist() }
622+
CodableTests.test("test_CGRect_JSON") { TestCodable().test_CGRect_JSON() }
623+
CodableTests.test("test_CGRect_Plist") { TestCodable().test_CGRect_Plist() }
624+
CodableTests.test("test_CGVector_JSON") { TestCodable().test_CGVector_JSON() }
625+
CodableTests.test("test_CGVector_Plist") { TestCodable().test_CGVector_Plist() }
461626
CodableTests.test("test_DateComponents_JSON") { TestCodable().test_DateComponents_JSON() }
462627
CodableTests.test("test_DateComponents_Plist") { TestCodable().test_DateComponents_Plist() }
463628

0 commit comments

Comments
 (0)