├── 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 += "" 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 | --------------------------------------------------------------------------------