Просмотр исходного кода

Swift glue in progress. Still fixing umbrella framework config.

Adam Pritchard 9 лет назад
Родитель
Сommit
7908122477

+ 65 - 3
MobileLibrary/iOS/PsiphonTunnelController/.gitignore

@@ -1,3 +1,65 @@
-project.xcworkspace
-xcuserdata
-build
+# Xcode
+#
+# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
+
+## Build generated
+build/
+DerivedData/
+
+## Various settings
+*.pbxuser
+!default.pbxuser
+*.mode1v3
+!default.mode1v3
+*.mode2v3
+!default.mode2v3
+*.perspectivev3
+!default.perspectivev3
+xcuserdata/
+
+## Other
+*.moved-aside
+*.xcuserstate
+
+## Obj-C/Swift specific
+*.hmap
+*.ipa
+*.dSYM.zip
+*.dSYM
+
+## Playgrounds
+timeline.xctimeline
+playground.xcworkspace
+
+# Swift Package Manager
+#
+# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
+# Packages/
+.build/
+
+# CocoaPods
+#
+# We recommend against adding the Pods directory to your .gitignore. However
+# you should judge for yourself, the pros and cons are mentioned at:
+# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
+#
+# Pods/
+
+# Carthage
+#
+# Add this line if you want to avoid checking in source code from Carthage dependencies.
+# Carthage/Checkouts
+
+Carthage/Build
+
+# fastlane
+#
+# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
+# screenshots whenever they are needed.
+# For more information about the recommended setup visit:
+# https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md
+
+fastlane/report.xml
+fastlane/Preview.html
+fastlane/screenshots
+fastlane/test_output

+ 41 - 10
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.pbxproj

@@ -8,23 +8,36 @@
 
 /* Begin PBXBuildFile section */
 		4445243B1D8B356E00AF7B5B /* Psi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4445243A1D8B356E00AF7B5B /* Psi.framework */; };
-		4445243C1D8B357500AF7B5B /* Psi.framework in Resources */ = {isa = PBXBuildFile; fileRef = 4445243A1D8B356E00AF7B5B /* Psi.framework */; };
-		44980AC21D63A3B300B78274 /* PsiphonTunnelController.h in Headers */ = {isa = PBXBuildFile; fileRef = 44980AC01D63A3B300B78274 /* PsiphonTunnelController.h */; settings = {ATTRIBUTES = (Public, ); }; };
-		44980ACB1D63A3EA00B78274 /* PsiphonTunnelController.m in Sources */ = {isa = PBXBuildFile; fileRef = 44980AC81D63A3EA00B78274 /* PsiphonTunnelController.m */; };
 		44980ACC1D63A3EA00B78274 /* Reachability.h in Headers */ = {isa = PBXBuildFile; fileRef = 44980AC91D63A3EA00B78274 /* Reachability.h */; settings = {ATTRIBUTES = (Public, ); }; };
 		44980ACD1D63A3EA00B78274 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 44980ACA1D63A3EA00B78274 /* Reachability.m */; };
 		44CC93D61D6CBD740082F743 /* rootCAs.txt in Resources */ = {isa = PBXBuildFile; fileRef = 44CC93D51D6CBD740082F743 /* rootCAs.txt */; };
+		66ED503D1D8C73EE00417FE8 /* PsiphonTunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66ED503B1D8C73EE00417FE8 /* PsiphonTunnel.swift */; };
+		66ED503E1D8C73EE00417FE8 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66ED503C1D8C73EE00417FE8 /* SwiftyJSON.swift */; };
+		66ED50531D9043B800417FE8 /* Psi.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 4445243A1D8B356E00AF7B5B /* Psi.framework */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
 /* End PBXBuildFile section */
 
+/* Begin PBXCopyFilesBuildPhase section */
+		66ED50521D9043A800417FE8 /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = "";
+			dstSubfolderSpec = 10;
+			files = (
+				66ED50531D9043B800417FE8 /* Psi.framework in CopyFiles */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
 /* Begin PBXFileReference section */
 		4445243A1D8B356E00AF7B5B /* Psi.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Psi.framework; path = PsiphonTunnelController/Psi.framework; sourceTree = "<group>"; };
 		44980ABD1D63A3B300B78274 /* PsiphonTunnelController.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PsiphonTunnelController.framework; sourceTree = BUILT_PRODUCTS_DIR; };
-		44980AC01D63A3B300B78274 /* PsiphonTunnelController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PsiphonTunnelController.h; sourceTree = "<group>"; };
 		44980AC11D63A3B300B78274 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
-		44980AC81D63A3EA00B78274 /* PsiphonTunnelController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = PsiphonTunnelController.m; sourceTree = "<group>"; };
 		44980AC91D63A3EA00B78274 /* Reachability.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Reachability.h; sourceTree = "<group>"; };
 		44980ACA1D63A3EA00B78274 /* Reachability.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Reachability.m; sourceTree = "<group>"; };
 		44CC93D51D6CBD740082F743 /* rootCAs.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = rootCAs.txt; path = PsiphonTunnelController/rootCAs.txt; sourceTree = "<group>"; };
+		66ED503B1D8C73EE00417FE8 /* PsiphonTunnel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PsiphonTunnel.swift; sourceTree = "<group>"; };
+		66ED503C1D8C73EE00417FE8 /* SwiftyJSON.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftyJSON.swift; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -60,10 +73,10 @@
 		44980ABF1D63A3B300B78274 /* PsiphonTunnelController */ = {
 			isa = PBXGroup;
 			children = (
-				44980AC81D63A3EA00B78274 /* PsiphonTunnelController.m */,
+				66ED503B1D8C73EE00417FE8 /* PsiphonTunnel.swift */,
+				66ED503C1D8C73EE00417FE8 /* SwiftyJSON.swift */,
 				44980AC91D63A3EA00B78274 /* Reachability.h */,
 				44980ACA1D63A3EA00B78274 /* Reachability.m */,
-				44980AC01D63A3B300B78274 /* PsiphonTunnelController.h */,
 				44980AC11D63A3B300B78274 /* Info.plist */,
 			);
 			path = PsiphonTunnelController;
@@ -84,7 +97,6 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				44980AC21D63A3B300B78274 /* PsiphonTunnelController.h in Headers */,
 				44980ACC1D63A3EA00B78274 /* Reachability.h in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -100,6 +112,7 @@
 				44980AB91D63A3B300B78274 /* Frameworks */,
 				44980ABA1D63A3B300B78274 /* Headers */,
 				44980ABB1D63A3B300B78274 /* Resources */,
+				66ED50521D9043A800417FE8 /* CopyFiles */,
 			);
 			buildRules = (
 			);
@@ -121,6 +134,7 @@
 				TargetAttributes = {
 					44980ABC1D63A3B300B78274 = {
 						CreatedOnToolsVersion = 8.0;
+						LastSwiftMigration = 0800;
 						ProvisioningStyle = Manual;
 					};
 				};
@@ -147,7 +161,6 @@
 			isa = PBXResourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				4445243C1D8B357500AF7B5B /* Psi.framework in Resources */,
 				44CC93D61D6CBD740082F743 /* rootCAs.txt in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
@@ -159,8 +172,9 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				66ED503E1D8C73EE00417FE8 /* SwiftyJSON.swift in Sources */,
 				44980ACD1D63A3EA00B78274 /* Reachability.m in Sources */,
-				44980ACB1D63A3EA00B78274 /* PsiphonTunnelController.m in Sources */,
+				66ED503D1D8C73EE00417FE8 /* PsiphonTunnel.swift in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -290,9 +304,18 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				ONLY_ACTIVE_ARCH = NO;
+				OTHER_LDFLAGS = (
+					"-read_only_relocs",
+					suppress,
+					"-sub_umbrella",
+					Psi,
+				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.psiphon.PsiphonTunnelController;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
+				SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME).h";
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALID_ARCHS = "arm64 armv7 armv7s";
 			};
@@ -314,9 +337,17 @@
 				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
 				ONLY_ACTIVE_ARCH = NO;
+				OTHER_LDFLAGS = (
+					"-read_only_relocs",
+					suppress,
+					"-sub_umbrella",
+					Psi,
+				);
 				PRODUCT_BUNDLE_IDENTIFIER = com.psiphon.PsiphonTunnelController;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SKIP_INSTALL = YES;
+				SWIFT_OBJC_INTERFACE_HEADER_NAME = "$(SWIFT_MODULE_NAME).h";
+				SWIFT_VERSION = 3.0;
 				TARGETED_DEVICE_FAMILY = "1,2";
 				VALID_ARCHS = "arm64 armv7 armv7s";
 			};

Разница между файлами не показана из-за своего большого размера
+ 589 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/PsiphonTunnel.swift


+ 0 - 56
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/PsiphonTunnelController.h

@@ -1,56 +0,0 @@
-//
-//  PsiphonMobile.h
-//  PsiphonMobile
-//
-//  Created by eugene-imac on 2016-08-16.
-//  Copyright © 2016 Psiphon Inc. All rights reserved.
-//
-
-
-//! Project version number for PsiphonMobile.
-FOUNDATION_EXPORT double PsiphonMobileVersionNumber;
-
-//! Project version string for PsiphonMobile.
-FOUNDATION_EXPORT const unsigned char PsiphonMobileVersionString[];
-
-// In this header, you should import all the public headers of your framework using statements like #import <PsiphonMobile/PublicHeader.h>
-
-
-@protocol TunneledAppProtocol
-- (NSString *) getPsiphonConfig;
-- (void) onDiagnosticMessage: (NSString *) message;
-- (void) onAvailableEgressRegions: (NSArray *) regions;
-- (void) onSocksProxyPortInUse: (NSInteger) port;
-- (void) onHttpProxyPortInUse: (NSInteger) port;
-- (void) onListeningSocksProxyPort: (NSInteger) port;
-- (void) onListeningHttpProxyPort: (NSInteger) port;
-- (void) onUpstreamProxyError: (NSString *) message;
-- (void) onConnecting;
-- (void) onConnected;
-- (void) onHomepage: (NSString *) url;
-- (void) onClientRegion: (NSString *) region;
-- (void) onClientUpgradeDownloaded: (NSString *) filename;
-- (void) onSplitTunnelRegion: (NSString *) region;
-- (void) onUntunneledAddress: (NSString *) address;
-- (void) onBytesTransferred: (long) sent : (long) received;
-- (void) onStartedWaitingForNetworkConnectivity;
-@end
-
-
-@interface PsiphonTunnelController : NSObject
-
-@property (weak) id <TunneledAppProtocol> tunneledAppProtocolDelegate;
-@property (nonatomic) NSInteger listeningSocksProxyPort;
-@property (nonatomic) NSArray *homepages;
-
-
-+ (id) sharedInstance;
-
--(void) startTunnel;
--(void) stopTunnel;
-
-@end
-
-@interface Psi : NSObject
-+ (void)sendFeedback:(NSString*)configJson diagnostics: (NSString*)diagnosticsJson b64EncodedPublicKey: (NSString*) b64EncodedPublicKey uploadServer: (NSString*)uploadServer uploadPath: (NSString*) uploadPath uploadServerHeaders: (NSString*)uploadServerHeaders;
-@end

+ 0 - 155
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/PsiphonTunnelController.m

@@ -1,155 +0,0 @@
-//
-//  PsiphonMobile.m
-//  PsiphonMobile
-//
-//  Created by eugene-imac on 2016-08-16.
-//  Copyright © 2016 Psiphon Inc. All rights reserved.
-//
-
-#import <Psi/Psi.h>
-#import "PsiphonTunnelController.h"
-#import "Reachability.h"
-
-@interface PsiphonTunnelController () <GoPsiPsiphonProvider>
-@end
-
-@implementation PsiphonTunnelController
-
-
-+(PsiphonTunnelController *) sharedInstance {
-    static PsiphonTunnelController *sharedInstance = nil;
-    static dispatch_once_t onceToken = 0;
-    dispatch_once(&onceToken, ^{
-        sharedInstance = [[self alloc] init];
-        // Do any other initialisation stuff here
-    });
-    return sharedInstance;
-}
-
--(void) startTunnel {
-    [self stopTunnel];
-    [[self tunneledAppProtocolDelegate] onDiagnosticMessage:@"starting Psiphon library"];
-    
-    @try {
-        NSString *configStr = [[self tunneledAppProtocolDelegate] getPsiphonConfig];
-        NSError *e = nil;
-        
-        GoPsiStart(
-                   configStr,
-                   @"",
-                   self,
-                   false, // useDeviceBinder
-                   &e);
-    }
-    @catch(NSException *exception) {
-        [[self tunneledAppProtocolDelegate] onDiagnosticMessage:[NSString stringWithFormat: @"failed to start Psiphon library: %@", exception.reason]];
-    }
-    [[self tunneledAppProtocolDelegate] onDiagnosticMessage:@"Psiphon library started"];
-}
-
--(void) stopTunnel {
-    [[self tunneledAppProtocolDelegate] onDiagnosticMessage: @"stopping Psiphon library"];
-    GoPsiStop();
-    [[self tunneledAppProtocolDelegate] onDiagnosticMessage: @"Psiphon library stop"];
-    
-}
-
-#pragma mark - GoPsiphonProvider protocol implementation
-
-- (NSString*)getPrimaryDnsServer {
-    return @"8.8.8.8";
-}
-
-- (NSString*)getSecondaryDnsServer {
-    return @"8.8.4.4";    
-}
-
-
-
-
-- (BOOL)bindToDevice:(long)fileDescriptor error:(NSError**)error {
-    return TRUE;
-}
-
-- (NSString*)getDnsServer {
-    //This method is used only in VPN mode
-    return @"";
-}
-
-- (long)hasNetworkConnectivity {
-    Reachability *reachability = [Reachability reachabilityForInternetConnection];
-    NetworkStatus netstat = [reachability currentReachabilityStatus];
-    return (int) netstat != NotReachable;
-}
-
-- (void)notice:(NSString*)noticeJSON {
-    NSData *noticeData = [noticeJSON dataUsingEncoding:NSUTF8StringEncoding];
-    NSError *error = nil;
-    BOOL diagnostic = TRUE;
-    
-    NSDictionary *notice = [NSJSONSerialization JSONObjectWithData:noticeData options:kNilOptions error:&error];
-    
-    if(error) {
-        
-        // TODO: handle JSON error
-        
-    }
-    else {
-        NSString *noticeType = [notice valueForKey:@"noticeType"];
-        if ([noticeType isEqualToString:@"Tunnels"]) {
-            NSInteger count = [[[notice valueForKey: @"data"] valueForKey:@"count"] integerValue];
-            if (count > 0) {
-                [self.tunneledAppProtocolDelegate onConnected];
-            } else {
-                [self.tunneledAppProtocolDelegate onConnecting];
-            }
-            
-        } else if ([noticeType isEqualToString:@"AvailableEgressRegions"]) {
-            NSArray *regions = [[notice valueForKey: @"data"] valueForKey:@"regions"];
-            [self.tunneledAppProtocolDelegate onAvailableEgressRegions:regions];
-        } else if ([noticeType isEqualToString:@"SocksProxyPortInUse"]) {
-            NSInteger port = [(NSNumber*)[[notice valueForKey: @"data"] valueForKey:@"port"] integerValue];
-            [self.tunneledAppProtocolDelegate onSocksProxyPortInUse:port];
-        } else if ([noticeType isEqualToString:@"HttpProxyPortInUse"]) {
-            NSInteger port = [(NSNumber*)[[notice valueForKey: @"data"] valueForKey:@"port"] integerValue];
-            [self.tunneledAppProtocolDelegate onHttpProxyPortInUse:port];
-        } else if ([noticeType isEqualToString:@"ListeningSocksProxyPort"]) {
-            NSInteger port = [(NSNumber*)[[notice valueForKey: @"data"] valueForKey:@"port"] integerValue];
-            [self.tunneledAppProtocolDelegate onListeningSocksProxyPort:port];
-        } else if ([noticeType isEqualToString:@"ListeningHttpProxyPort"]) {
-            NSInteger port = [(NSNumber*)[[notice valueForKey: @"data"] valueForKey:@"port"] integerValue];
-            [self.tunneledAppProtocolDelegate onListeningHttpProxyPort:port];
-        } else if ([noticeType isEqualToString:@"UpstreamProxyError"]) {
-            [self.tunneledAppProtocolDelegate onUpstreamProxyError:[[notice valueForKey: @"data"] valueForKey:@"message"]];
-        } else if ([noticeType isEqualToString:@"ClientUpgradeDownloaded"]) {
-            [self.tunneledAppProtocolDelegate onClientUpgradeDownloaded:[[notice valueForKey: @"data"] valueForKey:@"filename"]];
-        } else if ([noticeType isEqualToString:@"Homepage"]) {
-            [self.tunneledAppProtocolDelegate onHomepage:[[notice valueForKey: @"data"] valueForKey:@"url"]];
-        } else if ([noticeType isEqualToString:@"ClientRegion"]) {
-            [self.tunneledAppProtocolDelegate onClientRegion:[[notice valueForKey: @"data"] valueForKey:@"region"]];
-        } else if ([noticeType isEqualToString:@"UntunneledAddress"]) {
-            [self.tunneledAppProtocolDelegate onUntunneledAddress :[[notice valueForKey: @"data"] valueForKey:@"address"]];
-        } else if ([noticeType isEqualToString:@"BytesTransferred"]) {
-            diagnostic = FALSE;
-            NSDictionary *bytes = [notice valueForKey: @"data"];
-            [self.tunneledAppProtocolDelegate onBytesTransferred:[bytes[@"received"] longValue]:[bytes[@"sent"] longValue]];
-        }
-        
-        if (diagnostic) {
-            NSData *diagnosticData = [NSJSONSerialization dataWithJSONObject:[notice valueForKey: @"data"] options:kNilOptions error:&error];
-            if (error == nil){
-                NSString *diagnosticStr = [[NSString alloc] initWithData:diagnosticData encoding:NSUTF8StringEncoding];
-                NSString *diagnosticMessage = [NSString stringWithFormat:@"%@: %@", noticeType, diagnosticStr];
-                [self. tunneledAppProtocolDelegate onDiagnosticMessage : diagnosticMessage];
-            }
-        }
-    }
-}
-
-@end
-
-@implementation Psi
-+ (void)sendFeedback:(NSString*)configJson diagnostics: (NSString*)diagnosticsJson b64EncodedPublicKey: (NSString*) b64EncodedPublicKey uploadServer: (NSString*)uploadServer uploadPath: (NSString*) uploadPath uploadServerHeaders: (NSString*)uploadServerHeaders {
-    GoPsiSendFeedback(configJson, diagnosticsJson, b64EncodedPublicKey, uploadServer, uploadPath, uploadServerHeaders);
-}
-@end

+ 1245 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/SwiftyJSON.swift

@@ -0,0 +1,1245 @@
+//  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://www.json.org
+*/
+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:Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer? = nil) {
+        do {
+            let object: Any = try JSONSerialization.jsonObject(with: data, options: opt)
+            self.init(object)
+        } catch let aError as NSError {
+            if error != nil {
+                error??.pointee = aError
+            }
+            self.init(NSNull())
+        }
+    }
+
+    /**
+     Create a JSON from JSON string
+    - parameter string: Normal json string like '{"a":"b"}'
+
+    - returns: The created JSON
+    */
+    public static func parse(_ string:String) -> JSON {
+        return string.data(using: String.Encoding.utf8)
+            .flatMap({JSON(data: $0)}) ?? JSON(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: Any) {
+        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: Any](minimumCapacity: jsonDictionary.count)
+        for (key, json) in jsonDictionary {
+            dictionary[key] = json.object
+        }
+        self.init(dictionary)
+    }
+
+    /// fileprivate object
+    fileprivate var rawArray: [Any] = []
+    fileprivate var rawDictionary: [String : Any] = [:]
+    fileprivate var rawString: String = ""
+    fileprivate var rawNumber: NSNumber = 0
+    fileprivate var rawNull: NSNull = NSNull()
+    fileprivate var _type: Type = .null
+    fileprivate var _error: NSError? = nil
+
+    /// Object in JSON
+    public var object: Any {
+        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 [Any]:
+                _type = .array
+                self.rawArray = array
+            case let dictionary as [String : Any]:
+                _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()) } }
+}
+
+public enum JSONIndex:Comparable {
+    case array(Int)
+    case dictionary(DictionaryIndex<String, JSON>)
+    case null
+}
+
+public func ==(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs, rhs) {
+    case (.array(let left), .array(let right)):
+        return left == right
+    case (.dictionary(let left), .dictionary(let right)):
+        return left == right
+    default:
+        return false
+    }
+}
+
+public func <(lhs: JSONIndex, rhs: JSONIndex) -> Bool {
+    switch (lhs, rhs) {
+    case (.array(let left), .array(let right)):
+        return left < right
+    case (.dictionary(let left), .dictionary(let right)):
+        return left < right
+    default:
+        return false
+    }
+}
+
+
+extension JSON: Collection {
+
+    public typealias Index = JSONIndex
+
+    public var startIndex: Index{
+        switch type {
+        case .array:
+            return .array(rawArray.startIndex)
+        case .dictionary:
+            return .dictionary(dictionaryValue.startIndex)
+        default:
+            return .null
+        }
+    }
+
+    public var endIndex: Index{
+        switch type {
+        case .array:
+            return .array(rawArray.endIndex)
+        case .dictionary:
+            return .dictionary(dictionaryValue.endIndex)
+        default:
+            return .null
+        }
+    }
+
+    public func index(after i: Index) -> Index {
+        switch i {
+        case .array(let idx):
+            return .array(rawArray.index(after: idx))
+        case .dictionary(let idx):
+            return .dictionary(dictionaryValue.index(after: idx))
+        default:
+            return .null
+        }
+
+    }
+
+    public subscript (position: Index) -> (String, JSON) {
+        switch position {
+        case .array(let idx):
+            return (String(idx), JSON(self.rawArray[idx]))
+        case .dictionary(let idx):
+            return dictionaryValue[idx]
+        default:
+            return ("", JSON.null)
+        }
+    }
+
+
+}
+
+// MARK: - Subscript
+
+/**
+*  To mark both String and Int can be used in subscript.
+*/
+public enum JSONKey {
+    case index(Int)
+    case key(String)
+}
+
+public protocol JSONSubscriptType {
+    var jsonKey:JSONKey { get }
+}
+
+extension Int: JSONSubscriptType {
+    public var jsonKey:JSONKey {
+        return JSONKey.index(self)
+    }
+}
+
+extension String: JSONSubscriptType {
+    public var jsonKey:JSONKey {
+        return JSONKey.key(self)
+    }
+}
+
+extension JSON {
+
+    /// If `type` is `.Array`, return json whose object is `array[index]`, otherwise return null json with error.
+    fileprivate 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 whose object is `dictionary[key]` , otherwise return null json with error.
+    fileprivate 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:)`.
+    fileprivate subscript(sub sub: JSONSubscriptType) -> JSON {
+        get {
+            switch sub.jsonKey {
+            case .index(let index): return self[index: index]
+            case .key(let key): return self[key: key]
+            }
+        }
+        set {
+            switch sub.jsonKey {
+            case .index(let index): self[index: index] = newValue
+            case .key(let key): self[key: key] = 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.remove(at: 0)
+                var nextJSON = self[sub: path[0]]
+                nextJSON[aPath] = newValue
+                self[sub: path[0]] = nextJSON
+            }
+        }
+    }
+
+    /**
+    Find a json in the complex data structures by 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, Any)...) {
+        self.init(elements.reduce([String : Any](minimumCapacity: elements.count)){(dictionary: [String : Any], element:(String, Any)) -> [String : Any] in
+            var d = dictionary
+            d[element.0] = element.1
+            return d
+            })
+    }
+}
+
+extension JSON: Swift.ArrayLiteralConvertible {
+
+    public init(arrayLiteral elements: Any...) {
+        self.init(elements)
+    }
+}
+
+extension JSON: Swift.NilLiteralConvertible {
+
+    public init(nilLiteral: ()) {
+        self.init(NSNull())
+    }
+}
+
+// MARK: - Raw
+
+extension JSON: Swift.RawRepresentable {
+
+    public init?(rawValue: Any) {
+        if JSON(rawValue).type == .unknown {
+            return nil
+        } else {
+            self.init(rawValue)
+        }
+    }
+
+    public var rawValue: Any {
+        return self.object
+    }
+
+    public func rawData(options opt: JSONSerialization.WritingOptions = JSONSerialization.WritingOptions(rawValue: 0)) throws -> Data {
+        guard JSONSerialization.isValidJSONObject(self.object) else {
+            throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "JSON is invalid"])
+        }
+
+        return try JSONSerialization.data(withJSONObject: self.object, options: opt)
+    }
+
+    public func rawString(_ encoding: String.Encoding = String.Encoding.utf8, options opt: JSONSerialization.WritingOptions = .prettyPrinted) -> String? {
+        switch self.type {
+        case .array, .dictionary:
+            do {
+                let data = try self.rawData(options: opt)
+                return String(data: data, encoding: encoding)
+            } 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.CustomStringConvertible, Swift.CustomDebugStringConvertible {
+
+    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 [Any]
+    public var arrayObject: [Any]? {
+        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, Any)) -> [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 : Any]
+    public var dictionaryObject: [String : Any]? {
+        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 {
+
+    //Optional bool
+    public var bool: Bool? {
+        get {
+            switch self.type {
+            case .bool:
+                return self.rawNumber.boolValue
+            default:
+                return nil
+            }
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+
+    //Non-optional bool
+    public var boolValue: Bool {
+        get {
+            switch self.type {
+            case .bool, .number, .string:
+                return (self.object as AnyObject).boolValue
+            default:
+                return false
+            }
+        }
+        set {
+            self.object = NSNumber(value: 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 let newValue = newValue {
+                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 as AnyObject).stringValue
+            case .bool:
+                return (self.object as? Bool).map { String($0) } ?? ""
+            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 ?? NSNumber(value: 0)
+            default:
+                return NSNumber(value: 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 exists() -> Bool{
+        if let errorValue = error, errorValue.code == ErrorNotExist{
+            return false
+        }
+        return true
+    }
+}
+
+//MARK: - URL
+extension JSON {
+
+    //Optional URL
+    public var URL: Foundation.URL? {
+        get {
+            switch self.type {
+            case .string:
+                if let encodedString_ = self.rawString.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) {
+                    return Foundation.URL(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 let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+
+    public var doubleValue: Double {
+        get {
+            return self.numberValue.doubleValue
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var float: Float? {
+        get {
+            return self.number?.floatValue
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+
+    public var floatValue: Float {
+        get {
+            return self.numberValue.floatValue
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var int: Int? {
+        get {
+            return self.number?.intValue
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+
+    public var intValue: Int {
+        get {
+            return self.numberValue.intValue
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var uInt: UInt? {
+        get {
+            return self.number?.uintValue
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object = NSNull()
+            }
+        }
+    }
+
+    public var uIntValue: UInt {
+        get {
+            return self.numberValue.uintValue
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var int8: Int8? {
+        get {
+            return self.number?.int8Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var int8Value: Int8 {
+        get {
+            return self.numberValue.int8Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var uInt8: UInt8? {
+        get {
+            return self.number?.uint8Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var uInt8Value: UInt8 {
+        get {
+            return self.numberValue.uint8Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var int16: Int16? {
+        get {
+            return self.number?.int16Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var int16Value: Int16 {
+        get {
+            return self.numberValue.int16Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var uInt16: UInt16? {
+        get {
+            return self.number?.uint16Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var uInt16Value: UInt16 {
+        get {
+            return self.numberValue.uint16Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var int32: Int32? {
+        get {
+            return self.number?.int32Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var int32Value: Int32 {
+        get {
+            return self.numberValue.int32Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var uInt32: UInt32? {
+        get {
+            return self.number?.uint32Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var uInt32Value: UInt32 {
+        get {
+            return self.numberValue.uint32Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var int64: Int64? {
+        get {
+            return self.number?.int64Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var int64Value: Int64 {
+        get {
+            return self.numberValue.int64Value
+        }
+        set {
+            self.object = NSNumber(value: newValue)
+        }
+    }
+
+    public var uInt64: UInt64? {
+        get {
+            return self.number?.uint64Value
+        }
+        set {
+            if let newValue = newValue {
+                self.object = NSNumber(value: newValue)
+            } else {
+                self.object =  NSNull()
+            }
+        }
+    }
+
+    public var uInt64Value: UInt64 {
+        get {
+            return self.numberValue.uint64Value
+        }
+        set {
+            self.object = NSNumber(value: 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
+    }
+}
+
+fileprivate let trueNumber = NSNumber(value: true)
+fileprivate let falseNumber = NSNumber(value: false)
+fileprivate let trueObjCType = String(cString: trueNumber.objCType)
+fileprivate let falseObjCType = String(cString: falseNumber.objCType)
+
+
+//A C++ bool or a C99 _Bool
+//Do Not Know Why self.objCType is "B", maybe a bug.
+fileprivate let cppBoolType = "B"
+
+// MARK: - NSNumber: Comparable
+
+extension NSNumber {
+    var isBool:Bool {
+        get {
+
+            let objCType = String(cString: self.objCType)
+
+            if (self.compare(trueNumber) == ComparisonResult.orderedSame && (objCType == trueObjCType || objCType == cppBoolType))
+                || (self.compare(falseNumber) == ComparisonResult.orderedSame && (objCType == falseObjCType || objCType == cppBoolType)){
+                    return true
+            } else {
+                return false
+            }
+        }
+    }
+}
+
+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) == ComparisonResult.orderedSame
+    }
+}
+
+func !=(lhs: NSNumber, rhs: NSNumber) -> Bool {
+    return !(lhs == rhs)
+}
+
+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) == ComparisonResult.orderedAscending
+    }
+}
+
+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) == ComparisonResult.orderedDescending
+    }
+}
+
+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) != ComparisonResult.orderedDescending
+    }
+}
+
+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) != ComparisonResult.orderedAscending
+    }
+}

+ 7 - 3
MobileLibrary/iOS/README.md

@@ -14,7 +14,7 @@ conventions for using a Go library in an iOS app.
 * [git](https://git-scm.com/download/mac)
 * homebrew
   * Install from terminal: `/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"`
-* golang 
+* golang
   * Install from terminal: `brew install go`
 
 ####Build Steps
@@ -25,6 +25,10 @@ conventions for using a Go library in an iOS app.
 
 Coming soon
 
+* `$(PROJECT_DIR)/PsiphonTunnelController.framework` (or wherever the framework is located) must be added to the project's "Framework Search Paths" setting.
+* Add framework as Embedded Binary in General settings.
+* New user-defined value in project settings: `STRIP_BITCODE_FROM_COPIED_FILES: NO`.
+
 ###Acknowledgements
 
 Psiphon iOS Library uses:
@@ -32,11 +36,11 @@ Psiphon iOS Library uses:
 
 ####OpenSSL-for-iPhone Changes
 
-`build-libssl.sh` rebuilds openssl on every run.  Modifications were made to 
+`build-libssl.sh` rebuilds openssl on every run.  Modifications were made to
 not run unless required, they are:
 
 * Check if `libssl.a` and `libcrypto.a` are built and compare the version strings
 found in files to the `VERSION` variable in `build-libssl.sh`.
 
-* A new variable `FORCE_BUILD` is set to force a build.  Set this to *true* as 
+* A new variable `FORCE_BUILD` is set to force a build.  Set this to *true* as
 necessary.

Некоторые файлы не были показаны из-за большого количества измененных файлов