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

Merge pull request #233 from efryntov/master

Umbrella framework
Rod Hynes 9 лет назад
Родитель
Сommit
67e62b32ec
14 измененных файлов с 1426 добавлено и 2 удалено
  1. 3 0
      MobileLibrary/iOS/.gitignore
  2. 3 0
      MobileLibrary/iOS/PsiphonTunnelController/.gitignore
  3. 344 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.pbxproj
  4. 7 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/contents.xcworkspacedata
  5. 114 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/xcshareddata/PsiphonTunnelController.xcscmblueprint
  6. 22 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/xcuserdata/eugene.xcuserdatad/WorkspaceSettings.xcsettings
  7. 80 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/xcuserdata/eugene.xcuserdatad/xcschemes/PsiphonTunnelController.xcscheme
  8. 22 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/xcuserdata/eugene.xcuserdatad/xcschemes/xcschememanagement.plist
  9. 24 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Info.plist
  10. 54 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/PsiphonTunnelController.h
  11. 152 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/PsiphonTunnelController.m
  12. 102 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Reachability.h
  13. 481 0
      MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Reachability.m
  14. 18 2
      MobileLibrary/iOS/build-psiphon-framework.sh

+ 3 - 0
MobileLibrary/iOS/.gitignore

@@ -0,0 +1,3 @@
+framework
+go-ios-build
+

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

@@ -0,0 +1,3 @@
+project.xcworkspace
+xcuserdata
+build

+ 344 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.pbxproj

@@ -0,0 +1,344 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		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 */; };
+		44980ACD1D63A3EA00B78274 /* Reachability.m in Sources */ = {isa = PBXBuildFile; fileRef = 44980ACA1D63A3EA00B78274 /* Reachability.m */; };
+		44980AD71D63A6FD00B78274 /* Psi.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 44980AD61D63A6FD00B78274 /* Psi.framework */; };
+		44980ADA1D63B0A800B78274 /* Psi.framework in Resources */ = {isa = PBXBuildFile; fileRef = 44980AD61D63A6FD00B78274 /* Psi.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		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>"; };
+		44980AD61D63A6FD00B78274 /* Psi.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Psi.framework; path = ../framework/Psi.framework; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		44980AB91D63A3B300B78274 /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				44980AD71D63A6FD00B78274 /* Psi.framework in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		44980AB31D63A3B300B78274 = {
+			isa = PBXGroup;
+			children = (
+				44980ABF1D63A3B300B78274 /* PsiphonTunnelController */,
+				44980ABE1D63A3B300B78274 /* Products */,
+				44980AD51D63A6FD00B78274 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		44980ABE1D63A3B300B78274 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				44980ABD1D63A3B300B78274 /* PsiphonTunnelController.framework */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		44980ABF1D63A3B300B78274 /* PsiphonTunnelController */ = {
+			isa = PBXGroup;
+			children = (
+				44980AC81D63A3EA00B78274 /* PsiphonTunnelController.m */,
+				44980AC91D63A3EA00B78274 /* Reachability.h */,
+				44980ACA1D63A3EA00B78274 /* Reachability.m */,
+				44980AC01D63A3B300B78274 /* PsiphonTunnelController.h */,
+				44980AC11D63A3B300B78274 /* Info.plist */,
+			);
+			path = PsiphonTunnelController;
+			sourceTree = "<group>";
+		};
+		44980AD51D63A6FD00B78274 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				44980AD61D63A6FD00B78274 /* Psi.framework */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXHeadersBuildPhase section */
+		44980ABA1D63A3B300B78274 /* Headers */ = {
+			isa = PBXHeadersBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				44980AC21D63A3B300B78274 /* PsiphonTunnelController.h in Headers */,
+				44980ACC1D63A3EA00B78274 /* Reachability.h in Headers */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXHeadersBuildPhase section */
+
+/* Begin PBXNativeTarget section */
+		44980ABC1D63A3B300B78274 /* PsiphonTunnelController */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 44980AC51D63A3B300B78274 /* Build configuration list for PBXNativeTarget "PsiphonTunnelController" */;
+			buildPhases = (
+				44980AB81D63A3B300B78274 /* Sources */,
+				44980AB91D63A3B300B78274 /* Frameworks */,
+				44980ABA1D63A3B300B78274 /* Headers */,
+				44980ABB1D63A3B300B78274 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = PsiphonTunnelController;
+			productName = PsiphonTunnelController;
+			productReference = 44980ABD1D63A3B300B78274 /* PsiphonTunnelController.framework */;
+			productType = "com.apple.product-type.framework";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		44980AB41D63A3B300B78274 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0800;
+				ORGANIZATIONNAME = "Psiphon Inc.";
+				TargetAttributes = {
+					44980ABC1D63A3B300B78274 = {
+						CreatedOnToolsVersion = 8.0;
+						ProvisioningStyle = Manual;
+					};
+				};
+			};
+			buildConfigurationList = 44980AB71D63A3B300B78274 /* Build configuration list for PBXProject "PsiphonTunnelController" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = 44980AB31D63A3B300B78274;
+			productRefGroup = 44980ABE1D63A3B300B78274 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				44980ABC1D63A3B300B78274 /* PsiphonTunnelController */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		44980ABB1D63A3B300B78274 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				44980ADA1D63B0A800B78274 /* Psi.framework in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		44980AB81D63A3B300B78274 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				44980ACD1D63A3EA00B78274 /* Reachability.m in Sources */,
+				44980ACB1D63A3EA00B78274 /* PsiphonTunnelController.m in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		44980AC31D63A3B300B78274 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVES = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_BITCODE = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				OTHER_LDFLAGS = (
+					"-read_only_relocs",
+					suppress,
+				);
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Debug;
+		};
+		44980AC41D63A3B300B78274 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_SUSPICIOUS_MOVES = YES;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				CURRENT_PROJECT_VERSION = 1;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_BITCODE = NO;
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				OTHER_LDFLAGS = (
+					"-read_only_relocs",
+					suppress,
+				);
+				SDKROOT = iphoneos;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALIDATE_PRODUCT = YES;
+				VERSIONING_SYSTEM = "apple-generic";
+				VERSION_INFO_PREFIX = "";
+			};
+			name = Release;
+		};
+		44980AC61D63A3B300B78274 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				CODE_SIGN_IDENTITY = "";
+				DEFINES_MODULE = NO;
+				DEVELOPMENT_TEAM = "";
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = ../framework;
+				INFOPLIST_FILE = PsiphonTunnelController/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				ONLY_ACTIVE_ARCH = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = com.psiphon.PsiphonTunnelController;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALID_ARCHS = "arm64 armv7 armv7s";
+			};
+			name = Debug;
+		};
+		44980AC71D63A3B300B78274 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ARCHS = "$(ARCHS_STANDARD)";
+				CODE_SIGN_IDENTITY = "";
+				DEFINES_MODULE = NO;
+				DEVELOPMENT_TEAM = "";
+				DYLIB_COMPATIBILITY_VERSION = 1;
+				DYLIB_CURRENT_VERSION = 1;
+				DYLIB_INSTALL_NAME_BASE = "@rpath";
+				ENABLE_BITCODE = NO;
+				FRAMEWORK_SEARCH_PATHS = ../framework;
+				INFOPLIST_FILE = PsiphonTunnelController/Info.plist;
+				INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				ONLY_ACTIVE_ARCH = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = com.psiphon.PsiphonTunnelController;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				SKIP_INSTALL = YES;
+				TARGETED_DEVICE_FAMILY = "1,2";
+				VALID_ARCHS = "arm64 armv7 armv7s";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		44980AB71D63A3B300B78274 /* Build configuration list for PBXProject "PsiphonTunnelController" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				44980AC31D63A3B300B78274 /* Debug */,
+				44980AC41D63A3B300B78274 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		44980AC51D63A3B300B78274 /* Build configuration list for PBXNativeTarget "PsiphonTunnelController" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				44980AC61D63A3B300B78274 /* Debug */,
+				44980AC71D63A3B300B78274 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 44980AB41D63A3B300B78274 /* Project object */;
+}

+ 7 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Workspace
+   version = "1.0">
+   <FileRef
+      location = "self:PsiphonTunnelController.xcodeproj">
+   </FileRef>
+</Workspace>

+ 114 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/xcshareddata/PsiphonTunnelController.xcscmblueprint

@@ -0,0 +1,114 @@
+{
+  "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++F92F6D3",
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : {
+
+  },
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : {
+    "8B61C8B48DB4B96AD94F67018EFB8A9684F3EFD6+++DCC608E" : 9223372036854775807,
+    "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++38A1A30" : 9223372036854775807,
+    "A5AD174D8B9671567C38590D166034173C866E1D+++C7AE692" : 9223372036854775807,
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++ED25EBA" : 9223372036854775807,
+    "3CDC0364FAC963F881E15357C59A43097543A382+++74D281F" : 9223372036854775807,
+    "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++F92F6D3" : 9223372036854775807,
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++D9DC38C" : 9223372036854775807,
+    "D059B056F0F8D707428CFA53B26B54CB05124896+++41B0777" : 9223372036854775807,
+    "C811301B9BA5BED994AD79D58835794EFE2C4BAD+++5A1EDEB" : 9223372036854775807,
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++739CA66" : 9223372036854775807,
+    "A5AD174D8B9671567C38590D166034173C866E1D+++795FDF9" : 9223372036854775807,
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++0719F03" : 9223372036854775807,
+    "0EDA2F64A27382CC2BF1F85AB9A4AEECDF2AEE03+++14C946E" : 9223372036854775807,
+    "0559510868EA25C63F4143C7E150BF185B7A091F+++F4FA2CA" : 9223372036854775807
+  },
+  "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "7D3F11EA-BDE9-4C77-841B-858842838E65",
+  "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
+    "8B61C8B48DB4B96AD94F67018EFB8A9684F3EFD6+++DCC608E" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Inc\/bolt\/",
+    "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++38A1A30" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Labs\/psiphon-tunnel-core\/",
+    "A5AD174D8B9671567C38590D166034173C866E1D+++C7AE692" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/spacemonkeygo\/openssl\/",
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++ED25EBA" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/golang.org\/x\/net\/",
+    "3CDC0364FAC963F881E15357C59A43097543A382+++74D281F" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Inc\/dns\/",
+    "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++F92F6D3" : "psiphon-tunnel-core\/",
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++D9DC38C" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/golang.org\/x\/net\/",
+    "D059B056F0F8D707428CFA53B26B54CB05124896+++41B0777" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Inc\/goptlib\/",
+    "C811301B9BA5BED994AD79D58835794EFE2C4BAD+++5A1EDEB" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Inc\/goregen\/",
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++739CA66" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/golang.org\/x\/net\/",
+    "A5AD174D8B9671567C38590D166034173C866E1D+++795FDF9" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/spacemonkeygo\/openssl\/",
+    "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++0719F03" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/golang.org\/x\/net\/",
+    "0EDA2F64A27382CC2BF1F85AB9A4AEECDF2AEE03+++14C946E" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/Psiphon-Inc\/ratelimit\/",
+    "0559510868EA25C63F4143C7E150BF185B7A091F+++F4FA2CA" : "psiphon-tunnel-core\/MobileLibrary\/iOS\/go-ios-build\/src\/github.com\/spacemonkeygo\/spacelog\/"
+  },
+  "DVTSourceControlWorkspaceBlueprintNameKey" : "PsiphonTunnelController",
+  "DVTSourceControlWorkspaceBlueprintVersion" : 204,
+  "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "MobileLibrary\/iOS\/PsiphonTunnelController\/PsiphonTunnelController.xcodeproj",
+  "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/spacemonkeygo\/spacelog",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "0559510868EA25C63F4143C7E150BF185B7A091F+++F4FA2CA"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/ratelimit",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "0EDA2F64A27382CC2BF1F85AB9A4AEECDF2AEE03+++14C946E"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/go.googlesource.com\/net",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++0719F03"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/go.googlesource.com\/crypto",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++739CA66"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/go.googlesource.com\/mobile",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++D9DC38C"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/crypto",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "18F8E8F32D4612669B2AC73CE391ED54473F75DA+++ED25EBA"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/dns",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "3CDC0364FAC963F881E15357C59A43097543A382+++74D281F"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:efryntov\/psiphon-tunnel-core.git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++38A1A30"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "github.com:efryntov\/psiphon-tunnel-core.git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "654BDFA72D0BD5C4AAA375C3F6D7EA32AC58A304+++F92F6D3"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/bolt",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "8B61C8B48DB4B96AD94F67018EFB8A9684F3EFD6+++DCC608E"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/spacemonkeygo\/openssl",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "A5AD174D8B9671567C38590D166034173C866E1D+++795FDF9"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/openssl",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "A5AD174D8B9671567C38590D166034173C866E1D+++C7AE692"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/goregen",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "C811301B9BA5BED994AD79D58835794EFE2C4BAD+++5A1EDEB"
+    },
+    {
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/Psiphon-Inc\/goptlib",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git",
+      "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D059B056F0F8D707428CFA53B26B54CB05124896+++41B0777"
+    }
+  ]
+}

+ 22 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/project.xcworkspace/xcuserdata/eugene.xcuserdatad/WorkspaceSettings.xcsettings

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>BuildLocationStyle</key>
+	<string>CustomLocation</string>
+	<key>CustomBuildIntermediatesPath</key>
+	<string>Build/Intermediates</string>
+	<key>CustomBuildLocationType</key>
+	<string>RelativeToWorkspace</string>
+	<key>CustomBuildProductsPath</key>
+	<string>Build/Products</string>
+	<key>DerivedDataCustomLocation</key>
+	<string>/Users/eugene/Library/Developer/Xcode/DerivedData</string>
+	<key>DerivedDataLocationStyle</key>
+	<string>AbsolutePath</string>
+	<key>IssueFilterStyle</key>
+	<string>ShowActiveSchemeOnly</string>
+	<key>LiveSourceIssuesEnabled</key>
+	<true/>
+</dict>
+</plist>

+ 80 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/xcuserdata/eugene.xcuserdatad/xcschemes/PsiphonTunnelController.xcscheme

@@ -0,0 +1,80 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0800"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+      <BuildActionEntries>
+         <BuildActionEntry
+            buildForTesting = "YES"
+            buildForRunning = "YES"
+            buildForProfiling = "YES"
+            buildForArchiving = "YES"
+            buildForAnalyzing = "YES">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "44980ABC1D63A3B300B78274"
+               BuildableName = "PsiphonTunnelController.framework"
+               BlueprintName = "PsiphonTunnelController"
+               ReferencedContainer = "container:PsiphonTunnelController.xcodeproj">
+            </BuildableReference>
+         </BuildActionEntry>
+      </BuildActionEntries>
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Release"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "44980ABC1D63A3B300B78274"
+            BuildableName = "PsiphonTunnelController.framework"
+            BlueprintName = "PsiphonTunnelController"
+            ReferencedContainer = "container:PsiphonTunnelController.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+      <MacroExpansion>
+         <BuildableReference
+            BuildableIdentifier = "primary"
+            BlueprintIdentifier = "44980ABC1D63A3B300B78274"
+            BuildableName = "PsiphonTunnelController.framework"
+            BlueprintName = "PsiphonTunnelController"
+            ReferencedContainer = "container:PsiphonTunnelController.xcodeproj">
+         </BuildableReference>
+      </MacroExpansion>
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>

+ 22 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/xcuserdata/eugene.xcuserdatad/xcschemes/xcschememanagement.plist

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>SchemeUserState</key>
+	<dict>
+		<key>PsiphonTunnelController.xcscheme</key>
+		<dict>
+			<key>orderHint</key>
+			<integer>0</integer>
+		</dict>
+	</dict>
+	<key>SuppressBuildableAutocreation</key>
+	<dict>
+		<key>44980ABC1D63A3B300B78274</key>
+		<dict>
+			<key>primary</key>
+			<true/>
+		</dict>
+	</dict>
+</dict>
+</plist>

+ 24 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Info.plist

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>en</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>FMWK</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>$(CURRENT_PROJECT_VERSION)</string>
+	<key>NSPrincipalClass</key>
+	<string></string>
+</dict>
+</plist>

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

@@ -0,0 +1,54 @@
+//
+//  PsiphonMobile.h
+//  PsiphonMobile
+//
+//  Created by eugene-imac on 2016-08-16.
+//  Copyright © 2016 Psiphon Inc. All rights reserved.
+//
+
+#import <UIKit/UIKit.h>
+
+
+//! 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

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

@@ -0,0 +1,152 @@
+//
+//  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;
+    return 1L;
+    
+}
+
+- (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
+

+ 102 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Reachability.h

@@ -0,0 +1,102 @@
+/*
+ Copyright (c) 2011, Tony Million.
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import <Foundation/Foundation.h>
+#import <SystemConfiguration/SystemConfiguration.h>
+
+//! Project version number for MacOSReachability.
+FOUNDATION_EXPORT double ReachabilityVersionNumber;
+
+//! Project version string for MacOSReachability.
+FOUNDATION_EXPORT const unsigned char ReachabilityVersionString[];
+
+/**
+ * Create NS_ENUM macro if it does not exist on the targeted version of iOS or OS X.
+ *
+ * @see http://nshipster.com/ns_enum-ns_options/
+ **/
+#ifndef NS_ENUM
+#define NS_ENUM(_type, _name) enum _name : _type _name; enum _name : _type
+#endif
+
+extern NSString *const kReachabilityChangedNotification;
+
+typedef NS_ENUM(NSInteger, NetworkStatus) {
+    // Apple NetworkStatus Compatible Names.
+    NotReachable = 0,
+    ReachableViaWiFi = 2,
+    ReachableViaWWAN = 1
+};
+
+@class Reachability;
+
+typedef void (^NetworkReachable)(Reachability * reachability);
+typedef void (^NetworkUnreachable)(Reachability * reachability);
+typedef void (^NetworkReachability)(Reachability * reachability, SCNetworkConnectionFlags flags);
+
+
+@interface Reachability : NSObject
+
+@property (nonatomic, copy) NetworkReachable    reachableBlock;
+@property (nonatomic, copy) NetworkUnreachable  unreachableBlock;
+@property (nonatomic, copy) NetworkReachability reachabilityBlock;
+
+@property (nonatomic, assign) BOOL reachableOnWWAN;
+
+
++(instancetype)reachabilityWithHostname:(NSString*)hostname;
+// This is identical to the function above, but is here to maintain
+//compatibility with Apples original code. (see .m)
++(instancetype)reachabilityWithHostName:(NSString*)hostname;
++(instancetype)reachabilityForInternetConnection;
++(instancetype)reachabilityWithAddress:(void *)hostAddress;
++(instancetype)reachabilityForLocalWiFi;
+
+-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref;
+
+-(BOOL)startNotifier;
+-(void)stopNotifier;
+
+-(BOOL)isReachable;
+-(BOOL)isReachableViaWWAN;
+-(BOOL)isReachableViaWiFi;
+
+// WWAN may be available, but not active until a connection has been established.
+// WiFi may require a connection for VPN on Demand.
+-(BOOL)isConnectionRequired; // Identical DDG variant.
+-(BOOL)connectionRequired; // Apple's routine.
+// Dynamic, on demand connection?
+-(BOOL)isConnectionOnDemand;
+// Is user intervention required?
+-(BOOL)isInterventionRequired;
+
+-(NetworkStatus)currentReachabilityStatus;
+-(SCNetworkReachabilityFlags)reachabilityFlags;
+-(NSString*)currentReachabilityString;
+-(NSString*)currentReachabilityFlags;
+
+@end

+ 481 - 0
MobileLibrary/iOS/PsiphonTunnelController/PsiphonTunnelController/Reachability.m

@@ -0,0 +1,481 @@
+/*
+ Copyright (c) 2011, Tony Million.
+ All rights reserved.
+ 
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ 
+ 1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+ 
+ 2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+ 
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#import "Reachability.h"
+
+#import <sys/socket.h>
+#import <netinet/in.h>
+#import <netinet6/in6.h>
+#import <arpa/inet.h>
+#import <ifaddrs.h>
+#import <netdb.h>
+
+
+NSString *const kReachabilityChangedNotification = @"kReachabilityChangedNotification";
+
+
+@interface Reachability ()
+
+@property (nonatomic, assign) SCNetworkReachabilityRef  reachabilityRef;
+@property (nonatomic, strong) dispatch_queue_t          reachabilitySerialQueue;
+@property (nonatomic, strong) id                        reachabilityObject;
+
+-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags;
+-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags;
+
+@end
+
+
+static NSString *reachabilityFlags(SCNetworkReachabilityFlags flags)
+{
+    return [NSString stringWithFormat:@"%c%c %c%c%c%c%c%c%c",
+#if	TARGET_OS_IPHONE
+            (flags & kSCNetworkReachabilityFlagsIsWWAN)               ? 'W' : '-',
+#else
+            'X',
+#endif
+            (flags & kSCNetworkReachabilityFlagsReachable)            ? 'R' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionRequired)   ? 'c' : '-',
+            (flags & kSCNetworkReachabilityFlagsTransientConnection)  ? 't' : '-',
+            (flags & kSCNetworkReachabilityFlagsInterventionRequired) ? 'i' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionOnTraffic)  ? 'C' : '-',
+            (flags & kSCNetworkReachabilityFlagsConnectionOnDemand)   ? 'D' : '-',
+            (flags & kSCNetworkReachabilityFlagsIsLocalAddress)       ? 'l' : '-',
+            (flags & kSCNetworkReachabilityFlagsIsDirect)             ? 'd' : '-'];
+}
+
+// Start listening for reachability notifications on the current run loop
+static void TMReachabilityCallback(SCNetworkReachabilityRef target, SCNetworkReachabilityFlags flags, void* info)
+{
+#pragma unused (target)
+    
+    Reachability *reachability = ((__bridge Reachability*)info);
+    
+    // We probably don't need an autoreleasepool here, as GCD docs state each queue has its own autorelease pool,
+    // but what the heck eh?
+    @autoreleasepool
+    {
+        [reachability reachabilityChanged:flags];
+    }
+}
+
+
+@implementation Reachability
+
+#pragma mark - Class Constructor Methods
+
++(instancetype)reachabilityWithHostName:(NSString*)hostname
+{
+    return [Reachability reachabilityWithHostname:hostname];
+}
+
++(instancetype)reachabilityWithHostname:(NSString*)hostname
+{
+    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithName(NULL, [hostname UTF8String]);
+    if (ref)
+    {
+        id reachability = [[self alloc] initWithReachabilityRef:ref];
+        
+        CFRelease(ref);
+        
+        return reachability;
+    }
+    
+    return nil;
+}
+
++(instancetype)reachabilityWithAddress:(void *)hostAddress
+{
+    SCNetworkReachabilityRef ref = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr*)hostAddress);
+    if (ref)
+    {
+        id reachability = [[self alloc] initWithReachabilityRef:ref];
+        
+        CFRelease(ref);
+        
+        return reachability;
+    }
+    
+    return nil;
+}
+
++(instancetype)reachabilityForInternetConnection
+{
+    struct sockaddr_in zeroAddress;
+    bzero(&zeroAddress, sizeof(zeroAddress));
+    zeroAddress.sin_len = sizeof(zeroAddress);
+    zeroAddress.sin_family = AF_INET;
+    
+    return [self reachabilityWithAddress:&zeroAddress];
+}
+
++(instancetype)reachabilityForLocalWiFi
+{
+    struct sockaddr_in localWifiAddress;
+    bzero(&localWifiAddress, sizeof(localWifiAddress));
+    localWifiAddress.sin_len            = sizeof(localWifiAddress);
+    localWifiAddress.sin_family         = AF_INET;
+    // IN_LINKLOCALNETNUM is defined in <netinet/in.h> as 169.254.0.0
+    localWifiAddress.sin_addr.s_addr    = htonl(IN_LINKLOCALNETNUM);
+    
+    return [self reachabilityWithAddress:&localWifiAddress];
+}
+
+
+// Initialization methods
+
+-(instancetype)initWithReachabilityRef:(SCNetworkReachabilityRef)ref
+{
+    self = [super init];
+    if (self != nil)
+    {
+        self.reachableOnWWAN = YES;
+        self.reachabilityRef = ref;
+        
+        CFRetain(self.reachabilityRef);
+        
+        // We need to create a serial queue.
+        // We allocate this once for the lifetime of the notifier.
+        
+        self.reachabilitySerialQueue = dispatch_queue_create("com.tonymillion.reachability", NULL);
+    }
+    
+    return self;
+}
+
+-(void)dealloc
+{
+    [self stopNotifier];
+    
+    if(self.reachabilityRef)
+    {
+        CFRelease(self.reachabilityRef);
+        self.reachabilityRef = nil;
+    }
+    
+    self.reachableBlock          = nil;
+    self.unreachableBlock        = nil;
+    self.reachabilityBlock       = nil;
+    self.reachabilitySerialQueue = nil;
+}
+
+#pragma mark - Notifier Methods
+
+// Notifier
+// NOTE: This uses GCD to trigger the blocks - they *WILL NOT* be called on THE MAIN THREAD
+// - In other words DO NOT DO ANY UI UPDATES IN THE BLOCKS.
+//   INSTEAD USE dispatch_async(dispatch_get_main_queue(), ^{UISTUFF}) (or dispatch_sync if you want)
+
+-(BOOL)startNotifier
+{
+    // allow start notifier to be called multiple times
+    if(self.reachabilityObject && (self.reachabilityObject == self))
+    {
+        return YES;
+    }
+    
+    
+    SCNetworkReachabilityContext    context = { 0, NULL, NULL, NULL, NULL };
+    context.info = (__bridge void *)self;
+    
+    if(SCNetworkReachabilitySetCallback(self.reachabilityRef, TMReachabilityCallback, &context))
+    {
+        // Set it as our reachability queue, which will retain the queue
+        if(SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, self.reachabilitySerialQueue))
+        {
+            // this should do a retain on ourself, so as long as we're in notifier mode we shouldn't disappear out from under ourselves
+            // woah
+            self.reachabilityObject = self;
+            return YES;
+        }
+        else
+        {
+#ifdef DEBUG
+            NSLog(@"SCNetworkReachabilitySetDispatchQueue() failed: %s", SCErrorString(SCError()));
+#endif
+            
+            // UH OH - FAILURE - stop any callbacks!
+            SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+        }
+    }
+    else
+    {
+#ifdef DEBUG
+        NSLog(@"SCNetworkReachabilitySetCallback() failed: %s", SCErrorString(SCError()));
+#endif
+    }
+    
+    // if we get here we fail at the internet
+    self.reachabilityObject = nil;
+    return NO;
+}
+
+-(void)stopNotifier
+{
+    // First stop, any callbacks!
+    SCNetworkReachabilitySetCallback(self.reachabilityRef, NULL, NULL);
+    
+    // Unregister target from the GCD serial dispatch queue.
+    SCNetworkReachabilitySetDispatchQueue(self.reachabilityRef, NULL);
+    
+    self.reachabilityObject = nil;
+}
+
+#pragma mark - reachability tests
+
+// This is for the case where you flick the airplane mode;
+// you end up getting something like this:
+//Reachability: WR ct-----
+//Reachability: -- -------
+//Reachability: WR ct-----
+//Reachability: -- -------
+// We treat this as 4 UNREACHABLE triggers - really apple should do better than this
+
+#define testcase (kSCNetworkReachabilityFlagsConnectionRequired | kSCNetworkReachabilityFlagsTransientConnection)
+
+-(BOOL)isReachableWithFlags:(SCNetworkReachabilityFlags)flags
+{
+    BOOL connectionUP = YES;
+    
+    if(!(flags & kSCNetworkReachabilityFlagsReachable))
+        connectionUP = NO;
+    
+    if( (flags & testcase) == testcase )
+        connectionUP = NO;
+    
+#if	TARGET_OS_IPHONE
+    if(flags & kSCNetworkReachabilityFlagsIsWWAN)
+    {
+        // We're on 3G.
+        if(!self.reachableOnWWAN)
+        {
+            // We don't want to connect when on 3G.
+            connectionUP = NO;
+        }
+    }
+#endif
+    
+    return connectionUP;
+}
+
+-(BOOL)isReachable
+{
+    SCNetworkReachabilityFlags flags;
+    
+    if(!SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+        return NO;
+    
+    return [self isReachableWithFlags:flags];
+}
+
+-(BOOL)isReachableViaWWAN
+{
+#if	TARGET_OS_IPHONE
+    
+    SCNetworkReachabilityFlags flags = 0;
+    
+    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        // Check we're REACHABLE
+        if(flags & kSCNetworkReachabilityFlagsReachable)
+        {
+            // Now, check we're on WWAN
+            if(flags & kSCNetworkReachabilityFlagsIsWWAN)
+            {
+                return YES;
+            }
+        }
+    }
+#endif
+    
+    return NO;
+}
+
+-(BOOL)isReachableViaWiFi
+{
+    SCNetworkReachabilityFlags flags = 0;
+    
+    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        // Check we're reachable
+        if((flags & kSCNetworkReachabilityFlagsReachable))
+        {
+#if	TARGET_OS_IPHONE
+            // Check we're NOT on WWAN
+            if((flags & kSCNetworkReachabilityFlagsIsWWAN))
+            {
+                return NO;
+            }
+#endif
+            return YES;
+        }
+    }
+    
+    return NO;
+}
+
+
+// WWAN may be available, but not active until a connection has been established.
+// WiFi may require a connection for VPN on Demand.
+-(BOOL)isConnectionRequired
+{
+    return [self connectionRequired];
+}
+
+-(BOOL)connectionRequired
+{
+    SCNetworkReachabilityFlags flags;
+    
+    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        return (flags & kSCNetworkReachabilityFlagsConnectionRequired);
+    }
+    
+    return NO;
+}
+
+// Dynamic, on demand connection?
+-(BOOL)isConnectionOnDemand
+{
+    SCNetworkReachabilityFlags flags;
+    
+    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+                (flags & (kSCNetworkReachabilityFlagsConnectionOnTraffic | kSCNetworkReachabilityFlagsConnectionOnDemand)));
+    }
+    
+    return NO;
+}
+
+// Is user intervention required?
+-(BOOL)isInterventionRequired
+{
+    SCNetworkReachabilityFlags flags;
+    
+    if (SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        return ((flags & kSCNetworkReachabilityFlagsConnectionRequired) &&
+                (flags & kSCNetworkReachabilityFlagsInterventionRequired));
+    }
+    
+    return NO;
+}
+
+
+#pragma mark - reachability status stuff
+
+-(NetworkStatus)currentReachabilityStatus
+{
+    if([self isReachable])
+    {
+        if([self isReachableViaWiFi])
+            return ReachableViaWiFi;
+        
+#if	TARGET_OS_IPHONE
+        return ReachableViaWWAN;
+#endif
+    }
+    
+    return NotReachable;
+}
+
+-(SCNetworkReachabilityFlags)reachabilityFlags
+{
+    SCNetworkReachabilityFlags flags = 0;
+    
+    if(SCNetworkReachabilityGetFlags(self.reachabilityRef, &flags))
+    {
+        return flags;
+    }
+    
+    return 0;
+}
+
+-(NSString*)currentReachabilityString
+{
+    NetworkStatus temp = [self currentReachabilityStatus];
+    
+    if(temp == ReachableViaWWAN)
+    {
+        // Updated for the fact that we have CDMA phones now!
+        return NSLocalizedString(@"Cellular", @"");
+    }
+    if (temp == ReachableViaWiFi)
+    {
+        return NSLocalizedString(@"WiFi", @"");
+    }
+    
+    return NSLocalizedString(@"No Connection", @"");
+}
+
+-(NSString*)currentReachabilityFlags
+{
+    return reachabilityFlags([self reachabilityFlags]);
+}
+
+#pragma mark - Callback function calls this method
+
+-(void)reachabilityChanged:(SCNetworkReachabilityFlags)flags
+{
+    if([self isReachableWithFlags:flags])
+    {
+        if(self.reachableBlock)
+        {
+            self.reachableBlock(self);
+        }
+    }
+    else
+    {
+        if(self.unreachableBlock)
+        {
+            self.unreachableBlock(self);
+        }
+    }
+    
+    if(self.reachabilityBlock)
+    {
+        self.reachabilityBlock(self, flags);
+    }
+    
+    // this makes sure the change notification happens on the MAIN THREAD
+    dispatch_async(dispatch_get_main_queue(), ^{
+        [[NSNotificationCenter defaultCenter] postNotificationName:kReachabilityChangedNotification
+                                                            object:self];
+    });
+}
+
+#pragma mark - Debug Description
+
+- (NSString *) description
+{
+    NSString *description = [NSString stringWithFormat:@"<%@: %#x (%@)>",
+                             NSStringFromClass([self class]), (unsigned int) self, [self currentReachabilityFlags]];
+    return description;
+}
+
+@end

+ 18 - 2
MobileLibrary/iOS/build-psiphon-framework.sh

@@ -10,12 +10,16 @@ if [ $? -ne 0 ]; then
   exit 1
 fi
 
-OUTPUT_DIR=${BASE_DIR}/framework
-OUTPUT_FILE=Psi.framework
+VALID_ARCHS="arm64 armv7 armv7s"
+FRAMEWORK="Psi"
+OUTPUT_DIR="${BASE_DIR}/framework"
+OUTPUT_FILE="${FRAMEWORK}.framework"
+FRAMEWORK_BINARY="${OUTPUT_DIR}/${OUTPUT_FILE}/Versions/A/${FRAMEWORK}"
 
 LIBSSL=${BASE_DIR}/OpenSSL-for-iPhone/lib/libssl.a
 LIBCRYPTO=${BASE_DIR}/OpenSSL-for-iPhone/lib/libcrypto.a
 OPENSSL_INCLUDE=${BASE_DIR}/OpenSSL-for-iPhone/include/
+UMBRELLA_FRAMEWORK_XCODE_PROJECT=${BASE_DIR}/PsiphonTunnelController/PsiphonTunnelController.xcodeproj/
 
 # Not exporting this breaks go commands later if run via jenkins
 export GOPATH=${PWD}/go-ios-build
@@ -70,6 +74,8 @@ check_pinned_version
 if [ $? -ne 0 ]; then
     go get -u golang.org/x/mobile/cmd/gomobile
     cd ${GOPATH}/src/golang.org/x/mobile/cmd/gomobile
+    git checkout master 
+    git branch -d pinned
     git checkout -b pinned ${GOMOBILE_PINNED_REV}
     go build
     gomobile init -v
@@ -114,3 +120,13 @@ IOS_CGO_BUILD_FLAGS='// #cgo darwin CFLAGS: -I'"${OPENSSL_INCLUDE}"'\
 LC_ALL=C sed -i -- "s|// #cgo pkg-config: libssl|${IOS_CGO_BUILD_FLAGS}|" "${OPENSSL_SRC_DIR}/build.go"
 
 gomobile bind -v -target ios -ldflags="${LDFLAGS}" -o ${OUTPUT_DIR}/${OUTPUT_FILE} github.com/Psiphon-Labs/psiphon-tunnel-core/MobileLibrary/psi
+ARCHS="$(lipo -info "${FRAMEWORK_BINARY}" | rev | cut -d ':' -f1 | rev)"
+for ARCH in $ARCHS; do
+  if ! [[ "${VALID_ARCHS}" == *"$ARCH"* ]]; then
+    echo "Stripping ARCH ${ARCH} from ${FRAMEWORK_BINARY}"
+    lipo -remove "$ARCH" -output "$FRAMEWORK_BINARY" "$FRAMEWORK_BINARY" || exit 1
+  fi
+done
+
+xcodebuild clean build -configuration Release -sdk iphoneos -project ${UMBRELLA_FRAMEWORK_XCODE_PROJECT}
+echo "Done."