Skip to content

Commit

Permalink
Adding in parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
Erik Sargent committed Aug 22, 2016
1 parent 442ace2 commit 32814a1
Show file tree
Hide file tree
Showing 4 changed files with 218 additions and 5 deletions.
4 changes: 4 additions & 0 deletions Example/LKAPI.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
/* Begin PBXBuildFile section */
607FACEC1AFB9204008FA782 /* APITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* APITests.swift */; };
6FBF4E879B7EC7D0D10759FB /* Pods_LKAPI_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7DDE5DACBF32E6149F164429 /* Pods_LKAPI_Tests.framework */; };
BFFB61AC1D6B684600E7B5C3 /* ParsingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFFB61AB1D6B684600E7B5C3 /* ParsingTests.swift */; };
/* End PBXBuildFile section */

/* Begin PBXFileReference section */
Expand All @@ -21,6 +22,7 @@
7DDE5DACBF32E6149F164429 /* Pods_LKAPI_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_LKAPI_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
8642178C40AD8705BC647625 /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = "<group>"; };
AF213812B10805C7C6D522F1 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = "<group>"; };
BFFB61AB1D6B684600E7B5C3 /* ParsingTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ParsingTests.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -75,6 +77,7 @@
isa = PBXGroup;
children = (
607FACEB1AFB9204008FA782 /* APITests.swift */,
BFFB61AB1D6B684600E7B5C3 /* ParsingTests.swift */,
607FACE91AFB9204008FA782 /* Supporting Files */,
);
path = Tests;
Expand Down Expand Up @@ -218,6 +221,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
BFFB61AC1D6B684600E7B5C3 /* ParsingTests.swift in Sources */,
607FACEC1AFB9204008FA782 /* APITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
8 changes: 3 additions & 5 deletions Example/Tests/APITests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -134,12 +134,10 @@ enum Router: Routable {
}

var parameters: [String : AnyObject]? {
switch self {
case .PostTest(let data):
if case .PostTest(let data) = self {
return data

default:
return nil
}

return nil
}
}
158 changes: 158 additions & 0 deletions Example/Tests/ParsingTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//
// ParsingTests.swift
// LKAPI
//
// Created by Erik Sargent on 8/22/16.
// Copyright © 2016 CocoaPods. All rights reserved.
//

import UIKit
import XCTest
import LKAPI

import Alamofire

class ParsingTests: XCTestCase {
func testParseLiteralsFromDictionary() {
let dict: ModelDict = [
"int": 5,
"bool": true,
"double": 5.5,
"string": "Hello world!"
]

XCTAssertEqual(dict.parse("int", or: 0), 5)
XCTAssertEqual(dict.parse("bool", or: false), true)
XCTAssertEqual(dict.parse("double", or: 0.0), 5.5)
XCTAssertEqual(dict.parse("string", or: ""), "Hello world!")
}

func testParseEmptyLiteralsFromDictionary() {
let dict: ModelDict = [:]

XCTAssertEqual(dict.parse("int", or: 0), 0)
XCTAssertEqual(dict.parse("bool", or: false), false)
XCTAssertEqual(dict.parse("double", or: 0.0), 0.0)
XCTAssertEqual(dict.parse("string", or: ""), "")
}

func testParseModelType() {
struct Type: ModelType {
let id: Int
let name: String

init(data: ModelDict) {
id = data.parse("id", or: 0)
name = data.parse("name", or: "")
}
}

let dict: ModelDict = [
"id": 3,
"name": "Test"
]

let parsed = dict.parse(Type)

XCTAssertEqual(parsed?.id, 3)
XCTAssertEqual(parsed?.name, "Test")
}

func testParseNestedModelType() {
struct ObjectType: ModelType {
let id: Int
let name: String

init(data: ModelDict) {
id = data.parse("id", or: 0)
name = data.parse("name", or: "")
}
}

let dict: ModelDict = [
"object": [
"id": 3,
"name": "Test"
]
]

let parsed = dict.parse("object", type: ObjectType.self)

XCTAssertEqual(parsed?.id, 3)
XCTAssertEqual(parsed?.name, "Test")
}

func testParseParseableDate() {
let dict: ModelDict = [
"date": "2222-03-06"
]

let parsedDate = dict.parse("date", type: NSDate.self)

XCTAssertNotNil(parsedDate)
guard let date = parsedDate else {
XCTFail()
return
}

let calendar = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian)
let components = calendar?.components([.Year, .Month, .Day], fromDate: date)

XCTAssertEqual(components?.year, 2222)
XCTAssertEqual(components?.month, 3)
XCTAssertEqual(components?.day, 6)
}

func testNestedModal() {
let otherDict: ModelDict = [
"id": 12
]

let dict: ModelDict = [
"id": 3,
"name": "Test",
"otherModel": otherDict
]

struct Modal: ModelType {
let id: Int
let name: String
let other: OtherModel?

init(data: ModelDict) {
id = data.parse("id", or: 0)
name = data.parse("name", or: "")
other = data.parse("otherModel", type: OtherModel.self)
}
}

struct OtherModel: ModelType {
let id: Int

init(data: ModelDict) {
id = data.parse("id", or: 0)
}
}

let data = dict.parse(Modal)

XCTAssertNotNil(data)
XCTAssertEqual(data?.id, 3)
XCTAssertEqual(data?.name, "Test")
XCTAssertNotNil(data?.other)
XCTAssertEqual(data?.other?.id, 12)
}
}

extension NSDate: Parseable {
public static func parse(data: AnyObject) -> Parseable? {
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"

if let dateString = data as? String, date = dateFormatter.dateFromString(dateString) {
return date
}

return nil
}
}
53 changes: 53 additions & 0 deletions Pod/Classes/API.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,56 @@ public extension API {
}
}

///Modal dictionary from the server
public typealias ModelDict = [String: AnyObject]

///Type that can be parsed from JSON
public protocol Parseable {
static func parse(data: AnyObject) -> Parseable?
}

///Type that can be parsed from a ModelDict
public protocol ModelType: Parseable {
init(data: ModelDict)
static func parse(data: AnyObject) -> Parseable?
}

extension ModelType {
public static func parse(data: AnyObject) -> Parseable? {
if let data = data as? ModelDict {
return self.init(data: data)
}

return nil
}
}

public extension Dictionary where Key: StringLiteralConvertible, Value: AnyObject {
///Try to parse an object out of the dictionary, and return the fallback if it fails
public func parse<T>(key: String, or fallback: T) -> T {
if let dict = (self as? AnyObject) as? [String: AnyObject], object = dict[key] as? T {
return object
}

return fallback
}

///Parse the field as a Parseable
public func parse<T where T: Parseable>(key: String, type: T.Type) -> T? {
if let dict = (self as? AnyObject) as? [String: AnyObject], object = dict[key] {
return T.parse(object) as? T
}

return nil
}

///Parse the dictionary as a ModelType
public func parse<T where T: ModelType>(type: T.Type) -> T? {
if let dict = (self as? AnyObject) as? ModelDict {
return T(data: dict)
}

return nil
}
}

0 comments on commit 32814a1

Please sign in to comment.