├── Aldwych2.playground
├── Pages
│ ├── Adding and replacing.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Creating JSON.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Editing Values.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Equatable.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── JSONParsing.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Retrieve Values.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Type Safety in JSONArray.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ ├── Type Safety in JSONDictionary.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
│ └── XMLParsing.xcplaygroundpage
│ │ ├── Contents.swift
│ │ └── timeline.xctimeline
├── Resources
│ ├── iTunes.json
│ └── styles.xml
├── Sources
│ ├── Additional Files
│ │ ├── ErrorHandling.swift
│ │ ├── JSONParser.swift
│ │ ├── NSNumberExtensions.swift
│ │ ├── TypeSafety.swift
│ │ └── XMLParser.swift
│ ├── JSONObjectTypes
│ │ ├── JSONArray.swift
│ │ └── JSONDictionary.swift
│ ├── JSONValue
│ │ ├── JSONValue.swift
│ │ ├── _ArrayExtensions.swift
│ │ ├── _DictionaryExtensions.swift
│ │ ├── _Equatable.swift
│ │ ├── _ExportExtensions.swift
│ │ ├── _InitializerExtensions.swift
│ │ └── _SequenceExtensions.swift
│ └── Protocols
│ │ ├── Array_Protocol.swift
│ │ ├── Dictionary_Protocol.swift
│ │ └── JSONType_Protocol.swift
├── contents.xcplayground
├── playground.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── Aldwych2.xcscmblueprint
│ └── xcuserdata
│ │ └── anthonylevings.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
└── xcuserdata
│ └── anthonylevings.xcuserdatad
│ └── xcdebugger
│ └── Breakpoints_v2.xcbkptlist
└── README.md
/Aldwych2.playground/Pages/Adding and replacing.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 |
6 | var jsonValue:JSONDictionary?
7 |
8 | do {
9 | jsonValue = try JSONParser.parse(fileNamed: "iTunes.json") as? JSONDictionary
10 | }
11 | catch let e {
12 | errorString(error: e)
13 | }
14 | /*:
15 | # Adding and replacing
16 | ## Dictionaries and Arrays
17 | For adding and replacing dictionaries and arrays currently use the methods updateValue(forKey:) and insert(atIndex:) respectively
18 | */
19 |
20 | if var json = jsonValue
21 | {
22 | // adding a dictionary to a dictionary
23 | json.updateValue(["results":10], forKey:"moreResults", typesafe: .Typesafe)
24 |
25 | // removing an object from an array
26 | json["results"]?.removeAtIndex(0)
27 | // adding an array to an array
28 | json["results"]?.insert(["FoundStuff","Found More Stuff"], atIndex:0)
29 |
30 | json.jsonData() // transform back into JSON data
31 | json.stringify() // stringify JSONValues
32 |
33 |
34 | }
35 |
36 | //: [Next](@next)
37 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Adding and replacing.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Creating JSON.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 | /*:
6 | # Creating JSON
7 | It's simple to create JSONArray, JSONDictionary and JSONValue instances, and in turn to create NSData from those instances for saving to disk or sending over a network.
8 | */
9 | let json = JSONArray(array: [0,"One","Two",3,4,5.0,true,false])
10 | let data = json.jsonData()
11 |
12 | // Demonstration that JSON data matches original values.
13 | if let d = data {
14 | NSString(data: d, encoding: NSUTF8StringEncoding)
15 | }
16 |
17 |
18 | //: [Next](@next)
19 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Creating JSON.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Editing Values.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 | var jsonValue:JSONDictionary?
5 |
6 | do {
7 | jsonValue = try JSONParser.parse(fileNamed: "iTunes.json") as? JSONDictionary
8 | }
9 | catch let e {
10 | errorString(error: e)
11 | }
12 | /*:
13 | # Editing values
14 | Now we have access to values we are also able to edit values
15 | */
16 |
17 | if var json = jsonValue
18 | {
19 | json["results"]?[0] == json["results"]?[0]
20 | json["results"]?[0]["artistName"]?.str // "Jack Johnson"
21 | json["results"]?[0]["artistName"] = "Music Man"
22 | json["results"]?[0]["artistName"]?.str // "Music Man"
23 | json["results"]?.removeLast()
24 | json["resultCount"] = json["results"]?.count
25 | /*:
26 | ## Extracting and subsetting JSON data
27 | But not only can we edit JSONValues we can also easily transform back into data or stringify all the data or a subset of it
28 | */
29 | json.jsonData() // transform back into JSON data
30 | json.stringify() // stringify JSONValues
31 |
32 | // transform a subarray into data or stringify
33 | json["results"]?.jsonData()
34 | json["results"]?.stringify()
35 |
36 | // extract data or stringify a nested dictionary
37 | json["results"]?[0].jsonData()
38 | json["results"]?[0].stringify()
39 |
40 | }
41 |
42 | //: [Next](@next)
43 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Editing Values.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Equatable.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 | /*:
6 | # Equatable
7 | A broad range of comparisons
8 | */
9 | JSONValue(value:"String") == JSONValue(value:"Strung")
10 | JSONValue(value:"String") == JSONValue(value:"String")
11 | JSONValue(dictionary:["String":1,"Strung":2]) == JSONValue(value:"String")
12 |
13 | JSONValue(dictionary:["String":1,"Strung":2]) == JSONValue(dictionary:["String":1,"Strung":2])
14 | JSONValue(dictionary:["String":1,"Strung":2]) == JSONValue(dictionary:["String":1,"Strung":3])
15 | JSONValue(dictionary:["String":1,"Strung":2]) == JSONValue(dictionary:["String":1,"Strung":3])
16 |
17 | JSONValue(array: ["String","Strung"]) == JSONValue(array: ["String","String"])
18 | JSONValue(array: ["String","Strung"]) == JSONValue(array: ["String","Strung"])
19 |
20 | JSONArray(array: ["String","Strung"]) == JSONValue(value: [["String","String"],["String","Strung"]])[1]
21 | JSONArray(array: ["String","Strung"]) == JSONValue(value: [["String","String"],["String","Strung"]])[0]
22 |
23 | JSONDictionary(dictionary:["String":1,"Strung":2]) == JSONValue(dictionary:["Swing":["String":1,"Strung":2]])
24 | if JSONValue(dictionary:["Swing":["String":1,"Strung":2]])["Swing"] != nil {
25 | JSONValue(dictionary:["Swing":["String":1,"Strung":2]])["Swing"]! == JSONDictionary(dictionary:["String":1,"Strung":2])
26 | }
27 | //: Since JSONArray and JSDictionary adopt SequenceType and their elements (JSONValue) adopt Equatable, it is also possible to use contains()
28 | JSONArray(array: ["String","Strung"]).contains("String")
29 | JSONArray(array: ["String","Strung",2,3,4]).contains(4)
30 | //: JSONArray and JSONDictionary also adopt Equatable themselves, and so [JSONDictionary], [JSONArray] and [JSONValue] arrays can all be compared
31 | [JSONArray(array: ["String","Strung"]),JSONArray(array: ["String","Strung"]), JSONArray(array: ["String","Strung",2,3,4])] == [JSONArray(array: ["String","Strung"]),JSONArray(array: ["String","Strung"]), JSONArray(array: ["String","Strung",2,3,4])]
32 | //: And contains used with them as well:
33 | [JSONArray(array: ["String","Strung"]),JSONArray(array: ["String","Strung"]), JSONArray(array: ["String","Strung",2,3,4])].contains(JSONArray(array: ["String","Strung"]))
34 | //: [Next](@next)
35 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Equatable.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/JSONParsing.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 |
2 | import UIKit
3 |
4 | /*:
5 | ## Parsing
6 | The first step in parsing data into the JSONValue type is to use the static methods of JSONParser. For example we might have a file in the main bundle that we wish to parse:
7 | */
8 | do {
9 | let json = try JSONParser.parse(fileNamed: "iTunes.json") as? JSONDictionary
10 | }
11 | catch let e {
12 | errorString(error: e)
13 | }
14 | //: Alternatively there might be a URL from which we can retrieve JSON:
15 | do {
16 | let json = try JSONParser.parse(urlPath:"https://itunes.apple.com/search?term=jack+johnson") as? JSONDictionary
17 | }
18 | catch let e {
19 | errorString(error: e)
20 | }
21 |
22 |
23 |
24 | /*:
25 | There are also methods for handling NSData and NSURL available.
26 |
27 | **Note**: For convenience the error handling is passed off to the errorString function that can be found in the ErrorHandling.swift file.*/
28 |
29 | //: [Next](@next)
30 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/JSONParsing.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
15 |
16 |
21 |
22 |
27 |
28 |
32 |
33 |
37 |
38 |
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Retrieve Values.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 |
4 | import Foundation
5 |
6 | /*:
7 |
8 | # Retrieving values
9 | Handling JSON is all about remaining type safe, knowing what it is you are expecting to have returned so that values can be transported into core Swift types.
10 | */
11 | var jsonValue:JSONDictionary?
12 |
13 | do {
14 | jsonValue = try JSONParser.parse(fileNamed: "iTunes.json") as? JSONDictionary
15 | }
16 | catch let e {
17 | errorString(error: e)
18 | }
19 | //: Once we have our json it is possible to uncover the types within it through for-in loops and directly through subscripts.
20 |
21 |
22 | if let json = jsonValue
23 | {
24 | for (k,v) in json {
25 |
26 | if let s = v.str {
27 | print(s)
28 | }
29 | if let n = v.num {
30 | print("Key: \(k). Value: \(n).")
31 | }
32 | if let n = v.null {
33 | print(n)
34 | }
35 | if let a = v.jsonArray {
36 | print("Key: \(k). Array: \(a)")
37 | }
38 | if let d = v.jsonDictionary {
39 | print(d)
40 | }
41 | }
42 |
43 | // Returns a value of known type
44 | if let artistName = json["results"]?[0]["artistName"]?.str {
45 | artistName
46 | }
47 |
48 | }
49 |
50 |
51 | /*:
52 | For arrays we can follow a similarly familiar pattern when using the JSONArray type.
53 |
54 | if let json = jsonValue where json.jsonArr != nil {
55 | for v in json {
56 | if let s = v.str {
57 | print(s)
58 | }
59 | if let n = v.num {
60 | print(n)
61 | }
62 | if let n = v.null {
63 | print(n)
64 | }
65 | if let a = v.jsonArr {
66 | print(a)
67 | }
68 | if let d = v.jsonDict {
69 | print(d)
70 | }
71 | }
72 | }
73 | */
74 |
75 | //: [Next](@next)
76 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Retrieve Values.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
9 |
10 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Type Safety in JSONArray.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 | /*:
6 | # Type safety in JSONArray
7 | Dictionaries might have set value types for keys, but JSON arrays are likely to be less strict. Therefore, as a rule when subscripting values into arrays type safety is ignored.
8 | */
9 |
10 | var arr = JSONArray(array:["One","Two", 3, 4])
11 | arr[0] = 1
12 | //: We can avoid going beyond the bounds of an array by using startIndex and endIndex in the familiar way.
13 | if advance(arr.startIndex,2,arr.endIndex) != arr.endIndex {
14 | arr[advance(arr.startIndex,2),.Typesafe] = 3
15 | }
16 | //: And type safety can be applied in the following way:
17 | arr[2,.Typesafe] = 2
18 | arr[2].num
19 | //: Using .Unsafe where you want to be specific about the lack of type safety.
20 | arr[2,.Unsafe] = "Two"
21 | arr[2].str
22 | //: To assist in mainting type safety, there are a range of methods for testing whether the current type can be replaced with another:
23 | if arr[1].canReplaceWithString() == true {
24 | arr[1,.Typesafe] = "Three"
25 | }
26 | //: And so to be both type safe and avoid going beyond the bounds of an array, we can write code like this:
27 | if advance(arr.startIndex,1,arr.endIndex) != arr.endIndex && arr[advance(arr.startIndex,1)].canReplaceWithString() == true {
28 | arr[advance(arr.startIndex,1),.Typesafe] = "Four"
29 | }
30 |
31 | //: If you wish to explicitly identify a lack of type safety then use unsafe:
32 | arr[1,.Unsafe] = 2
33 | //: **Warning**: If you choose to make a type-safe replacement and attempt to insert a value of a different type into the array a crash will occur.
34 |
35 | //: [Next](@next)
36 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Type Safety in JSONArray.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Type Safety in JSONDictionary.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 | /*:
6 | # Type Safety in JSONDictionary
7 | One of the most important features of Swift is type safety and if we don't carry this over to JSON then JSONArray, JSONDictionary and JSONValue become simply a variation of AnyObject with some added methods. In order to carry type safety over to JSON (and to maintain consistency between JSONArray and JSONDictionary, a regular subscripting of a JSON dictionary is unsafe but can be made safe by using .Typesafe after the subscripting key.
8 | */
9 | var d = JSONDictionary(dictionary:["One":true,"Two":1])
10 |
11 | d["Two"] = "fourteen" // unsafe type changes OK
12 |
13 | d["Two",.Unsafe] = 14 // unsafe type changes OK and explicitly marked as so
14 | d["Two",.Typesafe] = 16 // type safe and if type didn't match the current type a crash would occur
15 |
16 | /*:
17 | The implementation of type safety in the first version of Aldwych was done on a whole object basis which could become confusing, so here type safety happens on an update by update basis and this makes it clearer in the code what is happening. And if you wish to use updateValue instead of subscripting this is implemented in a similar way. Although here typesafety must always be specified one way or another.
18 | */
19 |
20 | d.updateValue(2, forKey: "Two", typesafe: .Typesafe) // ["One": false, "Two": 2]
21 |
22 | //: The consequences of passing a value of a different type when typesafe is declared as true is a fatalError crash.
23 | // a.updateValue(false, forKey: "Two", typesafe: .Typesafe) // this code generates a fatal error, uncomment and expand debug area below to see
24 | //: Where type safety is not a consideration always set typesafe to .Unsafe.
25 | d.updateValue("Hello", forKey: "Two", typesafe: .Unsafe) // ["One": false, "Two": "Hello"]
26 | //: Currently if a value is null it is assumed that it can be changed to any new value. To convert an existing value to null can be done through turning type safety off but can also be performed in an easier way using a unique method: nullValueForKey()
27 | d.nullValueForKey("One")
28 | d["One"]?.null == NSNull() // true
29 | //: If you are uncertain of the type of value you wish to change, always test first
30 | if d["One"]?.bool != nil || d["One"]?.null != nil {
31 | d.updateValue(true, forKey: "One", typesafe: .Typesafe) // ["One": true, "Two": "Hello"]
32 | }
33 | //: and to save repetition there are some convenience methods you can use here:
34 |
35 | if d["One"]?.canReplaceWithBool() == true {
36 | d.updateValue(false, forKey: "One", typesafe: .Typesafe) // ["One": false, "Two": "Hello"]
37 | }
38 |
39 | d["One"]?.canReplaceWithString() // false, so we'd never try and make a type safe substitution of the value with a String
40 |
41 | //: Note: Aldwych 2.0 is being updated regularly, so please ensure you keep up to date with the latest version of the playground.
42 |
43 | //: [Next](@next)
44 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/Type Safety in JSONDictionary.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/XMLParsing.xcplaygroundpage/Contents.swift:
--------------------------------------------------------------------------------
1 | //: [Previous](@previous)
2 |
3 | import Foundation
4 |
5 |
6 | /*:
7 | # XML Parsing
8 | Finally here's a brief look at parsing XML in and out of JSON
9 | */
10 |
11 | if let url = NSBundle.mainBundle().URLForResource("styles", withExtension: "xml"),
12 | a = NSData(contentsOfURL: url) {
13 | let b = XMLParser()
14 | let c = b.parse(a)
15 |
16 | let d = "" + XMLParser.json2xml(c)!
17 |
18 | }
19 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Pages/XMLParsing.xcplaygroundpage/timeline.xctimeline:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Resources/iTunes.json:
--------------------------------------------------------------------------------
1 | {
2 | "resultCount":2,
3 | "results": [
4 | {"wrapperType":"track", "kind":"song", "artistId":909253, "collectionId":879273552, "trackId":879273565, "artistName":"Jack Johnson", "collectionName":"In Between Dreams", "trackName":"Better Together", "collectionCensoredName":"In Between Dreams", "trackCensoredName":"Better Together", "artistViewUrl":"https://itunes.apple.com/us/artist/jack-johnson/id909253?uo=4", "collectionViewUrl":"https://itunes.apple.com/us/album/better-together/id879273552?i=879273565&uo=4", "trackViewUrl":"https://itunes.apple.com/us/album/better-together/id879273552?i=879273565&uo=4", "previewUrl":"http://a898.phobos.apple.com/us/r1000/039/Music6/v4/13/22/67/1322678b-e40d-fb4d-8d9b-3268fe03b000/mzaf_8818596367816221008.plus.aac.p.m4a",
5 | "artworkUrl30":"http://is4.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.30x30-50.jpg",
6 | "artworkUrl60":"http://is1.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.60x60-50.jpg",
7 | "artworkUrl100":"http://is4.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.100x100-75.jpg", "collectionPrice":9.99, "trackPrice":1.29, "releaseDate":"2014-05-27T07:00:00Z", "collectionExplicitness":"notExplicit", "trackExplicitness":"notExplicit", "discCount":1, "discNumber":1, "trackCount":15, "trackNumber":1, "trackTimeMillis":207679, "country":"USA", "currency":"USD", "primaryGenreName":"Rock", "radioStationUrl":"https://itunes.apple.com/station/idra.879273565", "isStreamable":true},
8 | {"wrapperType":"track", "kind":"song", "artistId":909253, "collectionId":879273552, "trackId":879273569, "artistName":"Jack Johnson", "collectionName":"In Between Dreams", "trackName":"Banana Pancakes", "collectionCensoredName":"In Between Dreams", "trackCensoredName":"Banana Pancakes", "artistViewUrl":"https://itunes.apple.com/us/artist/jack-johnson/id909253?uo=4", "collectionViewUrl":"https://itunes.apple.com/us/album/banana-pancakes/id879273552?i=879273569&uo=4", "trackViewUrl":"https://itunes.apple.com/us/album/banana-pancakes/id879273552?i=879273569&uo=4", "previewUrl":"http://a540.phobos.apple.com/us/r1000/047/Music/v4/08/d9/c5/08d9c56d-73e5-be1c-1eda-071a48284440/mzaf_8565025008024189274.plus.aac.p.m4a",
9 | "artworkUrl30":"http://is4.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.30x30-50.jpg",
10 | "artworkUrl60":"http://is1.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.60x60-50.jpg",
11 | "artworkUrl100":"http://is4.mzstatic.com/image/pf/us/r30/Music4/v4/41/df/6f/41df6fb5-d08f-5573-fb4b-a56a9b6ea0cb/UMG_cvrart_00602537868858_01_RGB72_900x810_06UMGIM25847.100x100-75.jpg", "collectionPrice":9.99, "trackPrice":1.29, "releaseDate":"2014-05-27T07:00:00Z", "collectionExplicitness":"notExplicit", "trackExplicitness":"notExplicit", "discCount":1, "discNumber":1, "trackCount":15, "trackNumber":3, "trackTimeMillis":191854, "country":"USA", "currency":"USD", "primaryGenreName":"Rock", "radioStationUrl":"https://itunes.apple.com/station/idra.879273569", "isStreamable":true}]
12 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Resources/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Additional Files/ErrorHandling.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | public func errorString(error err:ErrorType) -> String {
4 | if let e = err as? JSONError {
5 | switch e {
6 | case .FileError (let error):
7 | return error
8 | case .DataError (let error):
9 | return error
10 | case .JSONValueError (let error):
11 | return error
12 | case .URLError(let error):
13 | return error
14 | case .TypeError(let error):
15 | return error
16 | }
17 | }
18 |
19 | else {
20 | return (err as NSError).description
21 | }
22 |
23 | }
24 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Additional Files/JSONParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // parser.swift
3 | // JSONParser
4 | //
5 | // Created by Anthony Levings on 11/03/2015.
6 | //
7 |
8 | import Foundation
9 |
10 | public enum JSONError:ErrorType {
11 | case JSONValueError(String), DataError(String), FileError(String), URLError(String), TypeError(String)
12 | }
13 | // receive data
14 | public struct JSONParser:JSONParserType {
15 |
16 |
17 | public static func parse(urlPath urlPath:String) throws -> JSONObjectType {
18 |
19 | guard let url = NSURL(string:urlPath) else {
20 | throw JSONError.FileError("URL with path \(urlPath) is not responding to request.")
21 | }
22 | return try parse(url)
23 | }
24 |
25 | public static func parse(fileNamed fileName:String) throws -> JSONObjectType {
26 | let pE = (fileName as NSString).pathExtension
27 | let name = (fileName as NSString).stringByDeletingPathExtension
28 | guard let url = NSBundle.mainBundle().URLForResource(name, withExtension: pE) else {
29 | throw JSONError.FileError("File not found with name \(fileName) in main bundle.")
30 | }
31 | return try parse(url)
32 | }
33 |
34 | public static func parse(url:NSURL) throws -> JSONObjectType {
35 | let d = try NSData(contentsOfURL: url, options:[])
36 | return try parse(d)
37 | }
38 |
39 | public static func parse(json:NSData) throws -> JSONObjectType {
40 |
41 | guard let jsonObject: AnyObject? = try NSJSONSerialization.JSONObjectWithData(json, options:[]) else {
42 | throw JSONError.JSONValueError("NSJSONSerialization returned nil.")
43 | }
44 |
45 | if let js = jsonObject as? [String:AnyObject] {
46 | let a = JSONDictionary(dictionary: js)
47 | return a
48 | }
49 | else if let js = jsonObject as? [AnyObject] {
50 | return JSONArray(array: js)
51 | }
52 | throw JSONError.JSONValueError("Not a valid dictionary or array for parsing into JSONValue.")
53 |
54 | }
55 |
56 |
57 |
58 |
59 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Additional Files/NSNumberExtensions.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NSNumberExtensions.swift
3 | // AldwychBoolTest
4 | //
5 | // Created by Anthony Levings on 14/05/2015.
6 | // Copyright (c) 2015 Gylphi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public extension NSNumber {
12 | public func isBoolNumber() -> Bool
13 | {
14 | let boolID = CFBooleanGetTypeID() // the type ID of CFBoolean
15 | let numID = CFGetTypeID(self) // the type ID of num
16 | return numID == boolID
17 | }
18 | }
19 |
20 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Additional Files/TypeSafety.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | /// **.Typesafe**: Type must be exchanged for type (exceptions: where current value is null or key/value not currently in JSONDictionary) **.Unsafe**: any JSON compatible type can be exchanged for any other JSON compatible type.
4 | public enum TypeSafety {
5 | case Typesafe, Unsafe
6 | }
7 | /// Takes existing JSONValue and returns a new JSONValue initialized from the value parameter if they are of the same type. If they are not the same type a fatalError crash occurs and a message is sent.
8 | public func typesafeReplace (jValue:JSONValue, value:AnyObject) -> JSONValue {
9 | if jValue.str != nil && value as? String != nil {
10 | return JSONValue(value:value)
11 | }
12 | else if jValue.bool != nil && value as? NSNumber != nil {
13 | if ((value as? NSNumber)?.isBoolNumber() == true) {
14 | return JSONValue(value:value)
15 | }
16 | else {
17 | fatalError("Attempt to replace bool with number in typesafe mode")
18 | }
19 | }
20 | else if jValue.num != nil && value as? NSNumber != nil {
21 | if ((value as? NSNumber)?.isBoolNumber() == false) {
22 | return JSONValue(value:value)
23 | }
24 | else {
25 | fatalError("Attempt to replace number with bool in typesafe mode")
26 | }
27 | }
28 | else if jValue.jsonArray != nil && value as? [AnyObject] != nil {
29 | return JSONValue(value:value)
30 | }
31 | else if jValue.jsonDictionary != nil && value as? [String:AnyObject] != nil {
32 | return JSONValue(value:value)
33 | }
34 | else {
35 | fatalError("Type safety has been breached or type is not JSON compatible")
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Additional Files/XMLParser.swift:
--------------------------------------------------------------------------------
1 | //
2 | // XMLParser.swift
3 | // SaveFile
4 | //
5 | // Created by Anthony Levings on 31/03/2015.
6 | //
7 |
8 | import Foundation
9 |
10 | public class XMLParser:NSObject, NSXMLParserDelegate {
11 |
12 | // where the dictionaries for each tag are stored
13 | var elementArray = [JSONValue]()
14 | var contentArray = [JSONValue]()
15 | // final document array where last dictionary
16 | var document = JSONValue.JDictionary([String:JSONValue]())
17 |
18 |
19 | public func parse(xml:NSData) -> JSONValue {
20 | let xml2json = NSXMLParser(data: xml)
21 | // xml2json.shouldProcessNamespaces = true
22 | xml2json.delegate = self
23 | xml2json.parse()
24 | return document
25 | }
26 |
27 | public func parserDidStartDocument(parser: NSXMLParser) {
28 |
29 | }
30 |
31 | public func parser(parser: NSXMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String]) {
32 |
33 |
34 | // current dictionary is the newly opened tag
35 | elementArray.append(JSONValue(dictionary: [elementName:"", "attributes":attributeDict]))
36 |
37 |
38 | // every new tag has an array added to the holdingArr
39 | contentArray.append(JSONValue.JArray([JSONValue]()))
40 |
41 | }
42 |
43 | public func parser(parser: NSXMLParser, foundCharacters string: String) {
44 | // current array is always the last item in holding, add string to array
45 | contentArray[contentArray.count-1].append(JSONValue(string))
46 | }
47 |
48 | public func parser(parser: NSXMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
49 |
50 | // current array, which might be one string or a nested set of elements is added to the current dictionary
51 | if contentArray.count > 0 {
52 | for (k,_) in elementArray.last! {
53 | if k != "attributes" {
54 | elementArray[elementArray.count-1][k] = contentArray.last
55 | }
56 | }
57 |
58 | }
59 |
60 | // add the current dictionary to the array before if there is one
61 | if contentArray.count > 1 {
62 |
63 | // FIXME: pointless extraction of dictionary to re-encode
64 | // add the dictionary into the previous JSONArray of the holdingArray
65 | contentArray[contentArray.count-2].append(JSONValue(dictionary: elementArray[elementArray.count-1].dictionary))
66 |
67 |
68 | // remove the dictionary
69 | if elementArray.count > 0 {
70 | // remove the array of the current dictionary that has already been assigned
71 | elementArray.removeLast()
72 | }
73 | if contentArray.count > 0 {
74 | contentArray.removeLast()
75 | }
76 |
77 | }
78 |
79 |
80 |
81 |
82 | }
83 |
84 | public func parserDidEndDocument(parser: NSXMLParser) {
85 | if let doc = elementArray.last { document = doc }
86 |
87 | }
88 |
89 |
90 |
91 | private static func xmlEntities(str:String) -> String {
92 | let xmlEntities = ["\"":""","&":"&","'":"'","<":"lt",">":">"]
93 | var strA = str
94 | for (k,v) in xmlEntities {
95 | strA = strA.stringByReplacingOccurrencesOfString(k, withString: v, options: [], range: Range(start: strA.startIndex, end: strA.endIndex))
96 | }
97 | return strA
98 |
99 | }
100 | // used to take a JSONDictionary
101 | public static func json2xml(json:JSONValue)->String? {
102 |
103 | let str = json2xmlUnchecked(json)
104 |
105 | if let d = str.dataUsingEncoding(NSUTF8StringEncoding) {
106 |
107 | let xml = NSXMLParser(data: d)
108 | if xml.parse() == true {
109 |
110 |
111 | return str
112 | }
113 | else {return nil }
114 | }
115 | else {return nil }
116 | }
117 | // used to take a JSONDictionary
118 | private static func json2xmlUnchecked(json:JSONValue) -> String {
119 |
120 | // TODO: bit of a fudge to allow the method to take a JSONDictionary, think about using protocol or reworking the method, OK for now - it works!
121 | let jArray = JSONValue(array:[json.dictionary])
122 | return json2xmlUnchecked2(jArray)
123 | }
124 | private static func json2xmlUnchecked2(json:JSONValue) -> String {
125 |
126 |
127 | var bodyHTML = ""
128 | for (_,b) in json {
129 | // if it's a string we simply add the string to the bodyHTML string
130 | if let str = b.str {
131 |
132 | bodyHTML += xmlEntities(str)
133 | // This bit works
134 |
135 | }
136 |
137 | // if it's a dictionary we know it has a tag key
138 | else if let _ = b.jsonDictionary
139 | {
140 |
141 | bodyHTML += extractFromDictionaryXml2json(b)
142 |
143 | }
144 |
145 | // it shouldn't be an array, and this can most likely be removed
146 | else if let _ = b.jsonArray
147 | {
148 | bodyHTML += json2xmlUnchecked2(b)
149 |
150 | }
151 | }
152 |
153 | return bodyHTML
154 | }
155 | static func extractFromDictionaryXml2json(dict:JSONValue) -> String {
156 | var elementHTML = ""
157 | for (k,v) in dict {
158 | // if it matches one in the list of tags in the body template work through that element's array to build the relevant HTML
159 | if k != "attributes" {
160 | elementHTML += "<"
161 | elementHTML += k
162 | if let atts = dict["attributes"]?.jsonDictionary {
163 | for (k,v) in atts {
164 | elementHTML += " "
165 | elementHTML += k
166 | elementHTML += "=\""
167 | if let s = v.str {
168 | elementHTML += s
169 |
170 | }
171 | elementHTML += "\""
172 | }
173 |
174 | elementHTML += ">"
175 | }
176 | if let text = v.str {
177 | elementHTML += xmlEntities(text)
178 |
179 | }
180 | if let _ = v.jsonArray {
181 | elementHTML += json2xmlUnchecked2(v)
182 |
183 | }
184 |
185 | elementHTML += ""
186 | elementHTML += k
187 | elementHTML += ">"
188 | }
189 |
190 | else if let _ = dict[k]?.jsonArray {
191 | // cycle back through
192 | elementHTML += json2xmlUnchecked2(dict[k]!)
193 | }
194 |
195 | }
196 | return elementHTML
197 | }
198 |
199 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONObjectTypes/JSONArray.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 |
4 | public enum JSONArray:JSONObjectType {
5 | case JArray([JSONValue])
6 | }
7 |
8 | extension JSONArray:Equatable {}
9 | public func ==(lhs: JSONArray, rhs: JSONArray) -> Bool {
10 | if lhs.count != rhs.count {
11 | return false
12 | }
13 | else {
14 | for v in lhs.enumerate() {
15 | if lhs[v.index] != rhs[v.index] {
16 | return false
17 | }
18 | }
19 | }
20 | return true
21 |
22 |
23 | }
24 | extension JSONArray {
25 | public func contains(element:AnyObject) -> Bool {
26 | return self.contains(JSONValue(value:element))
27 | }
28 | }
29 |
30 |
31 | // subscripting Arrays
32 | extension JSONArray: JSONArrayProtocol {
33 |
34 | public var startIndex:Index {
35 | switch self {
36 | case .JArray(let array):
37 | return array.startIndex
38 |
39 | }
40 | }
41 | public var endIndex:Index {
42 | switch self {
43 | case .JArray(let array):
44 | return array.endIndex
45 | }
46 |
47 | }
48 |
49 | public typealias Index = Int
50 | public subscript (position:Index) -> JSONValue {
51 | get {
52 | switch self {
53 | case .JArray (let a):
54 | if position >= a.endIndex {
55 | fatalError("Beyond bounds of array.")
56 | }
57 | return a[position]
58 |
59 | }
60 | }
61 | set(newValue) {
62 | switch self {
63 | case .JArray (var a):
64 | if position >= a.endIndex {
65 | fatalError("Tried to insert a value beyond the final value in the array.")
66 | }
67 | else {
68 | a[position] = newValue
69 | self = .JArray(a)
70 | }
71 | }
72 | }
73 | }
74 |
75 |
76 | public subscript (position:Index) -> AnyObject {
77 | get {
78 | fatalError("You shouldn't be trying to retrieve AnyObject.")
79 | }
80 | set(newValue) {
81 | switch self {
82 | case .JArray (var a):
83 | if position >= a.endIndex {
84 | fatalError("Tried to insert a value beyond the final value in the array.")
85 | }
86 | else {
87 | a[position] = JSONValue(value:newValue)
88 | self = .JArray(a)
89 | }
90 |
91 | }}
92 | }
93 |
94 | public subscript (position:Index, typesafe:TypeSafety) -> AnyObject {
95 | get {
96 |
97 | fatalError("You shouldn't be trying to retrieve AnyObject from a JSONArray!")
98 | }
99 | set(newValue) {
100 | switch self {
101 | case .JArray (var a):
102 | if position >= a.endIndex {
103 | fatalError("Tried to insert a value beyond the final value in the array.")
104 | }
105 |
106 | else if case .Unsafe = typesafe {
107 | a[position] = JSONValue(value:newValue)
108 | self = .JArray(a)
109 | }
110 | else if case .Typesafe = typesafe {
111 | a[position] = typesafeReplace(a[position], value:newValue)
112 | self = .JArray(a)
113 | }
114 |
115 | }}
116 | }
117 |
118 |
119 | }
120 |
121 | // Array Generator
122 | public struct JSONArrayGenerator:GeneratorType {
123 | // use dictionary with index as keys for position in array
124 | let value:JSONArray
125 | var indexInSequence:Int = 0
126 |
127 | init(value:JSONArray) {
128 | self.value = value
129 |
130 | }
131 |
132 | mutating public func next() -> JSONValue? {
133 | switch value {
134 | case .JArray (let Array) where !Array.isEmpty:
135 |
136 | if indexInSequence < Array.count
137 | { let element = Array[indexInSequence]
138 | ++indexInSequence
139 | return element
140 | }
141 | else { indexInSequence = 0
142 | return nil
143 | }
144 |
145 | default:
146 | return nil
147 | }
148 | }
149 | }
150 |
151 | extension JSONArray {
152 | public var array:[AnyObject] {
153 | var array = [AnyObject](count: self.count, repeatedValue: 0)
154 | switch self {
155 | case .JArray(let arr):
156 | for v in arr.enumerate() {
157 | if let a: AnyObject = v.1.str ?? v.1.num ?? v.1.bool ?? v.1.null {
158 | array[v.0] = a
159 | }
160 | else if v.1.jsonDictionary != nil {
161 | array[v.0] = v.1.dictionary as AnyObject
162 | }
163 | else if v.1.jsonArray != nil {
164 | array[v.0] = v.1.array as AnyObject
165 | }
166 | }
167 | }
168 | return array
169 | }
170 |
171 | public var nsArray:NSArray {
172 |
173 | let array = NSMutableArray(capacity: self.count)
174 | switch self {
175 | case .JArray(let arr):
176 | for v in arr {
177 | if let s: NSString = v.str {
178 | array.addObject(s)
179 | }
180 | if let n: NSNumber = v.num {
181 | array.addObject(n)
182 | }
183 | if let b: Bool = v.bool {
184 | array.addObject(b)
185 | }
186 | if let n: NSNull = v.null {
187 | array.addObject(n)
188 | }
189 | else if v.jsonDictionary != nil {
190 | array.addObject(v.nsDictionary)
191 | }
192 | else if v.jsonArray != nil {
193 | array.addObject(v.nsArray)
194 | }
195 | }
196 | }
197 | return array
198 | }
199 | }
200 |
201 | extension JSONArray {
202 | public init (array:[AnyObject]) {
203 | self = JSONArray.JArray(array.map{JSONValue(value:$0)})
204 | }
205 | // FIXME: add init for [JSONValue]
206 | }
207 |
208 |
209 | extension JSONArray: SequenceType {
210 |
211 | public typealias Generator = JSONArrayGenerator
212 | public func generate() -> Generator {
213 | let gen = Generator(value: self)
214 | return gen
215 | }
216 | }
217 |
218 |
219 | extension JSONArray {
220 | public var count:Int {
221 | switch self {
222 | case .JArray(let arr):
223 | return arr.count
224 | }
225 |
226 | }
227 | public var last:JSONValue? {
228 | switch self {
229 | case .JArray(let arr):
230 | return arr.last
231 | }
232 |
233 | }
234 | }
235 |
236 | // MARK: Append methods
237 | extension JSONArray {
238 | // Append method
239 | public mutating func append(value:AnyObject) {
240 | switch self {
241 | case .JArray(var array):
242 | array.append(JSONValue(value:value))
243 | self = JSONArray.JArray(array)
244 | }
245 | }
246 |
247 | public mutating func append(arr:JSONValue) {
248 | switch self {
249 | case .JArray(var array):
250 | array.append(arr)
251 | self = JSONArray.JArray(array)
252 | }
253 | }
254 |
255 | }
256 |
257 | // MARK: Insert methods
258 | extension JSONArray {
259 | public mutating func insert(value:AnyObject, atIndex ind:Int) {
260 | switch self {
261 | case .JArray(var array):
262 | array.insert(JSONValue(value:value), atIndex: ind)
263 | self = JSONArray.JArray(array)
264 |
265 | }
266 | }
267 |
268 | public mutating func insert(value:JSONValue, atIndex ind:Int) {
269 | switch self {
270 | case .JArray(var array):
271 | array.insert(value, atIndex: ind)
272 | self = JSONArray.JArray(array)
273 |
274 | }
275 | }
276 | }
277 |
278 |
279 | // MARK: Extend methods
280 | extension JSONArray {
281 | // Append method
282 | public mutating func extend(arr:[AnyObject]) {
283 | switch self {
284 | case .JArray(var array):
285 | let ext:[JSONValue] = arr.map{JSONValue(value:$0)}
286 | array.appendContentsOf(ext)
287 | self = JSONArray.JArray(array)
288 |
289 | }
290 | }
291 | public mutating func extend(arr:[JSONValue]) {
292 | switch self {
293 | case .JArray(var array):
294 | array.appendContentsOf(arr)
295 | self = JSONArray.JArray(array)
296 |
297 | }
298 | }
299 | }
300 |
301 | // MARK: Remove last method
302 | extension JSONArray {
303 | public mutating func removeLast() {
304 | switch self {
305 | case .JArray(var array):
306 | array.removeLast()
307 | self = JSONArray.JArray(array)
308 | }
309 | }
310 | public mutating func removeAtIndex(index:Int) {
311 | switch self {
312 | case .JArray(var array):
313 | array.removeAtIndex(index)
314 | self = JSONArray.JArray(array)
315 | }
316 | }
317 |
318 |
319 | }
320 |
321 |
322 |
323 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONObjectTypes/JSONDictionary.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension JSONDictionary {
4 | public func keys() -> [String] {
5 | switch self {
6 | case .JDictionary(let dictionary):
7 | return [String](dictionary.keys)
8 |
9 | }
10 | }
11 | }
12 | extension JSONDictionary:Equatable {}
13 | public func ==(lhs: JSONDictionary, rhs: JSONDictionary) -> Bool {
14 |
15 | let keys = lhs.keys() + rhs.keys()
16 |
17 | for k in keys {
18 | if lhs[k] != rhs[k] {
19 | return false
20 | }
21 | }
22 | return true
23 |
24 |
25 | }
26 |
27 |
28 | public enum JSONDictionary:JSONObjectType {
29 | case JDictionary([String:JSONValue])
30 | }
31 | extension JSONDictionary {
32 | public init (dictionary:[String: AnyObject]) {
33 | let initial = [String: JSONValue]()
34 | self = JSONDictionary.JDictionary(dictionary.reduce(initial, combine: { (var dict, val) in
35 | dict[val.0] = JSONValue(value:val.1)
36 | return dict
37 | }))
38 | }
39 | }
40 |
41 | extension JSONDictionary {
42 | public var dictionary:Swift.Dictionary {
43 | var dictionary = Swift.Dictionary()
44 |
45 | switch self {
46 | case .JDictionary (let dict):
47 | for (k,v) in dict {
48 | if let a: AnyObject = v.str ?? v.num ?? v.bool ?? v.null {
49 | dictionary[k] = a
50 | }
51 | else if v.jsonArray != nil {
52 | dictionary[k] = v.array as AnyObject
53 | }
54 | else if v.jsonDictionary != nil {
55 | dictionary[k] = v.dictionary as AnyObject
56 | }
57 | }
58 | }
59 | return dictionary
60 | }
61 |
62 | public var nsDictionary:NSDictionary {
63 | let dictionary = NSMutableDictionary()
64 |
65 | switch self {
66 | case .JDictionary (let dict):
67 | for (k,v) in dict {
68 | if let a: AnyObject = v.str ?? v.num ?? v.bool ?? v.null {
69 | dictionary[k] = a
70 | }
71 | else if v.jsonArray != nil {
72 | dictionary[k] = v.array as NSArray
73 | }
74 | else if v.jsonDictionary != nil {
75 | dictionary[k] = v.dictionary as NSDictionary
76 | }
77 | }
78 | }
79 | return dictionary
80 | }
81 |
82 | }
83 |
84 | extension JSONDictionary {
85 | public mutating func updateValue(value:JSONValue, forKey key:String) {
86 | switch self {
87 | case .JDictionary(var dictionary):
88 | dictionary.updateValue(value, forKey: key)
89 | self = .JDictionary(dictionary)
90 | }
91 | }
92 | enum JSONTypeError:ErrorType {
93 | case TypeError(String)
94 | }
95 | public mutating func updateValue(value:AnyObject, forKey key:String, typesafe:TypeSafety) {
96 | switch self {
97 | case .JDictionary(var dictionary):
98 | if case .Unsafe = typesafe {
99 | dictionary[key] = JSONValue(value:value)
100 | self = .JDictionary(dictionary)
101 | break
102 | }
103 | else if dictionary[key]?.null != nil {
104 | dictionary[key] = JSONValue(value:value)
105 | self = .JDictionary(dictionary)
106 | break
107 | }
108 | guard let dV = dictionary[key] else {
109 | // dictionary key currently has no associated value
110 | dictionary[key] = JSONValue(value:value)
111 | self = .JDictionary(dictionary)
112 | break
113 | }
114 |
115 | dictionary[key] = typesafeReplace(dV, value: value)
116 | self = .JDictionary(dictionary)
117 | }
118 | }
119 |
120 | public mutating func nullValueForKey(key:String) {
121 | // this should never ever fail
122 | updateValue(NSNull(), forKey: key, typesafe: .Unsafe)
123 | }
124 | }
125 |
126 |
127 | extension JSONDictionary {
128 | public subscript (key:String) -> JSONValue? {
129 | get {
130 | switch self {
131 | case .JDictionary(let a):
132 | return a[key]
133 |
134 | }}
135 | set(newValue) {
136 |
137 | switch self {
138 | case .JDictionary(var a):
139 | if let nV = newValue {
140 | a[key] = nV
141 | self = .JDictionary(a)
142 | }
143 | }}
144 | }
145 |
146 | public subscript (key:String) -> AnyObject? {
147 | get {
148 | return nil
149 | }
150 | set(newValue) {
151 |
152 | switch self {
153 | case .JDictionary(var a):
154 | if let nV = newValue {
155 | a[key] = JSONValue(value:nV)
156 | self = .JDictionary(a)
157 | }
158 |
159 | }}
160 | }
161 |
162 | public subscript (key:String, typesafe:TypeSafety) -> AnyObject? {
163 | get {
164 | return nil
165 | }
166 | set(newValue) {
167 |
168 | switch self {
169 | case .JDictionary(var a):
170 | guard let nV = newValue else {
171 | fatalError("No value to insert into array")
172 | }
173 | guard let dV = a[key] else {
174 | // no current key, so add
175 | a[key] = JSONValue(value:nV)
176 | self = .JDictionary(a)
177 | break
178 | }
179 | if case .Unsafe = typesafe {
180 | a[key] = JSONValue(value:nV)
181 | self = .JDictionary(a)
182 | }
183 | else if case .Typesafe = typesafe {
184 | a[key] = typesafeReplace(dV, value:nV)
185 | self = .JDictionary(a)
186 | }
187 |
188 |
189 | }}
190 | }
191 |
192 |
193 | }
194 |
195 |
196 | extension JSONDictionary: SequenceType {
197 |
198 | public typealias Generator = JSONDictionaryGenerator
199 |
200 | public func generate() -> Generator {
201 | let gen = Generator(value: self)
202 | return gen
203 | }
204 | }
205 |
206 | // FIXME: Necessary to repeat generator for JSONValue.JDictionary and JSONDictionary?
207 | // Dictionary Generator
208 | public struct JSONDictionaryGenerator:GeneratorType {
209 | // use dictionary with index as keys for position in array
210 | let value:JSONDictionary
211 | var indexInSequence:Int = 0
212 |
213 | init(value:JSONDictionary) {
214 | self.value = value
215 |
216 | }
217 |
218 | mutating public func next() -> (String,JSONValue)? {
219 | switch value {
220 | case .JDictionary (let dictionary):
221 | let keyArray = [String](dictionary.keys)
222 | if indexInSequence < dictionary.count
223 | { let key = keyArray[indexInSequence]
224 | let element = dictionary[key]
225 | ++indexInSequence
226 | return (key,element!)
227 | }
228 | else {
229 | indexInSequence = 0
230 | return nil
231 | }
232 | }
233 | }
234 | }
235 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/JSONValue.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 |
4 | // MARK: Core enum
5 | public enum JSONValue {
6 |
7 | case JString(String), Number(NSNumber), JBool(Bool), Null
8 | indirect case JArray([JSONValue]), JDictionary([String:JSONValue])
9 |
10 | }
11 |
12 |
13 | // MARK: Extract inner value
14 | extension JSONValue {
15 |
16 | public var str:String? {
17 | switch self {
18 | case .JString(let str):
19 | return str
20 | default:
21 | return nil
22 | }
23 |
24 | }
25 | public var strOpt:String?? {
26 | switch self {
27 | case .JString(let str):
28 | return str
29 | case .Null:
30 | let a:String? = nil
31 | return a
32 | default:
33 | return nil
34 | }
35 |
36 | }
37 |
38 |
39 | public var num:NSNumber? {
40 | switch self {
41 | case .Number(let num):
42 | return num
43 | default:
44 | return nil
45 | }
46 |
47 | }
48 | public var numOpt:NSNumber?? {
49 | switch self {
50 | case .Number(let num):
51 | return num
52 | case .Null:
53 | let a:NSNumber? = nil
54 | return a
55 | default:
56 | return nil
57 | }
58 |
59 | }
60 |
61 | public var bool:Bool? {
62 | switch self {
63 | case .JBool(let bool):
64 | return bool
65 | default:
66 | return nil
67 | }
68 |
69 | }
70 | public var boolOpt:Bool?? {
71 | switch self {
72 | case .JBool(let bool):
73 | return bool
74 | case .Null:
75 | let a:Bool? = nil
76 | return a
77 | default:
78 | return nil
79 | }
80 |
81 | }
82 | public var null:NSNull? {
83 | switch self {
84 | case .Null:
85 | return NSNull()
86 | default:
87 | return nil
88 | }
89 |
90 | }
91 |
92 | public var jsonArray:JSONArray? {
93 | switch self {
94 | case .JArray(let jsonArr):
95 | return JSONArray.JArray(jsonArr)
96 | default:
97 | return nil
98 | }
99 |
100 | }
101 | public var jsonArrayOpt:JSONArray?? {
102 | switch self {
103 | case .JArray(let jsonArr):
104 | return JSONArray.JArray(jsonArr)
105 | case .Null:
106 | let a:JSONArray? = nil
107 | return a
108 | default:
109 | return nil
110 | }
111 |
112 | }
113 | public var jsonDictionary:JSONDictionary? {
114 | switch self {
115 | case .JDictionary(let jsonDict):
116 | return JSONDictionary.JDictionary(jsonDict)
117 | default:
118 | return nil
119 | }
120 |
121 | }
122 |
123 | public var jsonDictionaryOpt:JSONDictionary?? {
124 | switch self {
125 | case .JDictionary(let jsonDict):
126 | return JSONDictionary.JDictionary(jsonDict)
127 | case .Null:
128 | let a:JSONDictionary? = nil
129 | return a
130 | default:
131 | return nil
132 | }
133 |
134 | }
135 |
136 |
137 | }
138 |
139 | extension JSONValue {
140 | public var count:Int {
141 | switch self {
142 | case .JArray(let arr):
143 | return arr.count
144 | default:
145 | return 0
146 | }
147 |
148 | }
149 | public var last:JSONValue? {
150 | switch self {
151 | case .JArray(let arr):
152 | return arr.last
153 | default:
154 | return nil
155 | }
156 |
157 | }
158 | }
159 |
160 | extension JSONValue:CustomStringConvertible {
161 |
162 | public var description:String {
163 |
164 | switch self {
165 | case .JDictionary( _):
166 | return Swift.String(self.dictionary)
167 | case .JArray( _):
168 | return Swift.String(self.array)
169 | case .JBool(let b):
170 | if b == true {
171 | return "true"
172 | }
173 | else {
174 | return "false"
175 | }
176 | case .Number(let n):
177 | return Swift.String(n)
178 | case .JString(let s):
179 | return "\"\(s)\""
180 | default:
181 | return "JSONValue"
182 | }
183 |
184 | }
185 |
186 |
187 |
188 | }
189 |
190 | public extension JSONValue {
191 | public func isJSONBool () -> Bool {
192 | return self.bool != nil
193 | }
194 | public func isJSONNull () -> Bool {
195 | return self.null != nil
196 | }
197 | public func isJSONNumber () -> Bool {
198 | return self.num != nil
199 | }
200 | public func isJSONString () -> Bool {
201 | return self.str != nil
202 | }
203 |
204 | public func isJSONDictionary () -> Bool {
205 | return self.jsonDictionary != nil
206 | }
207 | public func isJSONArray () -> Bool {
208 | return self.jsonDictionary != nil
209 | }
210 | public func canReplaceWithBool() -> Bool {
211 | if self.isJSONBool() || self.isJSONNull() {
212 | return true
213 | }
214 | return false
215 | }
216 | public func canReplaceWithNumber() -> Bool {
217 | if self.isJSONNumber() || self.isJSONNull() {
218 | return true
219 | }
220 | return false
221 | }
222 | public func canReplaceWithString() -> Bool {
223 | if self.isJSONString() || self.isJSONNull() {
224 | return true
225 | }
226 | return false
227 | }
228 | public func canReplaceWithArray() -> Bool {
229 | if self.isJSONArray() || self.isJSONNull() {
230 | return true
231 | }
232 | return false
233 | }
234 |
235 | public func canReplaceWithDictionary() -> Bool {
236 | if self.isJSONDictionary() || self.isJSONNull() {
237 | return true
238 | }
239 | return false
240 | }
241 |
242 |
243 | }
244 |
245 |
246 |
247 |
248 |
249 |
250 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_ArrayExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // MARK: Extract Array
4 | extension JSONValue: JSONArrayProtocol {
5 | public var array:[AnyObject] {
6 | var array = [AnyObject](count: self.count, repeatedValue: 0)
7 | switch self {
8 | case .JArray(let arr):
9 | for v in arr.enumerate() {
10 | if let a: AnyObject = v.1.str ?? v.1.num ?? v.1.bool ?? v.1.null {
11 | array[v.0] = a
12 | }
13 | else if v.1.jsonDictionary != nil {
14 | array[v.0] = v.1.dictionary as AnyObject
15 | }
16 | else if v.1.jsonArray != nil {
17 | array[v.0] = v.1.array as AnyObject
18 | }
19 | }
20 | default:
21 | break
22 | }
23 | return array
24 | }
25 |
26 | public var nsArray:NSArray {
27 |
28 | let array = NSMutableArray(capacity: self.count)
29 | switch self {
30 | case .JArray(let arr):
31 | for v in arr {
32 | if let s: NSString = v.str {
33 | array.addObject(s)
34 | }
35 | if let n: NSNumber = v.num {
36 | array.addObject(n)
37 | }
38 | if let b: Bool = v.bool {
39 | array.addObject(b)
40 | }
41 | if let n: NSNull = v.null {
42 | array.addObject(n)
43 | }
44 | else if v.jsonDictionary != nil {
45 | array.addObject(v.nsDictionary)
46 | }
47 | else if v.jsonArray != nil {
48 | array.addObject(v.nsArray)
49 | }
50 | }
51 | default:
52 | break
53 | }
54 | return array
55 | }
56 | }
57 |
58 | // subscripting Arrays
59 | // FIXME: adopt Indexable protocol
60 | extension JSONValue {
61 | public typealias Index = Int
62 | public var startIndex:Index {
63 | switch self {
64 | case .JArray(let array):
65 | return array.startIndex
66 | default:
67 | fatalError("This is not an Array!")
68 | }
69 | }
70 | public var endIndex:Index {
71 | switch self {
72 | case .JArray(let array):
73 | return array.endIndex
74 | default:
75 | fatalError("This is not an Array!")
76 | }
77 | }
78 |
79 | public subscript (position:Index) -> JSONValue {
80 | get {
81 | switch self {
82 | case .JArray (let a):
83 | if position >= a.endIndex {
84 | fatalError("Beyond bounds of Array.")
85 | }
86 | return a[position]
87 |
88 | default:
89 | fatalError("Not an Array.")
90 | }}
91 | set(newValue) {
92 | switch self {
93 | case .JArray (var a):
94 | if position >= a.endIndex {
95 | fatalError("Tried to insert a value beyond the final value in the array")
96 | }
97 | else {
98 | a[position] = newValue
99 | self = .JArray(a)
100 | }
101 | default:
102 | fatalError("Not an Array.")
103 | }}
104 | }
105 |
106 |
107 | public subscript (position:Index) -> AnyObject {
108 | get {
109 | fatalError("You shouldn't be trying to retrieve AnyObject from a JSONArray!")
110 | }
111 | set(newValue) {
112 | switch self {
113 | case .JArray (var a):
114 | if position >= a.endIndex {
115 | fatalError("Tried to insert a value beyond the final value in the array")
116 | }
117 | else {
118 | a[position] = JSONValue(value:newValue)
119 | self = .JArray(a)
120 | }
121 | default:
122 | fatalError("Not an Array.")
123 | }}
124 | }
125 |
126 | public subscript (position:Index, typesafe:TypeSafety) -> AnyObject {
127 | get {
128 | fatalError("You shouldn't be trying to retrieve AnyObject from a JSONArray!")
129 | }
130 | set(newValue) {
131 | switch self {
132 | case .JArray (var a):
133 | if position >= a.endIndex {
134 | fatalError("Tried to insert a value beyond the final value in the array")
135 | }
136 | else if case .Unsafe = typesafe {
137 | a[position] = JSONValue(value:newValue)
138 | self = .JArray(a)
139 | }
140 | else if case .Typesafe = typesafe {
141 | a[position] = typesafeReplace(a[position], value:newValue)
142 | self = .JArray(a)
143 | }
144 |
145 | default:
146 | fatalError("Not an Array.")
147 | }}
148 | }
149 |
150 |
151 |
152 |
153 | }
154 |
155 |
156 | // MARK: Append methods
157 | extension JSONValue {
158 | // Append method
159 | public mutating func append(value:AnyObject) {
160 | switch self {
161 | case .JArray(var array):
162 | array.append(JSONValue(value:value))
163 | self = JSONValue.JArray(array)
164 | default:
165 | return
166 | }
167 | }
168 |
169 | public mutating func append(arr:JSONValue) {
170 | switch self {
171 | case .JArray(var array):
172 | array.append(arr)
173 | self = JSONValue.JArray(array)
174 | default:
175 | return
176 | }
177 | }
178 |
179 | }
180 |
181 | // MARK: Insert methods
182 | extension JSONValue {
183 | public mutating func insert(value:AnyObject, atIndex ind:Int) {
184 | switch self {
185 | case .JArray(var array):
186 | array.insert(JSONValue(value:value), atIndex: ind)
187 | self = JSONValue.JArray(array)
188 | default:
189 | return
190 | }
191 | }
192 |
193 | public mutating func insert(value:JSONValue, atIndex ind:Int) {
194 | switch self {
195 | case .JArray(var array):
196 | array.insert(value, atIndex: ind)
197 | self = JSONValue.JArray(array)
198 | default:
199 | return
200 | }
201 | }
202 | }
203 |
204 | // MARK: Extend methods
205 | extension JSONValue {
206 | // Append method
207 | public mutating func extend(arr:[AnyObject]) {
208 | switch self {
209 | case .JArray(var array):
210 | let ext:[JSONValue] = arr.map{JSONValue(value:$0)}
211 | array.appendContentsOf(ext)
212 | self = JSONValue.JArray(array)
213 | default:
214 | return
215 | }
216 | }
217 | public mutating func extend(arr:[JSONValue]) {
218 | switch self {
219 | case .JArray(var array):
220 | array.appendContentsOf(arr)
221 | self = JSONValue.JArray(array)
222 | default:
223 | return
224 | }
225 | }
226 | }
227 | // MARK: Remove last method
228 | extension JSONValue {
229 | public mutating func removeLast() {
230 | switch self {
231 | case .JArray(var array):
232 | array.removeLast()
233 | self = JSONValue.JArray(array)
234 | default:
235 | return
236 | }
237 | }
238 | public mutating func removeAtIndex(index:Int) {
239 | switch self {
240 | case .JArray(var array):
241 | array.removeAtIndex(index)
242 | self = JSONValue.JArray(array)
243 | default:
244 | return
245 | }
246 | }
247 | }
248 |
249 |
250 |
251 |
252 |
253 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_DictionaryExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 |
4 | // MARK: Extract dictionary
5 | extension JSONValue {
6 | public var dictionary:Swift.Dictionary {
7 | var dictionary = Swift.Dictionary()
8 |
9 | switch self {
10 | case .JDictionary (let dict):
11 | for (k,v) in dict {
12 | if let a: AnyObject = v.str ?? v.num ?? v.bool ?? v.null {
13 | dictionary[k] = a
14 | }
15 | else if v.jsonArray != nil {
16 | dictionary[k] = v.array as AnyObject
17 | }
18 | else if v.jsonDictionary != nil {
19 | dictionary[k] = v.dictionary as AnyObject
20 | }
21 | }
22 | default:
23 | break
24 | }
25 | return dictionary
26 | }
27 |
28 | public var nsDictionary:NSDictionary {
29 | let dictionary = NSMutableDictionary()
30 |
31 | switch self {
32 | case .JDictionary (let dict):
33 | for (k,v) in dict {
34 | if let a: AnyObject = v.str ?? v.num ?? v.bool ?? v.null {
35 | dictionary[k] = a
36 | }
37 | else if v.jsonArray != nil {
38 | dictionary[k] = v.array as NSArray
39 | }
40 | else if v.jsonDictionary != nil {
41 | dictionary[k] = v.dictionary as NSDictionary
42 | }
43 | }
44 | default:
45 | break
46 | }
47 | return dictionary
48 | }
49 |
50 | }
51 |
52 | // Dictionary subscripting
53 | extension JSONValue {
54 |
55 |
56 | public subscript (key:String) -> JSONValue? {
57 | get {
58 | switch self {
59 | case .JDictionary(let a):
60 | return a[key]
61 |
62 | default:
63 |
64 | return nil
65 | }}
66 | set(newValue) {
67 |
68 | switch self {
69 | case .JDictionary(var a):
70 | if let nV = newValue {
71 | a[key] = nV
72 | self = .JDictionary(a)
73 | }
74 | default:
75 | return
76 | }}
77 | }
78 |
79 | public subscript (key:String) -> AnyObject? {
80 | get {
81 | return nil
82 | }
83 | set(newValue) {
84 |
85 | switch self {
86 | case .JDictionary(var a):
87 | if let nV = newValue {
88 | a[key] = JSONValue(value:nV)
89 | self = .JDictionary(a)
90 | }
91 | default:
92 | return
93 | }}
94 | }
95 |
96 | public subscript (key:String, typesafe:TypeSafety) -> AnyObject? {
97 | get {
98 | return nil
99 | }
100 | set(newValue) {
101 |
102 | switch self {
103 | case .JDictionary(var a):
104 | guard let nV = newValue else {
105 | fatalError("No value to insert into array")
106 | }
107 | guard let dV = a[key] else {
108 | // no current key, so add
109 | a[key] = JSONValue(value:nV)
110 | self = .JDictionary(a)
111 | break
112 | }
113 | if case .Unsafe = typesafe {
114 | a[key] = JSONValue(value:nV)
115 | self = .JDictionary(a)
116 | }
117 | else if case .Typesafe = typesafe {
118 | a[key] = typesafeReplace(dV, value:nV)
119 | self = .JDictionary(a)
120 | }
121 |
122 | default:
123 | fatalError("Trying to update value for key in non-dictionary type")
124 | }}
125 | }
126 |
127 |
128 | }
129 |
130 |
131 | // MARK: Keys and Values
132 | extension JSONValue {
133 | public var keys:[String] {
134 | switch self {
135 | case .JDictionary(let dict):
136 | return [String](dict.keys)
137 | default:
138 | return []
139 | }
140 |
141 | }
142 | public var values:[JSONValue] {
143 | switch self {
144 | case .JDictionary(let dict):
145 | return [JSONValue](dict.values)
146 | default:
147 | return []
148 | }
149 |
150 | }
151 | }
152 |
153 | extension JSONValue {
154 | public mutating func updateValue(value:JSONValue, forKey key:String) {
155 | switch self {
156 | case .JDictionary(var dictionary):
157 | dictionary[key] = value
158 | self = .JDictionary(dictionary)
159 | default:
160 | return
161 | }
162 |
163 | }
164 | public mutating func updateValue(value:AnyObject, forKey key:String, typesafe:TypeSafety) {
165 | switch self {
166 | case .JDictionary(var dictionary):
167 | if case .Unsafe = typesafe {
168 | dictionary[key] = JSONValue(value:value)
169 | self = .JDictionary(dictionary)
170 | break
171 | }
172 | else if dictionary[key]?.null != nil {
173 | dictionary[key] = JSONValue(value:value)
174 | self = .JDictionary(dictionary)
175 | break
176 | }
177 | guard let dV = dictionary[key] else {
178 | // dictionary key currently has no associated value
179 | dictionary[key] = JSONValue(value:value)
180 | self = .JDictionary(dictionary)
181 | break
182 | }
183 | dictionary[key] = typesafeReplace(dV, value: value)
184 | self = .JDictionary(dictionary)
185 |
186 | default:
187 | fatalError("Trying to update value for key in non-dictionary type")
188 | }
189 |
190 | }
191 | public mutating func nullValueForKey(key:String) {
192 | updateValue(NSNull(), forKey: key, typesafe: .Unsafe)
193 | }
194 | }
195 |
196 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_Equatable.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // MARK: JSONValue
4 | extension JSONValue:Equatable {}
5 | public func ==(lhs: JSONValue, rhs: JSONValue) -> Bool {
6 | if let ls = lhs.str,
7 | rs = rhs.str {
8 | return ls == rs
9 | }
10 | else if let lb = lhs.bool,
11 | rb = rhs.bool {
12 | return lb == rb
13 | }
14 | else if let lno = lhs.num,
15 | rno = rhs.num {
16 | return lno == rno
17 | }
18 | else if let ln = lhs.null,
19 | rn = rhs.null {
20 | return ln == rn
21 | }
22 | else if let lA = lhs.jsonArray,
23 | rA = rhs.jsonArray {
24 | return lA == rA
25 | }
26 | else if let lA = lhs.jsonDictionary,
27 | rA = rhs.jsonDictionary {
28 | return lA == rA
29 | }
30 | else {
31 | return false
32 | }
33 | }
34 |
35 | // MARK: JSONDictionary == JSONValue
36 | public func ==(lhs: JSONDictionary, rhs: JSONValue) -> Bool {
37 | if let rA = rhs.jsonDictionary {
38 | return lhs == rA
39 | }
40 | else {
41 | return false
42 | }
43 | }
44 | // MARK: JSONValue == JSONDictionary
45 | public func ==(lhs: JSONValue, rhs: JSONDictionary) -> Bool {
46 | if let lA = lhs.jsonDictionary {
47 | return rhs == lA
48 | }
49 | else {
50 | return false
51 | }
52 | }
53 | // MARK: JSONValue == JSONArray
54 | public func ==(lhs: JSONValue, rhs: JSONArray) -> Bool {
55 | if let lA = lhs.jsonArray {
56 | return rhs == lA
57 | }
58 | else {
59 | return false
60 | }
61 | }
62 |
63 | // MARK: JSONArray == JSONValue
64 | public func ==(lhs: JSONArray, rhs: JSONValue) -> Bool {
65 | if let rA = rhs.jsonArray {
66 | return lhs == rA
67 | }
68 | else {
69 | return false
70 | }
71 | }
72 |
73 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_ExportExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | extension JSONValue {
4 | public func jsonData(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> NSData? {
5 | switch self {
6 |
7 | case .JArray(_):
8 | return try! NSJSONSerialization.dataWithJSONObject(self.array, options: options)
9 | case .JDictionary(_):
10 | return try! NSJSONSerialization.dataWithJSONObject(self.dictionary, options: options)
11 | default:
12 | return nil
13 | }
14 |
15 | }
16 |
17 | public func stringify(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> Swift.String? {
18 | switch self {
19 |
20 | case .JArray(_):
21 | let data = try! NSJSONSerialization.dataWithJSONObject(self.array, options: options)
22 | let count = data.length / sizeof(UInt8)
23 |
24 | // create array of appropriate length:
25 | var array = [UInt8](count: count, repeatedValue: 0)
26 |
27 | // copy bytes into array
28 | data.getBytes(&array, length:count * sizeof(UInt8))
29 |
30 |
31 | return Swift.String(bytes: array, encoding: NSUTF8StringEncoding)
32 |
33 |
34 | case .JDictionary(_):
35 | let data = try! NSJSONSerialization.dataWithJSONObject(self.dictionary, options: options)
36 | let count = data.length / sizeof(UInt8)
37 |
38 | // create array of appropriate length:
39 | var array = [UInt8](count: count, repeatedValue: 0)
40 |
41 | // copy bytes into array
42 | data.getBytes(&array, length:count * sizeof(UInt8))
43 |
44 |
45 | return Swift.String(bytes: array, encoding: NSUTF8StringEncoding)
46 |
47 |
48 | default:
49 | return nil
50 | }
51 |
52 | }
53 |
54 |
55 | }
56 |
57 | extension JSONDictionary {
58 | public func jsonData(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> NSData? {
59 | switch self {
60 | case .JDictionary(_):
61 | return try! NSJSONSerialization.dataWithJSONObject(self.dictionary, options: options)
62 |
63 | }
64 |
65 | }
66 |
67 | public func stringify(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> Swift.String? {
68 | switch self {
69 |
70 | case .JDictionary(_):
71 | let data = try! NSJSONSerialization.dataWithJSONObject(self.dictionary, options: options)
72 | let count = data.length / sizeof(UInt8)
73 |
74 | // create array of appropriate length:
75 | var array = [UInt8](count: count, repeatedValue: 0)
76 |
77 | // copy bytes into array
78 | data.getBytes(&array, length:count * sizeof(UInt8))
79 |
80 |
81 | return String(bytes: array, encoding: NSUTF8StringEncoding)
82 |
83 |
84 | }
85 |
86 | }
87 |
88 |
89 | }
90 |
91 | extension JSONArray {
92 | public func jsonData(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> NSData? {
93 | switch self {
94 |
95 | case .JArray(_):
96 | return try! NSJSONSerialization.dataWithJSONObject(self.array, options: options)
97 | }
98 |
99 | }
100 |
101 | public func stringify(options:NSJSONWritingOptions = [], error:NSErrorPointer = nil) -> Swift.String? {
102 | switch self {
103 |
104 | case .JArray(_):
105 | let data = try! NSJSONSerialization.dataWithJSONObject(self.array, options: options)
106 | let count = data.length / sizeof(UInt8)
107 |
108 | // create array of appropriate length:
109 | var array = [UInt8](count: count, repeatedValue: 0)
110 |
111 | // copy bytes into array
112 | data.getBytes(&array, length:count * sizeof(UInt8))
113 |
114 |
115 | return Swift.String(bytes: array, encoding: NSUTF8StringEncoding)
116 |
117 |
118 | }
119 |
120 | }
121 |
122 |
123 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_InitializerExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // MARK: Initializers
4 | extension JSONValue {
5 | public init (_ num:NSNumber) {
6 | self = .Number(num)
7 | }
8 |
9 | public init (_ str:String) {
10 | self = .JString(str)
11 | }
12 | public init (_ val:NSNull) {
13 | self = .Null
14 | }
15 | public init (_ bool:Bool) {
16 | self = .JBool(bool)
17 | }
18 | public init (_ arr:[JSONValue]) {
19 | self = .JArray(arr)
20 | }
21 |
22 | public init (_ dict:[String:JSONValue]) {
23 | self = .JDictionary(dict)
24 | }
25 | }
26 |
27 |
28 | // MARK: AnyObject handlers
29 | extension JSONValue {
30 | public init (value:AnyObject) {
31 |
32 | if let a = value as? String {
33 | self = .JString(a)
34 | }
35 | else if let a = value as? NSNumber {
36 | if a.isBoolNumber() {
37 | self = .JBool(a.boolValue)
38 | }
39 | else {
40 | self = .Number(a)
41 | }
42 | }
43 | else if let _ = value as? NSNull {
44 | self = .Null
45 | }
46 | else if let a = value as? [AnyObject] {
47 | self = JSONValue(array:a)
48 | }
49 | else if let a = value as? [String:AnyObject] {
50 | self = JSONValue(dictionary:a)
51 | }
52 | else {
53 | self = .Null
54 | }
55 |
56 | }
57 |
58 | public init (array:[AnyObject]) {
59 | self = JSONValue(array.map{JSONValue(value:$0)})
60 | }
61 | public init (dictionary:[String: AnyObject]) {
62 | let initial = [String: JSONValue]()
63 | self = JSONValue(dictionary.reduce(initial, combine: { (var dict, val) in
64 | dict[val.0] = JSONValue(value:val.1)
65 | return dict
66 | }))
67 | }
68 | }
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/JSONValue/_SequenceExtensions.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 | protocol JSONGeneratorProtocol:GeneratorType {
3 |
4 | }
5 | // MARK: Sequence type
6 | // FIXME: Necessary to repeat generator for JSONValue.JDictionary and JSONDictionary?
7 | // Generator
8 | public struct JSONGenerator:JSONGeneratorProtocol {
9 | // use dictionary with index as keys for position in array
10 | let value:JSONValue
11 | var indexInSequence:Int = 0
12 |
13 | init(value:JSONValue) {
14 | self.value = value
15 |
16 | }
17 |
18 | mutating public func next() -> (String,JSONValue)? {
19 | switch value {
20 | case .JArray (let Array) where !Array.isEmpty:
21 |
22 | if indexInSequence < Array.count
23 | { let element = Array[indexInSequence]
24 | ++indexInSequence
25 | return ("",element)
26 | }
27 | else { indexInSequence = 0
28 | return nil
29 | }
30 | case .JString (let jsonString) where jsonString.characters.count > 0:
31 | let Array = jsonString.characters.map{JSONValue(String($0))}
32 | if indexInSequence < Array.count
33 | { let element = Array[indexInSequence]
34 | ++indexInSequence
35 | return ("",element)
36 | }
37 | else { indexInSequence = 0
38 | return nil
39 | }
40 | case .JDictionary (let dictionary):
41 | let keyArray = [String](dictionary.keys)
42 | if indexInSequence < dictionary.count
43 | { let key = keyArray[indexInSequence]
44 | let element = dictionary[key]
45 | ++indexInSequence
46 | return (key,element!)
47 | }
48 | else {
49 | indexInSequence = 0
50 | return nil
51 | }
52 |
53 | default:
54 | return nil
55 | }
56 | }
57 | }
58 |
59 |
60 |
61 |
62 | extension JSONValue: SequenceType {
63 |
64 | public typealias Generator = JSONGenerator
65 |
66 | public func generate() -> Generator {
67 |
68 | let gen = Generator(value: self)
69 | return gen
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Protocols/Array_Protocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Array_Protocol.swift
3 | // Aldwych_2_1
4 | //
5 | // Created by A J LEVINGS on 25/07/2015.
6 | // Copyright © 2015 Gylphi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol JSONArrayProtocol {
12 |
13 | typealias Index: ForwardIndexType
14 | var array:[AnyObject] { get }
15 | var nsArray:NSArray { get }
16 |
17 | var count:Int { get }
18 |
19 |
20 | var startIndex:Index { get }
21 | var endIndex:Index { get }
22 | /*
23 | subscript (key:Int) -> JSONDictionary? { get set }
24 | subscript (key:Int) -> JSONArray? { get set }
25 | */
26 |
27 | subscript (position:Self.Index) -> JSONValue { get set }
28 | subscript (position:Self.Index) -> AnyObject { get set }
29 | subscript (position:Self.Index, typesafe:TypeSafety) -> AnyObject { get set }
30 |
31 | mutating func append(value:AnyObject)
32 | mutating func append(arr:JSONValue)
33 |
34 | mutating func insert(str:AnyObject, atIndex ind:Int)
35 |
36 | mutating func extend(arr:[AnyObject])
37 | mutating func extend(arr:[JSONValue])
38 |
39 | mutating func removeLast()
40 | mutating func removeAtIndex(index:Int)
41 |
42 | func jsonData(options:NSJSONWritingOptions, error:NSErrorPointer) -> NSData?
43 | func stringify(options:NSJSONWritingOptions, error:NSErrorPointer) -> String?
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Protocols/Dictionary_Protocol.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DictionaryProtocol.swift
3 | // Aldwych_2_1
4 | //
5 | // Created by A J LEVINGS on 25/07/2015.
6 | // Copyright © 2015 Gylphi. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | public protocol JSONDictionaryProtocol {
12 | var dictionary:Dictionary { get }
13 | var nsDictionary:NSDictionary { get }
14 | /*
15 | subscript (key:String) -> JSONDictionary? { get set }
16 | subscript (key:String) -> JSONArray? { get set }
17 | */
18 | subscript (key:String) -> JSONValue? { get set }
19 | subscript (key:String) -> AnyObject? { get set }
20 |
21 | func jsonData(options:NSJSONWritingOptions, error:NSErrorPointer) -> NSData?
22 | func stringify(options:NSJSONWritingOptions, error:NSErrorPointer) -> String?
23 |
24 | func updateValue(value:JSONValue, forKey key:String)
25 | func updateValue(value:AnyObject, forKey key:String, typesafe:TypeSafety)
26 |
27 | mutating func nullValueForKey(key:String)
28 |
29 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/Sources/Protocols/JSONType_Protocol.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | // FIXME: JSONValueType not currently used (error raised)
4 | public protocol JSONValueType {
5 |
6 | var str:String? { get }
7 | var strOpt:String?? { get }
8 |
9 | var num:NSNumber? { get }
10 | var numOpt:NSNumber?? { get }
11 |
12 | var bool:Bool? { get }
13 | var boolOpt:Bool?? { get }
14 | var null:NSNull? { get }
15 |
16 | var jsonArray:JSONArray? { get }
17 | var jsonArrayOpt:JSONArray?? { get }
18 | var jsonDictionary:JSONDictionary? { get }
19 | var jsonDictionaryOpt:JSONDictionary?? { get }
20 |
21 | // subscripting Arrays
22 |
23 | subscript (key:Int) -> JSONValue? {get set}
24 | subscript (key:Int) -> String? {get set}
25 | subscript (key:Int) -> NSNumber? {get set}
26 | subscript (key:Int) -> Bool? {get set}
27 | subscript (key:Int) -> NSNull? { get set }
28 |
29 | // subscripting Dictionaries
30 | subscript (key:String) -> JSONValue? {get set}
31 | subscript (key:String) -> String? {get set}
32 | subscript (key:String) -> NSNumber? {get set}
33 | subscript (key:String) -> Bool? {get set}
34 | subscript (key:String) -> NSNull? { get set }
35 | }
36 |
37 |
38 | public protocol JSONParserType {
39 |
40 | static func parse(urlPath urlPath:String) throws -> JSONObjectType
41 | static func parse(fileNamed fileName:String) throws -> JSONObjectType
42 | static func parse(url:NSURL) throws -> JSONObjectType
43 | static func parse(json:NSData) throws -> JSONObjectType
44 |
45 | }
46 | public protocol JSONObjectType {
47 |
48 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/contents.xcplayground:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Aldwych2.playground/playground.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Aldwych2.playground/playground.xcworkspace/xcshareddata/Aldwych2.xcscmblueprint:
--------------------------------------------------------------------------------
1 | {
2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "F51B9556BA35A43D854EDC51F22399E51CD95520",
3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
4 |
5 | },
6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
7 | "D7432D4CE4D5866B9AD7A60910EA08E3FC1E7CB0" : 0,
8 | "F51B9556BA35A43D854EDC51F22399E51CD95520" : 0
9 | },
10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "DB2FB01B-CFDB-4BB4-99DC-F72F73E0FCD8",
11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
12 | "D7432D4CE4D5866B9AD7A60910EA08E3FC1E7CB0" : "",
13 | "F51B9556BA35A43D854EDC51F22399E51CD95520" : "Aldwych-2-JSON-Parser-for-Swift-\/"
14 | },
15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "Aldwych2",
16 | "DVTSourceControlWorkspaceBlueprintVersion" : 203,
17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "Aldwych2.playground",
18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
19 | {
20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/sketchytech\/Aldwych-2-JSON-Parser-for-Swift-.git",
21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D7432D4CE4D5866B9AD7A60910EA08E3FC1E7CB0"
23 | },
24 | {
25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/sketchytech\/Aldwych-2-JSON-Parser-for-Swift-.git",
26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "F51B9556BA35A43D854EDC51F22399E51CD95520"
28 | }
29 | ]
30 | }
--------------------------------------------------------------------------------
/Aldwych2.playground/playground.xcworkspace/xcuserdata/anthonylevings.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/sketchytech/Aldwych-2-JSON-Parser-for-Swift/18ead6bee03654e5c6ef8aff8a2f99317978c386/Aldwych2.playground/playground.xcworkspace/xcuserdata/anthonylevings.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Aldwych2.playground/xcuserdata/anthonylevings.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Aldwych 2.0 (JSON Parser for Swift)
2 | A new version of [Aldwych](https://github.com/sketchytech/Aldwych_JSON_Swift) that is centred around recursive enums. Clone and explore the playground to learn more.
3 |
4 | Works with Xcode 7, iOS 9.
5 |
--------------------------------------------------------------------------------