Jelajahi Sumber

Merge branch 'master' of git://github.com/zarghol/swifter into zarghol-master

# Conflicts:
#	Common/HttpServer.swift
Damian Kolakowski 10 tahun lalu
induk
melakukan
ddfa121489

+ 19 - 8
Common/DemoServer.swift

@@ -12,8 +12,10 @@ func demoServer(publicDir: String?) -> HttpServer {
     if let publicDir = publicDir {
         server["/resources/(.+)"] = HttpHandlers.directory(publicDir)
     }
+    
     server["/files(.+)"] = HttpHandlers.directoryBrowser("~/")
-    server["/magic"] = { .OK(.HTML("You asked for " + $0.url)) }
+    server["/magic"] = { .OK(.Html("You asked for " + $0.url)) }
+    
     server["/test"] = { request in
         var headersInfo = ""
         for (name, value) in request.headers {
@@ -23,31 +25,37 @@ func demoServer(publicDir: String?) -> HttpServer {
         for (name, value) in request.urlParams {
             queryParamsInfo += "\(name) : \(value)<br>"
         }
-        return .OK(.HTML("<h3>Address: \(request.address)</h3><h3>Url:</h3> \(request.url)<h3>Method: \(request.method)</h3><h3>Headers:</h3>\(headersInfo)<h3>Query:</h3>\(queryParamsInfo)"))
+        return .OK(.Html("<h3>Address: \(request.address)</h3><h3>Url:</h3> \(request.url)<h3>Method: \(request.method)</h3><h3>Headers:</h3>\(headersInfo)<h3>Query:</h3>\(queryParamsInfo)"))
     }
+    
     server["/params/(.+)/(.+)"] = { request in
         var capturedGroups = ""
         for (index, group) in request.capturedUrlGroups.enumerate() {
             capturedGroups += "Expression group \(index) : \(group)<br>"
         }
-        return .OK(.HTML("Url: \(request.url)<br>Method: \(request.method)<br>\(capturedGroups)"))
+        return .OK(.Html("Url: \(request.url)<br>Method: \(request.method)<br>\(capturedGroups)"))
     }
+    
     server["/json"] = { request in
-        return .OK(.JSON(["posts" : [[ "id" : 1, "message" : "hello world"],[ "id" : 2, "message" : "sample message"]], "new_updates" : false]))
+        return .OK(.Json(["posts" : [[ "id" : 1, "message" : "hello world"],[ "id" : 2, "message" : "sample message"]], "new_updates" : false]))
     }
+    
     server["/redirect"] = { request in
         return .MovedPermanently("http://www.google.com")
     }
+    
     server["/long"] = { request in
         var longResponse = ""
         for k in 0..<1000 { longResponse += "(\(k)),->" }
-        return .OK(.HTML(longResponse))
+        return .OK(.Html(longResponse))
     }
+    
     server["/demo"] = { request in
-        return .OK(.HTML("<center><h2>Hello Swift</h2>" +
+        return .OK(.Html("<center><h2>Hello Swift</h2>" +
             "<img src=\"https://devimages.apple.com.edgekey.net/swift/images/swift-hero_2x.png\"/><br>" +
             "</center>"))
     }
+    
     server["/login"] = { request in
         switch request.method.uppercaseString {
             case "GET":
@@ -60,19 +68,22 @@ func demoServer(publicDir: String?) -> HttpServer {
                 }
             case "POST":
                 let formFields = request.parseForm()
-                return HttpResponse.OK(.HTML(formFields.map({ "\($0.0) = \($0.1)" }).joinWithSeparator("<br>")))
+                return HttpResponse.OK(.Html(formFields.map({ "\($0.0) = \($0.1)" }).joinWithSeparator("<br>")))
             default:
                 return .NotFound
         }
         return .NotFound
     }
+    
     server["/raw"] = { request in
         return HttpResponse.RAW(200, "OK", ["XXX-Custom-Header": "value"], "Sample Response".dataUsingEncoding(NSUTF8StringEncoding)!)
     }
+    
     server["/"] = { request in
         var listPage = "Available services:<br><ul>"
         listPage += server.routes.map({ "<li><a href=\"\($0)\">\($0)</a></li>"}).joinWithSeparator("")
-        return .OK(.HTML(listPage))
+        return .OK(.Html(listPage))
     }
+
     return server
 }

+ 1 - 1
Common/HttpHandlers.swift

@@ -34,7 +34,7 @@ public class HttpHandlers {
                             var response = "<h3>\(filePath)</h3></br><table>"
                             response += files.map({ "<tr><td><a href=\"\(request.url)/\($0)\">\($0)</a></td></tr>"}).joinWithSeparator("")
                             response += "</table>"
-                            return HttpResponse.OK(.HTML(response))
+                            return HttpResponse.OK(.Html(response))
                         } catch  {
                             return HttpResponse.NotFound
                         }

+ 99 - 45
Common/HttpResponse.swift

@@ -6,52 +6,104 @@
 
 import Foundation
 
+public enum SerializationError: ErrorType {
+    case InvalidObject
+    case NotSupported
+    case EncodingError
+}
+
+public protocol Serializer {
+    func serialize(object: Any) throws -> String
+}
+
+public class JSONSerializer : Serializer {
+    public func serialize(object: Any) throws -> String {
+        guard let obj = object as? AnyObject where NSJSONSerialization.isValidJSONObject(obj) else {
+            throw SerializationError.InvalidObject
+        }
+        
+        let json = try NSJSONSerialization.dataWithJSONObject(obj, options: NSJSONWritingOptions.PrettyPrinted)
+        
+        guard let string = String(data: json, encoding: NSUTF8StringEncoding) else {
+            throw SerializationError.EncodingError
+        }
+        
+        return string
+    }
+    
+    private static func serialize(object: Any) throws -> String {
+        let serializer = JSONSerializer()
+        return try serializer.serialize(object)
+    }
+}
+
+public class XMLSerializer: Serializer {
+    public func serialize(object: Any) throws -> String {
+        throw SerializationError.NotSupported
+    }
+    
+    private static func serialize(object: Any) throws -> String {
+        let serializer = XMLSerializer()
+        return try serializer.serialize(object)
+    }
+}
+
+public class PLISTSerializer: Serializer {
+    public func serialize(object: Any) throws -> String {
+        let format = NSPropertyListFormat.XMLFormat_v1_0
+        
+        guard let obj = object as? AnyObject where NSPropertyListSerialization.propertyList(obj, isValidForFormat: format) else {
+            throw SerializationError.InvalidObject
+        }
+        
+        let plist = try NSPropertyListSerialization.dataWithPropertyList(obj, format: format, options: 0)
+        
+        guard let string = String(data: plist, encoding: NSUTF8StringEncoding) else {
+            throw SerializationError.EncodingError
+        }
+        
+        return string
+    }
+    
+    private static func serialize(object: Any) throws -> String {
+        let serializer = PLISTSerializer()
+        return try serializer.serialize(object)
+    }
+}
+
 public enum HttpResponseBody {
     
-    case JSON(AnyObject)
-    case XML(AnyObject)
-    case PLIST(AnyObject)
-    case HTML(String)
-    case STRING(String)
+    case Json(AnyObject)
+    case Xml(AnyObject)
+    case Plist(AnyObject)
+    case Html(String)
+    case Text(String)
+    case Custom(Serializer, Any)
     
     func data() -> String? {
-        switch self {
-            
-        case .JSON(let object):
-            if NSJSONSerialization.isValidJSONObject(object) {
-                do {
-                    let json = try NSJSONSerialization.dataWithJSONObject(object, options: NSJSONWritingOptions.PrettyPrinted)
-                    if let nsString = NSString(data: json, encoding: NSUTF8StringEncoding) {
-                        return nsString as String
-                    }
-                } catch let serializationError as NSError {
-                    return "Serialisation error: \(serializationError)"
-                }
+        do {
+            switch self {
+                
+            case .Json(let object):
+                return try JSONSerializer.serialize(object)
+                
+            case .Xml(let object):
+                return try XMLSerializer.serialize(object)
+                
+            case .Plist(let object):
+                return try PLISTSerializer.serialize(object)
+                
+            case .Text(let body):
+                return body
+                
+            case .Html(let body):
+                return "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
+                
+            case .Custom(let serializer, let object):
+                return try serializer.serialize(object)
             }
-            return "Invalid object to serialise."
-            
-        case .XML(_):
-            return "XML serialization not supported."
-            
-        case .PLIST(let object):
-            let format = NSPropertyListFormat.XMLFormat_v1_0
-            if NSPropertyListSerialization.propertyList(object, isValidForFormat: format) {
-                do {
-                    let plist = try NSPropertyListSerialization.dataWithPropertyList(object, format: format, options: 0)
-                    if let nsString = NSString(data: plist, encoding: NSUTF8StringEncoding) {
-                        return nsString as String
-                    }
-                } catch let serializationError as NSError {
-                    return "Serialisation error: \(serializationError)"
-                }
-            }
-            return "Invalid object to serialise."
-            
-        case .STRING(let body):
-            return body
-            
-        case .HTML(let body):
-            return "<html><meta charset=\"UTF-8\"><body>\(body)</body></html>"
+        } catch {
+            return "Serialisation error: \(error)"
         }
     }
 }
@@ -100,15 +152,15 @@ public enum HttpResponse {
         switch self {
 		case .OK(let body):
             switch body {
-                case .JSON(_)   : headers["Content-Type"] = "application/json"
-                case .PLIST(_)  : headers["Content-Type"] = "application/xml"
-                case .XML(_)    : headers["Content-Type"] = "application/xml"
+                case .Json(_)   : headers["Content-Type"] = "application/json"
+                case .Plist(_)  : headers["Content-Type"] = "application/xml"
+                case .Xml(_)    : headers["Content-Type"] = "application/xml"
                 // 'application/xml' or 'text/xml' ?
                 // From RFC: http://www.rfc-editor.org/rfc/rfc3023.txt - "If an XML document -- that is, the unprocessed, 
                 // source XML document -- is readable by casual users, text/xml is preferable to application/xml. 
                 // MIME user agents (and web user agents) that do not have explicit support for text/xml will treat it as text/plain, 
                 // for example, by displaying the XML MIME entity as plain text.
-                case .HTML(_)   : headers["Content-Type"] = "text/html"
+                case .Html(_)   : headers["Content-Type"] = "text/html"
                 default:break
             }
         case .MovedPermanently(let location): headers["Location"] = location
@@ -146,3 +198,5 @@ public enum HttpResponse {
 func ==(inLeft: HttpResponse, inRight: HttpResponse) -> Bool {
 	return inLeft.statusCode() == inRight.statusCode()
 }
+
+

+ 5 - 5
Common/HttpServer.swift

@@ -25,14 +25,14 @@ public class HttpServer
                 let regex = try NSRegularExpression(pattern: path, options: self.expressionOptions)
                 if let newHandler = newValue {
                     self.handlers.append(expression: regex, handler: newHandler)
+                    // Longer patterns will have higher priority.
+                    self.handlers = self.handlers.sort { $0.0.pattern > $1.0.pattern }
                 }
             } catch  {
                 print("Could not register handler for: \(path), error: \(error)")
             }
         }
-        get {
-            return nil
-        }
+        get { return nil }
     }
     
     public var routes:[String] {
@@ -77,7 +77,7 @@ public class HttpServer
             self.stop()
         }
     }
-    
+
     public func stop() {
         self.listenSocket.release()
         HttpServer.lock(self.clientSocketsLock) {
@@ -99,7 +99,7 @@ public class HttpServer
                 }
             }
         }
-		return nil
+        return nil
     }
     
     private func captureExpressionGroups(expression: NSRegularExpression, value: String) -> [String] {

+ 44 - 46
Swifter.xcodeproj/project.pbxproj

@@ -7,12 +7,9 @@
 	objects = {
 
 /* Begin PBXBuildFile section */
+		18E610A71BD6397D00B7D17A /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18E610A51BD6397D00B7D17A /* SwiftyJSON.swift */; };
 		7C71C5B01A1D52F800682BF0 /* login.html in CopyFiles */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
 		7C71C5B11A1EC49B00682BF0 /* logo.png in CopyFiles */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
-		7C839B7419422CFF003A6950 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B7319422CFF003A6950 /* AppDelegate.swift */; };
-		7C839B7619422CFF003A6950 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7C839B7519422CFF003A6950 /* ViewController.swift */; };
-		7C839B7919422CFF003A6950 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7C839B7719422CFF003A6950 /* Main.storyboard */; };
-		7C839B7B19422CFF003A6950 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7C839B7A19422CFF003A6950 /* Images.xcassets */; };
 		7CA4813E19A2EA8D0030B30D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4813D19A2EA8D0030B30D /* main.swift */; };
 		7CA4814E19A2EED00030B30D /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814A19A2EED00030B30D /* HttpParser.swift */; };
 		7CA4814F19A2EED00030B30D /* HttpParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CA4814A19A2EED00030B30D /* HttpParser.swift */; };
@@ -31,6 +28,11 @@
 		7CB102DD1A167FFA00CBA3B4 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */; };
 		7CB102DE1A1680EA00CBA3B4 /* DemoServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */; };
 		7CB102E01A17381D00CBA3B4 /* logo.png in Resources */ = {isa = PBXBuildFile; fileRef = 7CB102DF1A17381D00CBA3B4 /* logo.png */; };
+		7CDAB8121BE2A1D400C8A977 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAB80C1BE2A1D400C8A977 /* AppDelegate.swift */; };
+		7CDAB8131BE2A1D400C8A977 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 7CDAB80D1BE2A1D400C8A977 /* Main.storyboard */; };
+		7CDAB8141BE2A1D400C8A977 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 7CDAB80F1BE2A1D400C8A977 /* Images.xcassets */; };
+		7CDAB8151BE2A1D400C8A977 /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 7CDAB8101BE2A1D400C8A977 /* Info.plist */; };
+		7CDAB8161BE2A1D400C8A977 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7CDAB8111BE2A1D400C8A977 /* ViewController.swift */; };
 		98630C071A1C9A9D00478D08 /* login.html in Resources */ = {isa = PBXBuildFile; fileRef = 98630C061A1C9A9D00478D08 /* login.html */; };
 /* End PBXBuildFile section */
 
@@ -50,12 +52,8 @@
 /* End PBXCopyFilesBuildPhase section */
 
 /* Begin PBXFileReference section */
-		7C839B6E19422CFF003A6950 /* Swifter.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Swifter.app; sourceTree = BUILT_PRODUCTS_DIR; };
-		7C839B7219422CFF003A6950 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		7C839B7319422CFF003A6950 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
-		7C839B7519422CFF003A6950 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
-		7C839B7819422CFF003A6950 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
-		7C839B7A19422CFF003A6950 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		18E610A51BD6397D00B7D17A /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyJSON.swift; sourceTree = "<group>"; };
+		7C839B6E19422CFF003A6950 /* SwifteriOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SwifteriOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
 		7CA4813B19A2EA8D0030B30D /* SwifterOSX */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = SwifterOSX; sourceTree = BUILT_PRODUCTS_DIR; };
 		7CA4813D19A2EA8D0030B30D /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = "<group>"; };
 		7CA4814A19A2EED00030B30D /* HttpParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpParser.swift; sourceTree = "<group>"; };
@@ -67,6 +65,11 @@
 		7CB102D91A1664B200CBA3B4 /* HttpHandlers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HttpHandlers.swift; sourceTree = "<group>"; };
 		7CB102DC1A167FFA00CBA3B4 /* DemoServer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DemoServer.swift; sourceTree = "<group>"; };
 		7CB102DF1A17381D00CBA3B4 /* logo.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = logo.png; sourceTree = "<group>"; };
+		7CDAB80C1BE2A1D400C8A977 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
+		7CDAB80E1BE2A1D400C8A977 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+		7CDAB80F1BE2A1D400C8A977 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+		7CDAB8101BE2A1D400C8A977 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		7CDAB8111BE2A1D400C8A977 /* ViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
 		98630C061A1C9A9D00478D08 /* login.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = login.html; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
@@ -93,7 +96,7 @@
 			children = (
 				7CA4815619A2EF2B0030B30D /* Resources */,
 				7CA4814919A2EED00030B30D /* Common */,
-				7C839B7019422CFF003A6950 /* Swifter */,
+				7CDAB80B1BE2A1D400C8A977 /* SwifteriOS */,
 				7CA4813C19A2EA8D0030B30D /* SwifterOSX */,
 				7C839B6F19422CFF003A6950 /* Products */,
 			);
@@ -102,36 +105,17 @@
 		7C839B6F19422CFF003A6950 /* Products */ = {
 			isa = PBXGroup;
 			children = (
-				7C839B6E19422CFF003A6950 /* Swifter.app */,
+				7C839B6E19422CFF003A6950 /* SwifteriOS.app */,
 				7CA4813B19A2EA8D0030B30D /* SwifterOSX */,
 			);
 			name = Products;
 			sourceTree = "<group>";
 		};
-		7C839B7019422CFF003A6950 /* Swifter */ = {
-			isa = PBXGroup;
-			children = (
-				7C839B7319422CFF003A6950 /* AppDelegate.swift */,
-				7C839B7519422CFF003A6950 /* ViewController.swift */,
-				7C839B7719422CFF003A6950 /* Main.storyboard */,
-				7C839B7A19422CFF003A6950 /* Images.xcassets */,
-				7C839B7119422CFF003A6950 /* Supporting Files */,
-			);
-			path = Swifter;
-			sourceTree = "<group>";
-		};
-		7C839B7119422CFF003A6950 /* Supporting Files */ = {
-			isa = PBXGroup;
-			children = (
-				7C839B7219422CFF003A6950 /* Info.plist */,
-			);
-			name = "Supporting Files";
-			sourceTree = "<group>";
-		};
 		7CA4813C19A2EA8D0030B30D /* SwifterOSX */ = {
 			isa = PBXGroup;
 			children = (
 				7CA4813D19A2EA8D0030B30D /* main.swift */,
+				18E610A51BD6397D00B7D17A /* SwiftyJSON.swift */,
 			);
 			path = SwifterOSX;
 			sourceTree = "<group>";
@@ -160,12 +144,24 @@
 			path = Resources;
 			sourceTree = "<group>";
 		};
+		7CDAB80B1BE2A1D400C8A977 /* SwifteriOS */ = {
+			isa = PBXGroup;
+			children = (
+				7CDAB80C1BE2A1D400C8A977 /* AppDelegate.swift */,
+				7CDAB80D1BE2A1D400C8A977 /* Main.storyboard */,
+				7CDAB80F1BE2A1D400C8A977 /* Images.xcassets */,
+				7CDAB8101BE2A1D400C8A977 /* Info.plist */,
+				7CDAB8111BE2A1D400C8A977 /* ViewController.swift */,
+			);
+			path = SwifteriOS;
+			sourceTree = "<group>";
+		};
 /* End PBXGroup section */
 
 /* Begin PBXNativeTarget section */
-		7C839B6D19422CFF003A6950 /* Swifter */ = {
+		7C839B6D19422CFF003A6950 /* SwifteriOS */ = {
 			isa = PBXNativeTarget;
-			buildConfigurationList = 7C839B8A19422D00003A6950 /* Build configuration list for PBXNativeTarget "Swifter" */;
+			buildConfigurationList = 7C839B8A19422D00003A6950 /* Build configuration list for PBXNativeTarget "SwifteriOS" */;
 			buildPhases = (
 				7C839B6A19422CFF003A6950 /* Sources */,
 				7C839B6B19422CFF003A6950 /* Frameworks */,
@@ -175,9 +171,9 @@
 			);
 			dependencies = (
 			);
-			name = Swifter;
+			name = SwifteriOS;
 			productName = Swifter;
-			productReference = 7C839B6E19422CFF003A6950 /* Swifter.app */;
+			productReference = 7C839B6E19422CFF003A6950 /* SwifteriOS.app */;
 			productType = "com.apple.product-type.application";
 		};
 		7CA4813A19A2EA8D0030B30D /* SwifterOSX */ = {
@@ -228,7 +224,7 @@
 			projectDirPath = "";
 			projectRoot = "";
 			targets = (
-				7C839B6D19422CFF003A6950 /* Swifter */,
+				7C839B6D19422CFF003A6950 /* SwifteriOS */,
 				7CA4813A19A2EA8D0030B30D /* SwifterOSX */,
 			);
 		};
@@ -240,10 +236,11 @@
 			buildActionMask = 2147483647;
 			files = (
 				7CB102E01A17381D00CBA3B4 /* logo.png in Resources */,
-				7C839B7919422CFF003A6950 /* Main.storyboard in Resources */,
+				7CDAB8141BE2A1D400C8A977 /* Images.xcassets in Resources */,
+				7CDAB8151BE2A1D400C8A977 /* Info.plist in Resources */,
 				98630C071A1C9A9D00478D08 /* login.html in Resources */,
-				7C839B7B19422CFF003A6950 /* Images.xcassets in Resources */,
 				7CA4815819A2EF2B0030B30D /* test.json in Resources */,
+				7CDAB8131BE2A1D400C8A977 /* Main.storyboard in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -254,15 +251,15 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				7CDAB8161BE2A1D400C8A977 /* ViewController.swift in Sources */,
 				7CA4815019A2EED00030B30D /* HttpResponse.swift in Sources */,
 				7CB102DA1A1664B200CBA3B4 /* HttpHandlers.swift in Sources */,
 				7CB102DD1A167FFA00CBA3B4 /* DemoServer.swift in Sources */,
-				7C839B7619422CFF003A6950 /* ViewController.swift in Sources */,
+				7CDAB8121BE2A1D400C8A977 /* AppDelegate.swift in Sources */,
 				7CA4815419A2EED00030B30D /* Socket.swift in Sources */,
 				7CA4815219A2EED00030B30D /* HttpServer.swift in Sources */,
 				7CA4814E19A2EED00030B30D /* HttpParser.swift in Sources */,
 				7CA4815B19A2F6A60030B30D /* HttpRequest.swift in Sources */,
-				7C839B7419422CFF003A6950 /* AppDelegate.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -276,6 +273,7 @@
 				7CA4815519A2EED00030B30D /* Socket.swift in Sources */,
 				7CA4815319A2EED00030B30D /* HttpServer.swift in Sources */,
 				7CA4813E19A2EA8D0030B30D /* main.swift in Sources */,
+				18E610A71BD6397D00B7D17A /* SwiftyJSON.swift in Sources */,
 				7CB102DB1A16657200CBA3B4 /* HttpHandlers.swift in Sources */,
 				7CA4815C19A2F6A60030B30D /* HttpRequest.swift in Sources */,
 			);
@@ -284,10 +282,10 @@
 /* End PBXSourcesBuildPhase section */
 
 /* Begin PBXVariantGroup section */
-		7C839B7719422CFF003A6950 /* Main.storyboard */ = {
+		7CDAB80D1BE2A1D400C8A977 /* Main.storyboard */ = {
 			isa = PBXVariantGroup;
 			children = (
-				7C839B7819422CFF003A6950 /* Base */,
+				7CDAB80E1BE2A1D400C8A977 /* Base */,
 			);
 			name = Main.storyboard;
 			sourceTree = "<group>";
@@ -382,7 +380,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CLANG_ENABLE_MODULES = YES;
-				INFOPLIST_FILE = Swifter/Info.plist;
+				INFOPLIST_FILE = "$(SRCROOT)/SwifteriOS/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "pl.kolakowski..${PRODUCT_NAME:rfc1034identifier}";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -397,7 +395,7 @@
 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
 				ASSETCATALOG_COMPILER_LAUNCHIMAGE_NAME = LaunchImage;
 				CLANG_ENABLE_MODULES = YES;
-				INFOPLIST_FILE = Swifter/Info.plist;
+				INFOPLIST_FILE = "$(SRCROOT)/SwifteriOS/Info.plist";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
 				PRODUCT_BUNDLE_IDENTIFIER = "pl.kolakowski..${PRODUCT_NAME:rfc1034identifier}";
 				PRODUCT_NAME = "$(TARGET_NAME)";
@@ -450,7 +448,7 @@
 			defaultConfigurationIsVisible = 0;
 			defaultConfigurationName = Release;
 		};
-		7C839B8A19422D00003A6950 /* Build configuration list for PBXNativeTarget "Swifter" */ = {
+		7C839B8A19422D00003A6950 /* Build configuration list for PBXNativeTarget "SwifteriOS" */ = {
 			isa = XCConfigurationList;
 			buildConfigurations = (
 				7C839B8B19422D00003A6950 /* Debug */,

TEMPAT SAMPAH
Swifter.xcodeproj/project.xcworkspace/xcuserdata/damiankolakowski.xcuserdatad/UserInterfaceState.xcuserstate


+ 31 - 31
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist

@@ -202,11 +202,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "466971590.533081"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "40"
-            endingLineNumber = "40"
+            startingLineNumber = "38"
+            endingLineNumber = "38"
             landmarkName = "HttpServer"
             landmarkType = "3">
          </BreakpointContent>
@@ -218,13 +218,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "466971590.533081"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "42"
-            endingLineNumber = "42"
-            landmarkName = "start(_:)"
-            landmarkType = "5">
+            startingLineNumber = "40"
+            endingLineNumber = "40"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -234,13 +234,13 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "466971590.533081"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "43"
-            endingLineNumber = "43"
-            landmarkName = "start(_:)"
-            landmarkType = "5">
+            startingLineNumber = "41"
+            endingLineNumber = "41"
+            landmarkName = "HttpServer"
+            landmarkType = "3">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy
@@ -266,11 +266,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/DemoServer.swift"
-            timestampString = "456083936.619071"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "54"
-            endingLineNumber = "54"
+            startingLineNumber = "62"
+            endingLineNumber = "62"
             landmarkName = "demoServer(_:)"
             landmarkType = "7">
          </BreakpointContent>
@@ -282,11 +282,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/DemoServer.swift"
-            timestampString = "456083936.619071"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "56"
-            endingLineNumber = "56"
+            startingLineNumber = "64"
+            endingLineNumber = "64"
             landmarkName = "demoServer(_:)"
             landmarkType = "7">
          </BreakpointContent>
@@ -298,11 +298,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/DemoServer.swift"
-            timestampString = "466766836.996785"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "64"
-            endingLineNumber = "64"
+            startingLineNumber = "72"
+            endingLineNumber = "72"
             landmarkName = "demoServer(_:)"
             landmarkType = "7">
          </BreakpointContent>
@@ -362,11 +362,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "466972257.459384"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "113"
-            endingLineNumber = "113"
+            startingLineNumber = "109"
+            endingLineNumber = "109"
             landmarkName = "captureExpressionGroups(_:value:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -378,11 +378,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpServer.swift"
-            timestampString = "466972257.459384"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "114"
-            endingLineNumber = "114"
+            startingLineNumber = "110"
+            endingLineNumber = "110"
             landmarkName = "captureExpressionGroups(_:value:)"
             landmarkType = "5">
          </BreakpointContent>
@@ -474,11 +474,11 @@
             ignoreCount = "0"
             continueAfterRunningActions = "No"
             filePath = "Common/HttpResponse.swift"
-            timestampString = "438296767.654712"
+            timestampString = "467836520.415098"
             startingColumnNumber = "9223372036854775807"
             endingColumnNumber = "9223372036854775807"
-            startingLineNumber = "9"
-            endingLineNumber = "9">
+            startingLineNumber = "73"
+            endingLineNumber = "73">
          </BreakpointContent>
       </BreakpointProxy>
       <BreakpointProxy

+ 0 - 125
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcschemes/Swifter.xcscheme

@@ -1,125 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<Scheme
-   LastUpgradeVersion = "0700"
-   version = "1.3">
-   <BuildAction
-      parallelizeBuildables = "YES"
-      buildImplicitDependencies = "YES">
-      <BuildActionEntries>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "YES"
-            buildForArchiving = "YES"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "7C839B6D19422CFF003A6950"
-               BuildableName = "Swifter.app"
-               BlueprintName = "Swifter"
-               ReferencedContainer = "container:Swifter.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-         <BuildActionEntry
-            buildForTesting = "YES"
-            buildForRunning = "YES"
-            buildForProfiling = "NO"
-            buildForArchiving = "NO"
-            buildForAnalyzing = "YES">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "7CE581B41A36D9E300FB840F"
-               BuildableName = "Swifter Tests.xctest"
-               BlueprintName = "Swifter Tests"
-               ReferencedContainer = "container:Swifter.xcodeproj">
-            </BuildableReference>
-         </BuildActionEntry>
-      </BuildActionEntries>
-   </BuildAction>
-   <TestAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      buildConfiguration = "Debug">
-      <Testables>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "7C839B7F19422CFF003A6950"
-               BuildableName = "SwifterTests.xctest"
-               BlueprintName = "SwifterTests"
-               ReferencedContainer = "container:Swifter.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-         <TestableReference
-            skipped = "NO">
-            <BuildableReference
-               BuildableIdentifier = "primary"
-               BlueprintIdentifier = "7CE581B41A36D9E300FB840F"
-               BuildableName = "Swifter Tests.xctest"
-               BlueprintName = "Swifter Tests"
-               ReferencedContainer = "container:Swifter.xcodeproj">
-            </BuildableReference>
-         </TestableReference>
-      </Testables>
-      <MacroExpansion>
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "7C839B6D19422CFF003A6950"
-            BuildableName = "Swifter.app"
-            BlueprintName = "Swifter"
-            ReferencedContainer = "container:Swifter.xcodeproj">
-         </BuildableReference>
-      </MacroExpansion>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </TestAction>
-   <LaunchAction
-      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
-      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
-      launchStyle = "0"
-      useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Debug"
-      ignoresPersistentStateOnLaunch = "NO"
-      debugDocumentVersioning = "YES"
-      debugServiceExtension = "internal"
-      allowLocationSimulation = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "7C839B6D19422CFF003A6950"
-            BuildableName = "Swifter.app"
-            BlueprintName = "Swifter"
-            ReferencedContainer = "container:Swifter.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-      <AdditionalOptions>
-      </AdditionalOptions>
-   </LaunchAction>
-   <ProfileAction
-      shouldUseLaunchSchemeArgsEnv = "YES"
-      savedToolIdentifier = ""
-      useCustomWorkingDirectory = "NO"
-      buildConfiguration = "Release"
-      debugDocumentVersioning = "YES">
-      <BuildableProductRunnable
-         runnableDebuggingMode = "0">
-         <BuildableReference
-            BuildableIdentifier = "primary"
-            BlueprintIdentifier = "7C839B6D19422CFF003A6950"
-            BuildableName = "Swifter.app"
-            BlueprintName = "Swifter"
-            ReferencedContainer = "container:Swifter.xcodeproj">
-         </BuildableReference>
-      </BuildableProductRunnable>
-   </ProfileAction>
-   <AnalyzeAction
-      buildConfiguration = "Debug">
-   </AnalyzeAction>
-   <ArchiveAction
-      buildConfiguration = "Release"
-      revealArchiveInOrganizer = "YES">
-   </ArchiveAction>
-</Scheme>

+ 5 - 5
Swifter.xcodeproj/xcuserdata/damiankolakowski.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -9,11 +9,6 @@
 			<key>orderHint</key>
 			<integer>2</integer>
 		</dict>
-		<key>Swifter.xcscheme</key>
-		<dict>
-			<key>orderHint</key>
-			<integer>0</integer>
-		</dict>
 		<key>SwifterOSX copy.xcscheme</key>
 		<dict>
 			<key>orderHint</key>
@@ -24,6 +19,11 @@
 			<key>orderHint</key>
 			<integer>1</integer>
 		</dict>
+		<key>SwifteriOS.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
 	</dict>
 	<key>SuppressBuildableAutocreation</key>
 	<dict>

+ 1352 - 0
SwifterOSX/SwiftyJSON.swift

@@ -0,0 +1,1352 @@
+//  SwiftyJSON.swift
+//
+//  Copyright (c) 2014 Ruoyu Fu, Pinglin Tang
+//
+//  Permission is hereby granted, free of charge, to any person obtaining a copy
+//  of this software and associated documentation files (the "Software"), to deal
+//  in the Software without restriction, including without limitation the rights
+//  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+//  copies of the Software, and to permit persons to whom the Software is
+//  furnished to do so, subject to the following conditions:
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+//
+//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+//  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+//  THE SOFTWARE.
+
+import Foundation
+
+// MARK: - Error
+
+///Error domain
+public let ErrorDomain: String! = "SwiftyJSONErrorDomain"
+
+///Error code
+public let ErrorUnsupportedType: Int! = 999
+public let ErrorIndexOutOfBounds: Int! = 900
+public let ErrorWrongType: Int! = 901
+public let ErrorNotExist: Int! = 500
+public let ErrorInvalidJSON: Int! = 490
+
+// MARK: - JSON Type
+
+/**
+JSON's type definitions.
+
+See http://tools.ietf.org/html/rfc7231#section-4.3
+*/
+public enum Type :Int{
+    
+    case Number
+    case String
+    case Bool
+    case Array
+    case Dictionary
+    case Null
+    case Unknown
+}
+
+// MARK: - JSON Base
+
+public struct JSON {
+    
+    /**
+    Creates a JSON using the data.
+    
+    - parameter data:  The NSData used to convert to json.Top level object in data is an NSArray or NSDictionary
+    - parameter opt:   The JSON serialization reading options. `.AllowFragments` by default.
+    - parameter error: error The NSErrorPointer used to return the error. `nil` by default.
+    
+    - returns: The created JSON
+    */
+    public init(data:NSData, options opt: NSJSONReadingOptions = .AllowFragments, error: NSErrorPointer = nil) {
+        do {
+            let object: AnyObject = try NSJSONSerialization.JSONObjectWithData(data, options: opt)
+            self.init(object)
+        } catch let aError as NSError {
+            if error != nil {
+                error.memory = aError
+            }
+            self.init(NSNull())
+        }
+    }
+    
+    /**
+    Creates a JSON using the object.
+    
+    - parameter object:  The object must have the following properties: All objects are NSString/String, NSNumber/Int/Float/Double/Bool, NSArray/Array, NSDictionary/Dictionary, or NSNull; All dictionary keys are NSStrings/String; NSNumbers are not NaN or infinity.
+    
+    - returns: The created JSON
+    */
+    public init(_ object: AnyObject) {
+        self.object = object
+    }
+    
+    /**
+    Creates a JSON from a [JSON]
+    
+    - parameter jsonArray: A Swift array of JSON objects
+    
+    - returns: The created JSON
+    */
+    public init(_ jsonArray:[JSON]) {
+        self.init(jsonArray.map { $0.object })
+    }
+    
+    /**
+    Creates a JSON from a [String: JSON]
+    
+    - parameter jsonDictionary: A Swift dictionary of JSON objects
+    
+    - returns: The created JSON
+    */
+    public init(_ jsonDictionary:[String: JSON]) {
+        var dictionary = [String: AnyObject]()
+        for (key, json) in jsonDictionary {
+            dictionary[key] = json.object
+        }
+        self.init(dictionary)
+    }
+    
+    /// Private object
+    private var rawArray: [AnyObject] = []
+    private var rawDictionary: [String : AnyObject] = [:]
+    private var rawString: String = ""
+    private var rawNumber: NSNumber = 0
+    private var rawNull: NSNull = NSNull()
+    /// Private type
+    private var _type: Type = .Null
+    /// prviate error
+    private var _error: NSError? = nil
+    
+    /// Object in JSON
+    public var object: AnyObject {
+        get {
+            switch self.type {
+            case .Array:
+                return self.rawArray
+            case .Dictionary:
+                return self.rawDictionary
+            case .String:
+                return self.rawString
+            case .Number:
+                return self.rawNumber
+            case .Bool:
+                return self.rawNumber
+            default:
+                return self.rawNull
+            }
+        }
+        set {
+            _error = nil
+            switch newValue {
+            case let number as NSNumber:
+                if number.isBool {
+                    _type = .Bool
+                } else {
+                    _type = .Number
+                }
+                self.rawNumber = number
+            case  let string as String:
+                _type = .String
+                self.rawString = string
+            case  _ as NSNull:
+                _type = .Null
+            case let array as [AnyObject]:
+                _type = .Array
+                self.rawArray = array
+            case let dictionary as [String : AnyObject]:
+                _type = .Dictionary
+                self.rawDictionary = dictionary
+            default:
+                _type = .Unknown
+                _error = NSError(domain: ErrorDomain, code: ErrorUnsupportedType, userInfo: [NSLocalizedDescriptionKey: "It is a unsupported type"])
+            }
+        }
+    }
+    
+    /// json type
+    public var type: Type { get { return _type } }
+    
+    /// Error in JSON
+    public var error: NSError? { get { return self._error } }
+    
+    /// The static null json
+    @available(*, unavailable, renamed="null")
+    public static var nullJSON: JSON { get { return null } }
+    public static var null: JSON { get { return JSON(NSNull()) } }
+}
+
+// MARK: - CollectionType, SequenceType, Indexable
+extension JSON : Swift.CollectionType, Swift.SequenceType, Swift.Indexable {
+    
+    public typealias Generator = JSONGenerator
+    
+    public typealias Index = JSONIndex
+    
+    public var startIndex: JSON.Index {
+        switch self.type {
+        case .Array:
+            return JSONIndex(arrayIndex: self.rawArray.startIndex)
+        case .Dictionary:
+            return JSONIndex(dictionaryIndex: self.rawDictionary.startIndex)
+        default:
+            return JSONIndex()
+        }
+    }
+    
+    public var endIndex: JSON.Index {
+        switch self.type {
+        case .Array:
+            return JSONIndex(arrayIndex: self.rawArray.endIndex)
+        case .Dictionary:
+            return JSONIndex(dictionaryIndex: self.rawDictionary.endIndex)
+        default:
+            return JSONIndex()
+        }
+    }
+    
+    public subscript (position: JSON.Index) -> JSON.Generator.Element {
+        switch self.type {
+        case .Array:
+            return (String(position.arrayIndex), JSON(self.rawArray[position.arrayIndex!]))
+        case .Dictionary:
+            let (key, value) = self.rawDictionary[position.dictionaryIndex!]
+            return (key, JSON(value))
+        default:
+            return ("", JSON.null)
+        }
+    }
+    
+    /// If `type` is `.Array` or `.Dictionary`, return `array.empty` or `dictonary.empty` otherwise return `false`.
+    public var isEmpty: Bool {
+        get {
+            switch self.type {
+            case .Array:
+                return self.rawArray.isEmpty
+            case .Dictionary:
+                return self.rawDictionary.isEmpty
+            default:
+                return true
+            }
+        }
+    }
+    
+    /// If `type` is `.Array` or `.Dictionary`, return `array.count` or `dictonary.count` otherwise return `0`.
+    public var count: Int {
+        switch self.type {
+        case .Array:
+            return self.rawArray.count
+        case .Dictionary:
+            return self.rawDictionary.count
+        default:
+            return 0
+        }
+    }
+    
+    public func underestimateCount() -> Int {
+        switch self.type {
+        case .Array:
+            return self.rawArray.underestimateCount()
+        case .Dictionary:
+            return self.rawDictionary.underestimateCount()
+        default:
+            return 0
+        }
+    }
+    
+    /**
+    If `type` is `.Array` or `.Dictionary`, return a generator over the elements like `Array` or `Dictionary`, otherwise return a generator over empty.
+    
+    - returns: Return a *generator* over the elements of JSON.
+    */
+    public func generate() -> JSON.Generator {
+        return JSON.Generator(self)
+    }
+}
+
+public struct JSONIndex: ForwardIndexType, _Incrementable, Equatable, Comparable {
+    
+    let arrayIndex: Int?
+    let dictionaryIndex: DictionaryIndex<String, AnyObject>?
+    
+    let type: Type
+    
+    init(){
+        self.arrayIndex = nil
+        self.dictionaryIndex = nil
+        self.type = .Unknown
+    }
+    
+    init(arrayIndex: Int) {
+        self.arrayIndex = arrayIndex
+        self.dictionaryIndex = nil
+        self.type = .Array
+    }
+    
+    init(dictionaryIndex: DictionaryIndex<String, AnyObject>) {
+        self.arrayIndex = nil
+        self.dictionaryIndex = dictionaryIndex
+        self.type = .Dictionary
+    }
+    
+    public func successor() -> JSONIndex {
+        switch self.type {
+        case .Array:
+            return JSONIndex(arrayIndex: self.arrayIndex!.successor())
+        case .Dictionary:
+            return JSONIndex(dictionaryIndex: self.dictionaryIndex!.successor())
+        default:
+            return JSONIndex()
+        }
+    }
+}
+
+public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs.type, rhs.type) {
+    case (.Array, .Array):
+        return lhs.arrayIndex == rhs.arrayIndex
+    case (.Dictionary, .Dictionary):
+        return lhs.dictionaryIndex == rhs.dictionaryIndex
+    default:
+        return false
+    }
+}
+
+public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs.type, rhs.type) {
+    case (.Array, .Array):
+        return lhs.arrayIndex < rhs.arrayIndex
+    case (.Dictionary, .Dictionary):
+        return lhs.dictionaryIndex < rhs.dictionaryIndex
+    default:
+        return false
+    }
+}
+
+public func <=(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs.type, rhs.type) {
+    case (.Array, .Array):
+        return lhs.arrayIndex <= rhs.arrayIndex
+    case (.Dictionary, .Dictionary):
+        return lhs.dictionaryIndex <= rhs.dictionaryIndex
+    default:
+        return false
+    }
+}
+
+public func >=(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs.type, rhs.type) {
+    case (.Array, .Array):
+        return lhs.arrayIndex >= rhs.arrayIndex
+    case (.Dictionary, .Dictionary):
+        return lhs.dictionaryIndex >= rhs.dictionaryIndex
+    default:
+        return false
+    }
+}
+
+public func >(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs.type, rhs.type) {
+    case (.Array, .Array):
+        return lhs.arrayIndex > rhs.arrayIndex
+    case (.Dictionary, .Dictionary):
+        return lhs.dictionaryIndex > rhs.dictionaryIndex
+    default:
+        return false
+    }
+}
+
+public struct JSONGenerator : GeneratorType {
+    
+    public typealias Element = (String, JSON)
+    
+    private let type: Type
+    private var dictionayGenerate: DictionaryGenerator<String, AnyObject>?
+    private var arrayGenerate: IndexingGenerator<[AnyObject]>?
+    private var arrayIndex: Int = 0
+    
+    init(_ json: JSON) {
+        self.type = json.type
+        if type == .Array {
+            self.arrayGenerate = json.rawArray.generate()
+        }else {
+            self.dictionayGenerate = json.rawDictionary.generate()
+        }
+    }
+    
+    public mutating func next() -> JSONGenerator.Element? {
+        switch self.type {
+        case .Array:
+            if let o = self.arrayGenerate!.next() {
+                return (String(self.arrayIndex++), JSON(o))
+            } else {
+                return nil
+            }
+        case .Dictionary:
+            if let (k, v): (String, AnyObject) = self.dictionayGenerate!.next() {
+                return (k, JSON(v))
+            } else {
+                return nil
+            }
+        default:
+            return nil
+        }
+    }
+}
+
+// MARK: - Subscript
+
+/**
+*  To mark both String and Int can be used in subscript.
+*/
+public protocol JSONSubscriptType {}
+
+extension Int: JSONSubscriptType {}
+
+extension String: JSONSubscriptType {}
+
+extension JSON {
+    
+    /// If `type` is `.Array`, return json which's object is `array[index]`, otherwise return null json with error.
+    private subscript(index index: Int) -> JSON {
+        get {
+            if self.type != .Array {
+                var r = JSON.null
+                r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] failure, It is not an array"])
+                return r
+            } else if index >= 0 && index < self.rawArray.count {
+                return JSON(self.rawArray[index])
+            } else {
+                var r = JSON.null
+                r._error = NSError(domain: ErrorDomain, code:ErrorIndexOutOfBounds , userInfo: [NSLocalizedDescriptionKey: "Array[\(index)] is out of bounds"])
+                return r
+            }
+        }
+        set {
+            if self.type == .Array {
+                if self.rawArray.count > index && newValue.error == nil {
+                    self.rawArray[index] = newValue.object
+                }
+            }
+        }
+    }
+    
+    /// If `type` is `.Dictionary`, return json which's object is `dictionary[key]` , otherwise return null json with error.
+    private subscript(key key: String) -> JSON {
+        get {
+            var r = JSON.null
+            if self.type == .Dictionary {
+                if let o = self.rawDictionary[key] {
+                    r = JSON(o)
+                } else {
+                    r._error = NSError(domain: ErrorDomain, code: ErrorNotExist, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] does not exist"])
+                }
+            } else {
+                r._error = self._error ?? NSError(domain: ErrorDomain, code: ErrorWrongType, userInfo: [NSLocalizedDescriptionKey: "Dictionary[\"\(key)\"] failure, It is not an dictionary"])
+            }
+            return r
+        }
+        set {
+            if self.type == .Dictionary && newValue.error == nil {
+                self.rawDictionary[key] = newValue.object
+            }
+        }
+    }
+    
+    /// If `sub` is `Int`, return `subscript(index:)`; If `sub` is `String`,  return `subscript(key:)`.
+    private subscript(sub sub: JSONSubscriptType) -> JSON {
+        get {
+            if sub is String {
+                return self[key:sub as! String]
+            } else {
+                return self[index:sub as! Int]
+            }
+        }
+        set {
+            if sub is String {
+                self[key:sub as! String] = newValue
+            } else {
+                self[index:sub as! Int] = newValue
+            }
+        }
+    }
+    
+    /**
+    Find a json in the complex data structuresby using the Int/String's array.
+    
+    - parameter path: The target json's path. Example:
+    
+    let json = JSON[data]
+    let path = [9,"list","person","name"]
+    let name = json[path]
+    
+    The same as: let name = json[9]["list"]["person"]["name"]
+    
+    - returns: Return a json found by the path or a null json with error
+    */
+    public subscript(path: [JSONSubscriptType]) -> JSON {
+        get {
+            return path.reduce(self) { $0[sub: $1] }
+        }
+        set {
+            switch path.count {
+            case 0:
+                return
+            case 1:
+                self[sub:path[0]].object = newValue.object
+            default:
+                var aPath = path; aPath.removeAtIndex(0)
+                var nextJSON = self[sub: path[0]]
+                nextJSON[aPath] = newValue
+                self[sub: path[0]] = nextJSON
+            }
+        }
+    }
+    
+    /**
+    Find a json in the complex data structuresby using the Int/String's array.
+    
+    - parameter path: The target json's path. Example:
+    
+    let name = json[9,"list","person","name"]
+    
+    The same as: let name = json[9]["list"]["person"]["name"]
+    
+    - returns: Return a json found by the path or a null json with error
+    */
+    public subscript(path: JSONSubscriptType...) -> JSON {
+        get {
+            return self[path]
+        }
+        set {
+            self[path] = newValue
+        }
+    }
+}
+
+// MARK: - LiteralConvertible
+
+extension JSON: Swift.StringLiteralConvertible {
+    
+    public init(stringLiteral value: StringLiteralType) {
+        self.init(value)
+    }
+    
+    public init(extendedGraphemeClusterLiteral value: StringLiteralType) {
+        self.init(value)
+    }
+    
+    public init(unicodeScalarLiteral value: StringLiteralType) {
+        self.init(value)
+    }
+}
+
+extension JSON: Swift.IntegerLiteralConvertible {
+    
+    public init(integerLiteral value: IntegerLiteralType) {
+        self.init(value)
+    }
+}
+
+extension JSON: Swift.BooleanLiteralConvertible {
+    
+    public init(booleanLiteral value: BooleanLiteralType) {
+        self.init(value)
+    }
+}
+
+extension JSON: Swift.FloatLiteralConvertible {
+    
+    public init(floatLiteral value: FloatLiteralType) {
+        self.init(value)
+    }
+}
+
+extension JSON: Swift.DictionaryLiteralConvertible {
+    
+    public init(dictionaryLiteral elements: (String, AnyObject)...) {
+        self.init(elements.reduce([String : AnyObject]()){(dictionary: [String : AnyObject], element:(String, AnyObject)) -> [String : AnyObject] in
+            var d = dictionary
+            d[element.0] = element.1
+            return d
+            })
+    }
+}
+
+extension JSON: Swift.ArrayLiteralConvertible {
+    
+    public init(arrayLiteral elements: AnyObject...) {
+        self.init(elements)
+    }
+}
+
+extension JSON: Swift.NilLiteralConvertible {
+    
+    public init(nilLiteral: ()) {
+        self.init(NSNull())
+    }
+}
+
+// MARK: - Raw
+
+extension JSON: Swift.RawRepresentable {
+    
+    public init?(rawValue: AnyObject) {
+        if JSON(rawValue).type == .Unknown {
+            return nil
+        } else {
+            self.init(rawValue)
+        }
+    }
+    
+    public var rawValue: AnyObject {
+        return self.object
+    }
+    
+    public func rawData(options opt: NSJSONWritingOptions = NSJSONWritingOptions(rawValue: 0)) throws -> NSData {
+        guard NSJSONSerialization.isValidJSONObject(self.object) else {
+            throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"])
+        }
+        
+        return try NSJSONSerialization.dataWithJSONObject(self.object, options: opt)
+    }
+    
+    public func rawString(encoding: UInt = NSUTF8StringEncoding, options opt: NSJSONWritingOptions = .PrettyPrinted) -> String? {
+        switch self.type {
+        case .Array, .Dictionary:
+            do {
+                let data = try self.rawData(options: opt)
+                return NSString(data: data, encoding: encoding) as? String
+            } catch _ {
+                return nil
+            }
+        case .String:
+            return self.rawString
+        case .Number:
+            return self.rawNumber.stringValue
+        case .Bool:
+            return self.rawNumber.boolValue.description
+        case .Null:
+            return "null"
+        default:
+            return nil
+        }
+    }
+}
+
+// MARK: - Printable, DebugPrintable
+
+extension JSON: Swift.Printable, Swift.DebugPrintable {
+    
+    public var description: String {
+        if let string = self.rawString(options:.PrettyPrinted) {
+            return string
+        } else {
+            return "unknown"
+        }
+    }
+    
+    public var debugDescription: String {
+        return description
+    }
+}
+
+// MARK: - Array
+
+extension JSON {
+    
+    //Optional [JSON]
+    public var array: [JSON]? {
+        get {
+            if self.type == .Array {
+                return self.rawArray.map{ JSON($0) }
+            } else {
+                return nil
+            }
+        }
+    }
+    
+    //Non-optional [JSON]
+    public var arrayValue: [JSON] {
+        get {
+            return self.array ?? []
+        }
+    }
+    
+    //Optional [AnyObject]
+    public var arrayObject: [AnyObject]? {
+        get {
+            switch self.type {
+            case .Array:
+                return self.rawArray
+            default:
+                return nil
+            }
+        }
+        set {
+            if let array = newValue {
+                self.object = array
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+}
+
+// MARK: - Dictionary
+
+extension JSON {
+    
+    //Optional [String : JSON]
+    public var dictionary: [String : JSON]? {
+        if self.type == .Dictionary {
+            return self.rawDictionary.reduce([String : JSON]()) { (dictionary: [String : JSON], element: (String, AnyObject)) -> [String : JSON] in
+                var d = dictionary
+                d[element.0] = JSON(element.1)
+                return d
+            }
+        } else {
+            return nil
+        }
+    }
+    
+    //Non-optional [String : JSON]
+    public var dictionaryValue: [String : JSON] {
+        return self.dictionary ?? [:]
+    }
+    
+    //Optional [String : AnyObject]
+    public var dictionaryObject: [String : AnyObject]? {
+        get {
+            switch self.type {
+            case .Dictionary:
+                return self.rawDictionary
+            default:
+                return nil
+            }
+        }
+        set {
+            if let v = newValue {
+                self.object = v
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+}
+
+// MARK: - Bool
+
+extension JSON: Swift.BooleanType {
+    
+    //Optional bool
+    public var bool: Bool? {
+        get {
+            switch self.type {
+            case .Bool:
+                return self.rawNumber.boolValue
+            default:
+                return nil
+            }
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(bool: newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    //Non-optional bool
+    public var boolValue: Bool {
+        get {
+            switch self.type {
+            case .Bool, .Number, .String:
+                return self.object.boolValue
+            default:
+                return false
+            }
+        }
+        set {
+            self.object = NSNumber(bool: newValue)
+        }
+    }
+}
+
+// MARK: - String
+
+extension JSON {
+    
+    //Optional string
+    public var string: String? {
+        get {
+            switch self.type {
+            case .String:
+                return self.object as? String
+            default:
+                return nil
+            }
+        }
+        set {
+            if newValue != nil {
+                self.object = NSString(string:newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    //Non-optional string
+    public var stringValue: String {
+        get {
+            switch self.type {
+            case .String:
+                return self.object as! String
+            case .Number:
+                return self.object.stringValue
+            case .Bool:
+                return (self.object as! Bool).description
+            default:
+                return ""
+            }
+        }
+        set {
+            self.object = NSString(string:newValue)
+        }
+    }
+}
+
+// MARK: - Number
+extension JSON {
+    
+    //Optional number
+    public var number: NSNumber? {
+        get {
+            switch self.type {
+            case .Number, .Bool:
+                return self.rawNumber
+            default:
+                return nil
+            }
+        }
+        set {
+            self.object = newValue ?? NSNull()
+        }
+    }
+    
+    //Non-optional number
+    public var numberValue: NSNumber {
+        get {
+            switch self.type {
+            case .String:
+                let decimal = NSDecimalNumber(string: self.object as? String)
+                if decimal == NSDecimalNumber.notANumber() {  // indicates parse error
+                    return NSDecimalNumber.zero()
+                }
+                return decimal
+            case .Number, .Bool:
+                return self.object as! NSNumber
+            default:
+                return NSNumber(double: 0.0)
+            }
+        }
+        set {
+            self.object = newValue
+        }
+    }
+}
+
+//MARK: - Null
+extension JSON {
+    
+    public var null: NSNull? {
+        get {
+            switch self.type {
+            case .Null:
+                return self.rawNull
+            default:
+                return nil
+            }
+        }
+        set {
+            self.object = NSNull()
+        }
+    }
+    public func isExists() -> Bool{
+        if let errorValue = error where errorValue.code == ErrorNotExist{
+            return false
+        }
+        return true
+    }
+}
+
+//MARK: - URL
+extension JSON {
+    
+    //Optional URL
+    public var URL: NSURL? {
+        get {
+            switch self.type {
+            case .String:
+                if let encodedString_ = self.rawString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet()) {
+                    return NSURL(string: encodedString_)
+                } else {
+                    return nil
+                }
+            default:
+                return nil
+            }
+        }
+        set {
+            self.object = newValue?.absoluteString ?? NSNull()
+        }
+    }
+}
+
+// MARK: - Int, Double, Float, Int8, Int16, Int32, Int64
+
+extension JSON {
+    
+    public var double: Double? {
+        get {
+            return self.number?.doubleValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(double: newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    public var doubleValue: Double {
+        get {
+            return self.numberValue.doubleValue
+        }
+        set {
+            self.object = NSNumber(double: newValue)
+        }
+    }
+    
+    public var float: Float? {
+        get {
+            return self.number?.floatValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(float: newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    public var floatValue: Float {
+        get {
+            return self.numberValue.floatValue
+        }
+        set {
+            self.object = NSNumber(float: newValue)
+        }
+    }
+    
+    public var int: Int? {
+        get {
+            return self.number?.longValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(integer: newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    public var intValue: Int {
+        get {
+            return self.numberValue.integerValue
+        }
+        set {
+            self.object = NSNumber(integer: newValue)
+        }
+    }
+    
+    public var uInt: UInt? {
+        get {
+            return self.number?.unsignedLongValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(unsignedLong: newValue!)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+    
+    public var uIntValue: UInt {
+        get {
+            return self.numberValue.unsignedLongValue
+        }
+        set {
+            self.object = NSNumber(unsignedLong: newValue)
+        }
+    }
+    
+    public var int8: Int8? {
+        get {
+            return self.number?.charValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(char: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var int8Value: Int8 {
+        get {
+            return self.numberValue.charValue
+        }
+        set {
+            self.object = NSNumber(char: newValue)
+        }
+    }
+    
+    public var uInt8: UInt8? {
+        get {
+            return self.number?.unsignedCharValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(unsignedChar: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var uInt8Value: UInt8 {
+        get {
+            return self.numberValue.unsignedCharValue
+        }
+        set {
+            self.object = NSNumber(unsignedChar: newValue)
+        }
+    }
+    
+    public var int16: Int16? {
+        get {
+            return self.number?.shortValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(short: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var int16Value: Int16 {
+        get {
+            return self.numberValue.shortValue
+        }
+        set {
+            self.object = NSNumber(short: newValue)
+        }
+    }
+    
+    public var uInt16: UInt16? {
+        get {
+            return self.number?.unsignedShortValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(unsignedShort: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var uInt16Value: UInt16 {
+        get {
+            return self.numberValue.unsignedShortValue
+        }
+        set {
+            self.object = NSNumber(unsignedShort: newValue)
+        }
+    }
+    
+    public var int32: Int32? {
+        get {
+            return self.number?.intValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(int: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var int32Value: Int32 {
+        get {
+            return self.numberValue.intValue
+        }
+        set {
+            self.object = NSNumber(int: newValue)
+        }
+    }
+    
+    public var uInt32: UInt32? {
+        get {
+            return self.number?.unsignedIntValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(unsignedInt: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var uInt32Value: UInt32 {
+        get {
+            return self.numberValue.unsignedIntValue
+        }
+        set {
+            self.object = NSNumber(unsignedInt: newValue)
+        }
+    }
+    
+    public var int64: Int64? {
+        get {
+            return self.number?.longLongValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(longLong: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var int64Value: Int64 {
+        get {
+            return self.numberValue.longLongValue
+        }
+        set {
+            self.object = NSNumber(longLong: newValue)
+        }
+    }
+    
+    public var uInt64: UInt64? {
+        get {
+            return self.number?.unsignedLongLongValue
+        }
+        set {
+            if newValue != nil {
+                self.object = NSNumber(unsignedLongLong: newValue!)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+    
+    public var uInt64Value: UInt64 {
+        get {
+            return self.numberValue.unsignedLongLongValue
+        }
+        set {
+            self.object = NSNumber(unsignedLongLong: newValue)
+        }
+    }
+}
+
+//MARK: - Comparable
+extension JSON : Swift.Comparable {}
+
+public func ==(lhs: JSON, rhs: JSON) -> Bool {
+    
+    switch (lhs.type, rhs.type) {
+    case (.Number, .Number):
+        return lhs.rawNumber == rhs.rawNumber
+    case (.String, .String):
+        return lhs.rawString == rhs.rawString
+    case (.Bool, .Bool):
+        return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue
+    case (.Array, .Array):
+        return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+    case (.Dictionary, .Dictionary):
+        return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+    case (.Null, .Null):
+        return true
+    default:
+        return false
+    }
+}
+
+public func <=(lhs: JSON, rhs: JSON) -> Bool {
+    
+    switch (lhs.type, rhs.type) {
+    case (.Number, .Number):
+        return lhs.rawNumber <= rhs.rawNumber
+    case (.String, .String):
+        return lhs.rawString <= rhs.rawString
+    case (.Bool, .Bool):
+        return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue
+    case (.Array, .Array):
+        return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+    case (.Dictionary, .Dictionary):
+        return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+    case (.Null, .Null):
+        return true
+    default:
+        return false
+    }
+}
+
+public func >=(lhs: JSON, rhs: JSON) -> Bool {
+    
+    switch (lhs.type, rhs.type) {
+    case (.Number, .Number):
+        return lhs.rawNumber >= rhs.rawNumber
+    case (.String, .String):
+        return lhs.rawString >= rhs.rawString
+    case (.Bool, .Bool):
+        return lhs.rawNumber.boolValue == rhs.rawNumber.boolValue
+    case (.Array, .Array):
+        return lhs.rawArray as NSArray == rhs.rawArray as NSArray
+    case (.Dictionary, .Dictionary):
+        return lhs.rawDictionary as NSDictionary == rhs.rawDictionary as NSDictionary
+    case (.Null, .Null):
+        return true
+    default:
+        return false
+    }
+}
+
+public func >(lhs: JSON, rhs: JSON) -> Bool {
+    
+    switch (lhs.type, rhs.type) {
+    case (.Number, .Number):
+        return lhs.rawNumber > rhs.rawNumber
+    case (.String, .String):
+        return lhs.rawString > rhs.rawString
+    default:
+        return false
+    }
+}
+
+public func <(lhs: JSON, rhs: JSON) -> Bool {
+    
+    switch (lhs.type, rhs.type) {
+    case (.Number, .Number):
+        return lhs.rawNumber < rhs.rawNumber
+    case (.String, .String):
+        return lhs.rawString < rhs.rawString
+    default:
+        return false
+    }
+}
+
+private let trueNumber = NSNumber(bool: true)
+private let falseNumber = NSNumber(bool: false)
+private let trueObjCType = String.fromCString(trueNumber.objCType)
+private let falseObjCType = String.fromCString(falseNumber.objCType)
+
+// MARK: - NSNumber: Comparable
+
+extension NSNumber {
+    var isBool:Bool {
+        get {
+            let objCType = String.fromCString(self.objCType)
+            if (self.compare(trueNumber) == NSComparisonResult.OrderedSame && objCType == trueObjCType)
+                || (self.compare(falseNumber) == NSComparisonResult.OrderedSame && objCType == falseObjCType){
+                    return true
+            } else {
+                return false
+            }
+        }
+    }
+}
+
+public func ==(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    switch (lhs.isBool, rhs.isBool) {
+    case (false, true):
+        return false
+    case (true, false):
+        return false
+    default:
+        return lhs.compare(rhs) == NSComparisonResult.OrderedSame
+    }
+}
+
+public func !=(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    return !(lhs == rhs)
+}
+
+public func <(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    
+    switch (lhs.isBool, rhs.isBool) {
+    case (false, true):
+        return false
+    case (true, false):
+        return false
+    default:
+        return lhs.compare(rhs) == NSComparisonResult.OrderedAscending
+    }
+}
+
+public func >(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    
+    switch (lhs.isBool, rhs.isBool) {
+    case (false, true):
+        return false
+    case (true, false):
+        return false
+    default:
+        return lhs.compare(rhs) == NSComparisonResult.OrderedDescending
+    }
+}
+
+public func <=(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    
+    switch (lhs.isBool, rhs.isBool) {
+    case (false, true):
+        return false
+    case (true, false):
+        return false
+    default:
+        return lhs.compare(rhs) != NSComparisonResult.OrderedDescending
+    }
+}
+
+public func >=(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    
+    switch (lhs.isBool, rhs.isBool) {
+    case (false, true):
+        return false
+    case (true, false):
+        return false
+    default:
+        return lhs.compare(rhs) != NSComparisonResult.OrderedAscending
+    }
+}

+ 18 - 0
SwifterOSX/main.swift

@@ -6,9 +6,27 @@
 
 import Foundation
 
+class SwiftyJSONSerializer: Serializer {
+    func serialize(object: Any) throws -> String {
+        guard let obj = object as? JSON,
+            let rawString = obj.rawString() else {
+                throw SerializationError.InvalidObject
+        }
+        return rawString
+    }
+}
+
 let server = demoServer(NSBundle.mainBundle().resourcePath!)
 
 do {
+    server["/SwiftyJSON"] = { request in
+        let serialize = SwiftyJSONSerializer()
+        let js: JSON = ["return": "OK", "isItAJSON": true, "code" : 200]
+        return .OK(.Custom(serialize, js))
+    }
+    server["/testAfterBaseRoute"] = { request in
+        return .OK(.Html("ok !"))
+    }
     try server.start(9080)
     print("Server has started ( port = 9080 ). Try to connect now...")
     NSRunLoop.mainRunLoop().run()

+ 1 - 2
Swifter/AppDelegate.swift → SwifteriOS/AppDelegate.swift

@@ -16,5 +16,4 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
     func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
         return true
     }
-}
-
+}

+ 0 - 0
Swifter/Base.lproj/Main.storyboard → SwifteriOS/Base.lproj/Main.storyboard


+ 0 - 0
Swifter/Images.xcassets/AppIcon.appiconset/Contents.json → SwifteriOS/Images.xcassets/AppIcon.appiconset/Contents.json


+ 0 - 0
Swifter/Images.xcassets/LaunchImage.launchimage/Contents.json → SwifteriOS/Images.xcassets/LaunchImage.launchimage/Contents.json


+ 0 - 0
Swifter/Info.plist → SwifteriOS/Info.plist


+ 0 - 0
Swifter/ViewController.swift → SwifteriOS/ViewController.swift