example/ivy/ios: add iOS source code
Change-Id: Ifabbfcfd75a92b0eb81631e3bb6eca1e4228139a
Reviewed-on: https://go-review.googlesource.com/14208
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
diff --git a/example/ivy/ios/README.md b/example/ivy/ios/README.md
new file mode 100644
index 0000000..02885a5
--- /dev/null
+++ b/example/ivy/ios/README.md
@@ -0,0 +1,14 @@
+# Ivy iOS App source
+
+This directory contains the source code to the Ivy iOS app.
+
+To build, first create the mobile.framework out of the Go
+implementation of Ivy. Run:
+
+```
+ go get robpike.io/ivy
+ gomobile bind -target=ios robpike.io/ivy/mobile
+```
+
+Place the mobile.framework directory in this directory, and
+then open ivy.xcodeproj in Xcode.
diff --git a/example/ivy/ios/ivy.xcodeproj/project.pbxproj b/example/ivy/ios/ivy.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..8e6d974
--- /dev/null
+++ b/example/ivy/ios/ivy.xcodeproj/project.pbxproj
@@ -0,0 +1,390 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ B461D25D1B31B27700EC4870 /* tape.html in Resources */ = {isa = PBXBuildFile; fileRef = B461D25C1B31B27700EC4870 /* tape.html */; };
+ B48878331B2E714100C7CC3C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B488780C1B2D1C3F00C7CC3C /* AppDelegate.m */; };
+ B48878341B2E714100C7CC3C /* IvyController.m in Sources */ = {isa = PBXBuildFile; fileRef = B488780F1B2D1C3F00C7CC3C /* IvyController.m */; };
+ B48878351B2E714100C7CC3C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B48878091B2D1C3F00C7CC3C /* main.m */; };
+ B48878371B2E714100C7CC3C /* Suggestion.m in Sources */ = {isa = PBXBuildFile; fileRef = B48878311B2DF1B200C7CC3C /* Suggestion.m */; };
+ B48878381B2E719000C7CC3C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B48878111B2D1C3F00C7CC3C /* Main.storyboard */; };
+ B48878391B2E719000C7CC3C /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = B48878141B2D1C3F00C7CC3C /* Images.xcassets */; };
+ B488783D1B2F9CD500C7CC3C /* DocsController.m in Sources */ = {isa = PBXBuildFile; fileRef = B488783C1B2F9CD500C7CC3C /* DocsController.m */; };
+ B4F12E871B3834120077D7AC /* Launch.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = B4F12E861B3834120077D7AC /* Launch.storyboard */; };
+ B4FB2A9A1B9890540087EE14 /* mobile.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B4C2833A1B98889100878964 /* mobile.framework */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ B488781E1B2D1C3F00C7CC3C /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = B48877FC1B2D1C3F00C7CC3C /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = B48878031B2D1C3F00C7CC3C;
+ remoteInfo = ivy;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXFileReference section */
+ B461D25C1B31B27700EC4870 /* tape.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = tape.html; sourceTree = "<group>"; };
+ B48878041B2D1C3F00C7CC3C /* ivy.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ivy.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ B48878081B2D1C3F00C7CC3C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+ B48878091B2D1C3F00C7CC3C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
+ B488780B1B2D1C3F00C7CC3C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
+ B488780C1B2D1C3F00C7CC3C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
+ B488780E1B2D1C3F00C7CC3C /* IvyController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IvyController.h; sourceTree = "<group>"; };
+ B488780F1B2D1C3F00C7CC3C /* IvyController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IvyController.m; sourceTree = "<group>"; };
+ B48878121B2D1C3F00C7CC3C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
+ B48878141B2D1C3F00C7CC3C /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = "<group>"; };
+ B48878301B2DF1B200C7CC3C /* Suggestion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Suggestion.h; sourceTree = "<group>"; };
+ B48878311B2DF1B200C7CC3C /* Suggestion.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Suggestion.m; sourceTree = "<group>"; };
+ B488783B1B2F9CD500C7CC3C /* DocsController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DocsController.h; sourceTree = "<group>"; };
+ B488783C1B2F9CD500C7CC3C /* DocsController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DocsController.m; sourceTree = "<group>"; };
+ B4C2833A1B98889100878964 /* mobile.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = mobile.framework; sourceTree = "<group>"; };
+ B4F12E861B3834120077D7AC /* Launch.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = Launch.storyboard; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ B48878011B2D1C3F00C7CC3C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B4FB2A9A1B9890540087EE14 /* mobile.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B488781A1B2D1C3F00C7CC3C /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ B48877FB1B2D1C3F00C7CC3C = {
+ isa = PBXGroup;
+ children = (
+ B4C2833A1B98889100878964 /* mobile.framework */,
+ B48878061B2D1C3F00C7CC3C /* ivy */,
+ B48878051B2D1C3F00C7CC3C /* Products */,
+ );
+ sourceTree = "<group>";
+ };
+ B48878051B2D1C3F00C7CC3C /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ B48878041B2D1C3F00C7CC3C /* ivy.app */,
+ );
+ name = Products;
+ sourceTree = "<group>";
+ };
+ B48878061B2D1C3F00C7CC3C /* ivy */ = {
+ isa = PBXGroup;
+ children = (
+ B488780B1B2D1C3F00C7CC3C /* AppDelegate.h */,
+ B488780C1B2D1C3F00C7CC3C /* AppDelegate.m */,
+ B488780E1B2D1C3F00C7CC3C /* IvyController.h */,
+ B488780F1B2D1C3F00C7CC3C /* IvyController.m */,
+ B488783B1B2F9CD500C7CC3C /* DocsController.h */,
+ B488783C1B2F9CD500C7CC3C /* DocsController.m */,
+ B48878301B2DF1B200C7CC3C /* Suggestion.h */,
+ B48878311B2DF1B200C7CC3C /* Suggestion.m */,
+ B461D25C1B31B27700EC4870 /* tape.html */,
+ B48878111B2D1C3F00C7CC3C /* Main.storyboard */,
+ B4F12E861B3834120077D7AC /* Launch.storyboard */,
+ B48878141B2D1C3F00C7CC3C /* Images.xcassets */,
+ B48878071B2D1C3F00C7CC3C /* Supporting Files */,
+ );
+ path = ivy;
+ sourceTree = "<group>";
+ };
+ B48878071B2D1C3F00C7CC3C /* Supporting Files */ = {
+ isa = PBXGroup;
+ children = (
+ B48878081B2D1C3F00C7CC3C /* Info.plist */,
+ B48878091B2D1C3F00C7CC3C /* main.m */,
+ );
+ name = "Supporting Files";
+ sourceTree = "<group>";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ B48878031B2D1C3F00C7CC3C /* ivy */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = B48878271B2D1C3F00C7CC3C /* Build configuration list for PBXNativeTarget "ivy" */;
+ buildPhases = (
+ B48878001B2D1C3F00C7CC3C /* Sources */,
+ B48878011B2D1C3F00C7CC3C /* Frameworks */,
+ B48878021B2D1C3F00C7CC3C /* Resources */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = ivy;
+ productName = ivy;
+ productReference = B48878041B2D1C3F00C7CC3C /* ivy.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ B48877FC1B2D1C3F00C7CC3C /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 0640;
+ ORGANIZATIONNAME = "The Go Authors";
+ TargetAttributes = {
+ B48878031B2D1C3F00C7CC3C = {
+ CreatedOnToolsVersion = 6.1.1;
+ DevelopmentTeam = YE84DJ86AZ;
+ };
+ B488781C1B2D1C3F00C7CC3C = {
+ CreatedOnToolsVersion = 6.1.1;
+ TestTargetID = B48878031B2D1C3F00C7CC3C;
+ };
+ };
+ };
+ buildConfigurationList = B48877FF1B2D1C3F00C7CC3C /* Build configuration list for PBXProject "ivy" */;
+ compatibilityVersion = "Xcode 3.2";
+ developmentRegion = English;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = B48877FB1B2D1C3F00C7CC3C;
+ productRefGroup = B48878051B2D1C3F00C7CC3C /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ B48878031B2D1C3F00C7CC3C /* ivy */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ B48878021B2D1C3F00C7CC3C /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B48878381B2E719000C7CC3C /* Main.storyboard in Resources */,
+ B4F12E871B3834120077D7AC /* Launch.storyboard in Resources */,
+ B48878391B2E719000C7CC3C /* Images.xcassets in Resources */,
+ B461D25D1B31B27700EC4870 /* tape.html in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B488781B1B2D1C3F00C7CC3C /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ B48878001B2D1C3F00C7CC3C /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ B48878331B2E714100C7CC3C /* AppDelegate.m in Sources */,
+ B488783D1B2F9CD500C7CC3C /* DocsController.m in Sources */,
+ B48878341B2E714100C7CC3C /* IvyController.m in Sources */,
+ B48878351B2E714100C7CC3C /* main.m in Sources */,
+ B48878371B2E714100C7CC3C /* Suggestion.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+ B48878191B2D1C3F00C7CC3C /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ B488781F1B2D1C3F00C7CC3C /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = B48878031B2D1C3F00C7CC3C /* ivy */;
+ targetProxy = B488781E1B2D1C3F00C7CC3C /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ B48878111B2D1C3F00C7CC3C /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ B48878121B2D1C3F00C7CC3C /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "<group>";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ B48878251B2D1C3F00C7CC3C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = arm64;
+ 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_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_SYMBOLS_PRIVATE_EXTERN = NO;
+ 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 = 8.1;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "arm64 x86_64 armv7";
+ };
+ name = Debug;
+ };
+ B48878261B2D1C3F00C7CC3C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ ARCHS = "$(ARCHS_STANDARD)";
+ 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_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = YES;
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ 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 = 8.1;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ VALID_ARCHS = "arm64 x86_64 armv7";
+ };
+ name = Release;
+ };
+ B48878281B2D1C3F00C7CC3C /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = ivy/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = ivy;
+ STRIP_STYLE = debugging;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "arm64 armv7";
+ };
+ name = Debug;
+ };
+ B48878291B2D1C3F00C7CC3C /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CODE_SIGN_IDENTITY = "iPhone Developer";
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ INFOPLIST_FILE = ivy/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 8.0;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ LIBRARY_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)",
+ );
+ ONLY_ACTIVE_ARCH = NO;
+ PRODUCT_NAME = ivy;
+ STRIP_STYLE = debugging;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALID_ARCHS = "arm64 armv7";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ B48877FF1B2D1C3F00C7CC3C /* Build configuration list for PBXProject "ivy" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B48878251B2D1C3F00C7CC3C /* Debug */,
+ B48878261B2D1C3F00C7CC3C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ B48878271B2D1C3F00C7CC3C /* Build configuration list for PBXNativeTarget "ivy" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ B48878281B2D1C3F00C7CC3C /* Debug */,
+ B48878291B2D1C3F00C7CC3C /* Release */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = B48877FC1B2D1C3F00C7CC3C /* Project object */;
+}
diff --git a/example/ivy/ios/ivy/AppDelegate.h b/example/ivy/ios/ivy/AppDelegate.h
new file mode 100644
index 0000000..0585c7d
--- /dev/null
+++ b/example/ivy/ios/ivy/AppDelegate.h
@@ -0,0 +1,14 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+#import "Suggestion.h"
+#import "IvyController.h"
+
+@interface AppDelegate
+ : UIResponder <UIApplicationDelegate, UITextFieldDelegate, UIWebViewDelegate>
+
+@property (strong, nonatomic) UIWindow *window;
+
+@end
diff --git a/example/ivy/ios/ivy/AppDelegate.m b/example/ivy/ios/ivy/AppDelegate.m
new file mode 100644
index 0000000..41221c6
--- /dev/null
+++ b/example/ivy/ios/ivy/AppDelegate.m
@@ -0,0 +1,40 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "AppDelegate.h"
+#import "IvyController.h"
+
+@interface AppDelegate ()
+
+@end
+
+@implementation AppDelegate
+
+- (BOOL)application:(UIApplication *)application
+ didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
+{
+ return YES;
+}
+
+- (void)applicationWillResignActive:(UIApplication *)application
+{
+}
+
+- (void)applicationDidEnterBackground:(UIApplication *)application
+{
+}
+
+- (void)applicationWillEnterForeground:(UIApplication *)application
+{
+}
+
+- (void)applicationDidBecomeActive:(UIApplication *)application
+{
+}
+
+- (void)applicationWillTerminate:(UIApplication *)application
+{
+}
+
+@end
diff --git a/example/ivy/ios/ivy/Base.lproj/Main.storyboard b/example/ivy/ios/ivy/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..b773a75
--- /dev/null
+++ b/example/ivy/ios/ivy/Base.lproj/Main.storyboard
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="mTw-C8-NzX">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
+ </dependencies>
+ <scenes>
+ <!--Docs-->
+ <scene sceneID="qSe-m0-5Rh">
+ <objects>
+ <viewController title="Docs" id="rfr-rm-AXI" customClass="DocsController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="TeY-hL-zeC"/>
+ <viewControllerLayoutGuide type="bottom" id="Yrx-qe-pYd"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="a4n-1z-obZ">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <webView opaque="NO" tag="11" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="fk4-q0-6a2">
+ <rect key="frame" x="16" y="64" width="568" height="528"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ <dataDetectorType key="dataDetectorTypes"/>
+ </webView>
+ </subviews>
+ <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="fk4-q0-6a2" firstAttribute="top" secondItem="a4n-1z-obZ" secondAttribute="top" constant="64" id="NP8-Ie-n5Y"/>
+ <constraint firstAttribute="trailing" secondItem="fk4-q0-6a2" secondAttribute="trailing" constant="16" id="e1b-5n-MOa"/>
+ <constraint firstAttribute="bottom" secondItem="fk4-q0-6a2" secondAttribute="bottom" constant="8" id="fAM-W8-Lfc"/>
+ <constraint firstItem="fk4-q0-6a2" firstAttribute="leading" secondItem="a4n-1z-obZ" secondAttribute="leading" constant="16" id="fpe-JV-je0"/>
+ </constraints>
+ </view>
+ <navigationItem key="navigationItem" title="Documentation" id="lf7-H3-aZF"/>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="HMz-gF-Hp5" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="1725" y="386"/>
+ </scene>
+ <!--Ivy Controller-->
+ <scene sceneID="tne-QT-ifu">
+ <objects>
+ <viewController title="Ivy Controller" id="BYZ-38-t0r" customClass="IvyController" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
+ <viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleAspectFit" id="8bC-Xf-vdC">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="9SS-TP-C7c" userLabel="InputField">
+ <rect key="frame" x="16" y="538" width="568" height="30"/>
+ <color key="backgroundColor" red="0.98823529409999999" green="0.98039215690000003" blue="0.81176470590000005" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <rect key="contentStretch" x="1" y="1" width="1" height="1"/>
+ <fontDescription key="fontDescription" name="Menlo-Bold" family="Menlo" pointSize="14"/>
+ <textInputTraits key="textInputTraits" autocorrectionType="no" spellCheckingType="no" keyboardType="alphabet" keyboardAppearance="alert" enablesReturnKeyAutomatically="YES"/>
+ </textField>
+ <webView opaque="NO" tag="2" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="Tgh-Lc-vbE">
+ <rect key="frame" x="16" y="64" width="568" height="466"/>
+ <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
+ <dataDetectorType key="dataDetectorTypes"/>
+ </webView>
+ </subviews>
+ <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstItem="9SS-TP-C7c" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="16" id="L0M-Fx-f5N"/>
+ <constraint firstAttribute="bottom" secondItem="9SS-TP-C7c" secondAttribute="bottom" constant="32" id="Xn1-j6-As3"/>
+ <constraint firstItem="Tgh-Lc-vbE" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leading" constant="16" id="aoL-2Z-ve3"/>
+ <constraint firstAttribute="trailing" secondItem="Tgh-Lc-vbE" secondAttribute="trailing" constant="16" id="fY6-BA-NIh"/>
+ <constraint firstItem="Tgh-Lc-vbE" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="top" constant="64" id="jmc-WH-cl4"/>
+ <constraint firstItem="Tgh-Lc-vbE" firstAttribute="width" secondItem="9SS-TP-C7c" secondAttribute="width" id="vR1-iG-UWH"/>
+ <constraint firstItem="9SS-TP-C7c" firstAttribute="top" secondItem="Tgh-Lc-vbE" secondAttribute="bottom" constant="8" id="xgU-d0-9TL"/>
+ </constraints>
+ </view>
+ <navigationItem key="navigationItem" title="Ivy" id="KhW-J4-UcU">
+ <barButtonItem key="rightBarButtonItem" title="Help" id="GSn-BW-al6">
+ <connections>
+ <segue destination="rfr-rm-AXI" kind="show" id="bNF-6S-rOa"/>
+ </connections>
+ </barButtonItem>
+ </navigationItem>
+ <connections>
+ <outlet property="bottomConstraint" destination="Xn1-j6-As3" id="EKm-WV-Y1w"/>
+ </connections>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="1069" y="386"/>
+ </scene>
+ <!--Navigation Controller-->
+ <scene sceneID="ZgV-45-Pf8">
+ <objects>
+ <navigationController automaticallyAdjustsScrollViewInsets="NO" id="mTw-C8-NzX" sceneMemberID="viewController">
+ <toolbarItems/>
+ <navigationBar key="navigationBar" opaque="NO" contentMode="scaleToFill" barStyle="black" id="aev-Tm-XK3">
+ <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ <color key="backgroundColor" red="1" green="1" blue="0.80392156859999997" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <color key="tintColor" cocoaTouchSystemColor="lightTextColor"/>
+ </navigationBar>
+ <nil name="viewControllers"/>
+ <connections>
+ <segue destination="BYZ-38-t0r" kind="relationship" relationship="rootViewController" id="Nyy-d4-AXi"/>
+ </connections>
+ </navigationController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="i1l-Sg-DC5" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="305" y="-371"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/example/ivy/ios/ivy/DocsController.h b/example/ivy/ios/ivy/DocsController.h
new file mode 100644
index 0000000..c29682e
--- /dev/null
+++ b/example/ivy/ios/ivy/DocsController.h
@@ -0,0 +1,10 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+// DocsController displays the documentation page.
+@interface DocsController : UIViewController
+
+@end
diff --git a/example/ivy/ios/ivy/DocsController.m b/example/ivy/ios/ivy/DocsController.m
new file mode 100644
index 0000000..49525ad
--- /dev/null
+++ b/example/ivy/ios/ivy/DocsController.m
@@ -0,0 +1,31 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "DocsController.h"
+#import "mobile/Mobile.h"
+
+@interface DocsController ()
+
+@end
+
+@implementation DocsController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+ UIWebView *webView = (UIWebView *)[self.view viewWithTag:11];
+ NSString *helpHTML = GoMobileHelp();
+ [webView loadHTMLString:helpHTML baseURL:NULL];
+ if ([self respondsToSelector:@selector(
+ setAutomaticallyAdjustsScrollViewInsets:)]) {
+ self.automaticallyAdjustsScrollViewInsets = NO;
+ }
+}
+
+- (void)didReceiveMemoryWarning
+{
+ [super didReceiveMemoryWarning];
+}
+
+@end
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000..295161a
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,74 @@
+{
+ "images" : [
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "iphone",
+ "size" : "29x29",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "ivy-ios-80.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "apple-touch-icon-120x120-1.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "apple-touch-icon-120x120.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "ivy-ios-180.png",
+ "scale" : "3x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "29x29",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "ipad",
+ "size" : "40x40",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "apple-touch-icon-76x76.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "apple-touch-icon-152x152.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
\ No newline at end of file
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120-1.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120-1.png
new file mode 100644
index 0000000..f3db5c3
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120-1.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120.png
new file mode 100644
index 0000000..f3db5c3
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-120x120.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-152x152.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-152x152.png
new file mode 100644
index 0000000..0a495d3
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-152x152.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-76x76.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-76x76.png
new file mode 100644
index 0000000..b0be2a7
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/apple-touch-icon-76x76.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-180.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-180.png
new file mode 100644
index 0000000..2c996eb
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-180.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-80.png b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-80.png
new file mode 100644
index 0000000..1e3c7c1
--- /dev/null
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/ivy-ios-80.png
Binary files differ
diff --git a/example/ivy/ios/ivy/Info.plist b/example/ivy/ios/ivy/Info.plist
new file mode 100644
index 0000000..559e2b5
--- /dev/null
+++ b/example/ivy/ios/ivy/Info.plist
@@ -0,0 +1,46 @@
+<?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>CFBundleDisplayName</key>
+ <string>Ivy</string>
+ <key>CFBundleExecutable</key>
+ <string>$(EXECUTABLE_NAME)</string>
+ <key>CFBundleIdentifier</key>
+ <string>com.google.$(PRODUCT_NAME:rfc1034identifier)</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundleName</key>
+ <string>$(PRODUCT_NAME)</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleShortVersionString</key>
+ <string>1.0</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>6</string>
+ <key>LSRequiresIPhoneOS</key>
+ <true/>
+ <key>UILaunchStoryboardName</key>
+ <string>Launch</string>
+ <key>UIMainStoryboardFile</key>
+ <string>Main</string>
+ <key>UIRequiredDeviceCapabilities</key>
+ <array/>
+ <key>UISupportedInterfaceOrientations</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ <string>UIInterfaceOrientationLandscapeLeft</string>
+ <string>UIInterfaceOrientationLandscapeRight</string>
+ </array>
+ <key>UISupportedInterfaceOrientations~ipad</key>
+ <array>
+ <string>UIInterfaceOrientationPortrait</string>
+ <string>UIInterfaceOrientationPortraitUpsideDown</string>
+ </array>
+</dict>
+</plist>
diff --git a/example/ivy/ios/ivy/IvyController.h b/example/ivy/ios/ivy/IvyController.h
new file mode 100644
index 0000000..3bca62a
--- /dev/null
+++ b/example/ivy/ios/ivy/IvyController.h
@@ -0,0 +1,20 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+#import "Suggestion.h"
+
+// IvyController displays the main app view.
+@interface IvyController
+ : UIViewController <UITextFieldDelegate, UIWebViewDelegate,
+ SuggestionDelegate>
+
+@property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
+
+// A text input field coupled to an output "tape", rendered with a UIWebView.
+@property (strong, nonatomic) UITextField *input;
+@property (strong, nonatomic) Suggestion *suggestionView;
+@property (strong, nonatomic) UIWebView *tape;
+
+@end
diff --git a/example/ivy/ios/ivy/IvyController.m b/example/ivy/ios/ivy/IvyController.m
new file mode 100644
index 0000000..971a872
--- /dev/null
+++ b/example/ivy/ios/ivy/IvyController.m
@@ -0,0 +1,179 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "IvyController.h"
+#import "mobile/Mobile.h"
+
+@interface IvyController ()
+
+@end
+
+@implementation IvyController
+
+- (void)viewDidLoad
+{
+ [super viewDidLoad];
+
+ self.input = (UITextField *)[self.view viewWithTag:1];
+ self.input.delegate = self;
+ self.input.autocorrectionType = UITextAutocorrectionTypeNo;
+ self.input.keyboardType = UIKeyboardTypeNumbersAndPunctuation;
+
+ self.suggestionView = [[Suggestion alloc] init];
+ self.suggestionView.delegate = self;
+
+ self.tape = (UIWebView *)[self.view viewWithTag:2];
+ self.tape.delegate = self;
+
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(textDidChange:)
+ name:UITextFieldTextDidChangeNotification
+ object:self.input];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardWillShow:)
+ name:UIKeyboardWillShowNotification
+ object:nil];
+ [[NSNotificationCenter defaultCenter]
+ addObserver:self
+ selector:@selector(keyboardWillHide:)
+ name:UIKeyboardWillHideNotification
+ object:nil];
+
+ NSURL *bundleURL =
+ [[NSBundle mainBundle] URLForResource:@"tape" withExtension:@"html"];
+ NSURLRequest *request = [NSURLRequest requestWithURL:bundleURL];
+ [self.tape loadRequest:request];
+ self.tape.delegate = self;
+ [self.input becomeFirstResponder];
+}
+
+- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
+{
+ if ([textField isEqual:self.input]) {
+ textField.inputAccessoryView = self.suggestionView;
+ textField.autocorrectionType = UITextAutocorrectionTypeNo;
+ [textField reloadInputViews];
+ }
+ return YES;
+}
+
+- (BOOL)textFieldShouldEndEditing:(UITextField *)textField
+{
+ if ([textField isEqual:self.input]) {
+ textField.inputAccessoryView = nil;
+ [textField reloadInputViews];
+ }
+ return YES;
+}
+
+- (BOOL)textField:(UITextField *)textField
+ shouldChangeCharactersInRange:(NSRange)range
+ replacementString:(NSString *)str
+{
+ if ([str isEqualToString:@"\n"]) {
+ [self
+ appendTape:[NSString stringWithFormat:@"<b>%@</b>", [self.input text]]];
+ NSString *expr = [self.input.text stringByAppendingString:@"\n"];
+ NSString *result;
+ NSError *err = [NSError alloc];
+ if (!GoMobileEval(expr, &result, &err)) {
+ result = err.description;
+ }
+ result = [result
+ stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+ result =
+ [result stringByReplacingOccurrencesOfString:@"<" withString:@"<"];
+ result =
+ [result stringByReplacingOccurrencesOfString:@">" withString:@">"];
+ NSMutableArray *lines =
+ (NSMutableArray *)[result componentsSeparatedByString:@"\n"];
+ for (NSMutableString *line in lines) {
+ [self appendTape:line];
+ }
+ self.input.text = @"";
+ return NO;
+ }
+
+ return YES;
+}
+
+- (void)textDidChange:(NSNotification *)notif
+{
+ [self.suggestionView suggestFor:self.input.text];
+}
+
+- (void)suggestionReplace:(NSString *)text
+{
+ self.input.text = text;
+ [self.suggestionView suggestFor:text];
+}
+
+- (void)keyboardWillShow:(NSNotification *)aNotification
+{
+ // Move the input text field up, as the keyboard has taken some of the screen.
+ NSDictionary *info = [aNotification userInfo];
+ CGRect kbFrame =
+ [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
+ NSNumber *duration =
+ [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
+
+ UIViewAnimationCurve keyboardTransitionAnimationCurve;
+ [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
+ getValue:&keyboardTransitionAnimationCurve];
+ UIViewAnimationOptions options =
+ keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
+
+ [UIView animateWithDuration:duration.floatValue
+ delay:0
+ options:options
+ animations:^{
+ self.bottomConstraint.constant = kbFrame.size.height + 32;
+ [self.view layoutIfNeeded];
+ }
+ completion:^(BOOL finished) {
+ [self scrollTapeToBottom];
+ }];
+}
+
+- (void)keyboardWillHide:(NSNotification *)aNotification
+{
+ // Move the input text field back down.
+ NSDictionary *info = [aNotification userInfo];
+ NSNumber *duration =
+ [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
+
+ UIViewAnimationCurve keyboardTransitionAnimationCurve;
+ [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
+ getValue:&keyboardTransitionAnimationCurve];
+ UIViewAnimationOptions options =
+ keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
+
+ [UIView animateWithDuration:duration.floatValue
+ delay:0
+ options:options
+ animations:^{
+ self.bottomConstraint.constant = 32;
+ [self.view layoutIfNeeded];
+ }
+ completion:^(BOOL finished) {
+ [self scrollTapeToBottom];
+ }];
+}
+
+- (void)scrollTapeToBottom
+{
+ NSString *scroll = @"window.scrollBy(0, document.body.offsetHeight);";
+ [self.tape stringByEvaluatingJavaScriptFromString:scroll];
+}
+
+- (void)appendTape:(NSString *)text
+{
+ NSString *injectSrc = @"appendDiv('%@');";
+ NSString *runToInject = [NSString stringWithFormat:injectSrc, text];
+ [self.tape stringByEvaluatingJavaScriptFromString:runToInject];
+}
+
+@end
diff --git a/example/ivy/ios/ivy/Launch.storyboard b/example/ivy/ios/ivy/Launch.storyboard
new file mode 100644
index 0000000..10ce963
--- /dev/null
+++ b/example/ivy/ios/ivy/Launch.storyboard
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="7702" systemVersion="14D136" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="Je6-Vd-UK0">
+ <dependencies>
+ <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="7701"/>
+ </dependencies>
+ <scenes>
+ <!--Ivy-->
+ <scene sceneID="Cg9-fz-KQ0">
+ <objects>
+ <viewController id="q4c-m3-eMP" sceneMemberID="viewController">
+ <layoutGuides>
+ <viewControllerLayoutGuide type="top" id="aQh-FK-ySS"/>
+ <viewControllerLayoutGuide type="bottom" id="g6W-0l-hXo"/>
+ </layoutGuides>
+ <view key="view" contentMode="scaleToFill" id="EGD-xr-ybO">
+ <rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
+ <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
+ <subviews>
+ <textField opaque="NO" clipsSubviews="YES" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" placeholder="Type an expression" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="8Hr-jh-68p">
+ <rect key="frame" x="16" y="538" width="568" height="30"/>
+ <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <fontDescription key="fontDescription" name="Menlo-Regular" family="Menlo" pointSize="14"/>
+ <textInputTraits key="textInputTraits"/>
+ </textField>
+ </subviews>
+ <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
+ <constraints>
+ <constraint firstAttribute="bottom" secondItem="8Hr-jh-68p" secondAttribute="bottom" constant="32" id="9Iw-uB-UB9"/>
+ <constraint firstAttribute="trailing" secondItem="8Hr-jh-68p" secondAttribute="trailing" constant="16" id="OJq-r1-Z32"/>
+ <constraint firstItem="8Hr-jh-68p" firstAttribute="leading" secondItem="EGD-xr-ybO" secondAttribute="leading" constant="16" id="SZn-Fs-u3S"/>
+ </constraints>
+ </view>
+ <navigationItem key="navigationItem" title="Ivy" id="r1v-ZR-dJA"/>
+ </viewController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="jy3-Sq-DLe" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="1281" y="-205"/>
+ </scene>
+ <!--Navigation Controller-->
+ <scene sceneID="fyF-Os-Uj2">
+ <objects>
+ <navigationController automaticallyAdjustsScrollViewInsets="NO" id="Je6-Vd-UK0" sceneMemberID="viewController">
+ <toolbarItems/>
+ <navigationBar key="navigationBar" contentMode="scaleToFill" barStyle="black" id="W8Y-Y5-bE1">
+ <rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
+ <autoresizingMask key="autoresizingMask"/>
+ </navigationBar>
+ <nil name="viewControllers"/>
+ <connections>
+ <segue destination="q4c-m3-eMP" kind="relationship" relationship="rootViewController" id="R2u-45-ffm"/>
+ </connections>
+ </navigationController>
+ <placeholder placeholderIdentifier="IBFirstResponder" id="r6I-II-OCI" userLabel="First Responder" sceneMemberID="firstResponder"/>
+ </objects>
+ <point key="canvasLocation" x="469" y="-205"/>
+ </scene>
+ </scenes>
+</document>
diff --git a/example/ivy/ios/ivy/Suggestion.h b/example/ivy/ios/ivy/Suggestion.h
new file mode 100644
index 0000000..fa1efab
--- /dev/null
+++ b/example/ivy/ios/ivy/Suggestion.h
@@ -0,0 +1,21 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import <UIKit/UIKit.h>
+
+@protocol SuggestionDelegate <NSObject>
+
+@required
+- (void)suggestionReplace:(NSString *)text;
+@end
+
+@interface Suggestion : UIInputView
+
+- (instancetype)init;
+- (instancetype)initWithFrame:(CGRect)frame;
+- (void)suggestFor:(NSString *)text;
+
+@property (weak) id<SuggestionDelegate> delegate;
+
+@end
diff --git a/example/ivy/ios/ivy/Suggestion.m b/example/ivy/ios/ivy/Suggestion.m
new file mode 100644
index 0000000..509d587
--- /dev/null
+++ b/example/ivy/ios/ivy/Suggestion.m
@@ -0,0 +1,198 @@
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#import "Suggestion.h"
+
+#define maxSuggestions 4 + 3
+
+@implementation Suggestion {
+ NSString *text;
+ NSRange range;
+
+ NSMutableOrderedSet *options;
+ NSMutableArray *buttons;
+ NSArray *possibleSuggestions;
+ NSCharacterSet *breakingChars;
+}
+
+- (instancetype)init
+{
+ CGFloat screenWidth = [UIScreen mainScreen].bounds.size.width;
+ self = [self initWithFrame:CGRectMake(0.0f, 0.0f, screenWidth, 36.0f)];
+ return self;
+}
+
+- (instancetype)initWithFrame:(CGRect)frame
+{
+ self = [super initWithFrame:frame inputViewStyle:UIInputViewStyleKeyboard];
+ if (self) {
+ possibleSuggestions = @[
+ @")base ",
+ @")debug ",
+ @")format ",
+ @")maxdigits ",
+ @")op ",
+ @")origin ",
+ @")prec ",
+ @")prompt ",
+ @")seed ",
+ @"cos ",
+ @"iota ",
+ @"log ",
+ @"max ",
+ @"min ",
+ @"pi ",
+ @"rho ",
+ @"sin ",
+ @"sqrt ",
+ @"tan "
+ ];
+ breakingChars =
+ [NSCharacterSet characterSetWithCharactersInString:@"/+-*,^|= "];
+ options = [[NSMutableOrderedSet alloc] initWithCapacity:maxSuggestions];
+ buttons = [[NSMutableArray alloc] init];
+ self.backgroundColor = [UIColor colorWithWhite:0.0f alpha:0.05f];
+ [self setSuggestions:nil];
+ }
+ return self;
+}
+
+- (void)suggestFor:(NSString *)t
+{
+ text = t;
+ range =
+ [text rangeOfCharacterFromSet:breakingChars options:NSBackwardsSearch];
+ if (range.location == NSNotFound) {
+ range.location = 0;
+ range.length = text.length;
+ } else {
+ if (range.location > 0 &&
+ [text characterAtIndex:range.location - 1] == ')') {
+ // Special case for suggestions that start with ") ".
+ range.location -= 1;
+ range.length++;
+ } else {
+ range.location += 1;
+ range.length -= 0;
+ }
+ }
+ range.length = text.length - range.location;
+ if (range.length == 0) {
+ [self setSuggestions:nil];
+ } else {
+ NSString *prefix = [text substringWithRange:range];
+ // TODO: make not so slow.
+ NSArray *suggestions = @[];
+ for (NSString *suggestion in possibleSuggestions) {
+ if ([suggestion hasPrefix:prefix] && prefix.length < suggestion.length) {
+ suggestions = [suggestions arrayByAddingObject:suggestion];
+ }
+ }
+ if (suggestions.count > 3) {
+ suggestions = nil;
+ }
+ [self setSuggestions:suggestions];
+ }
+ [self setNeedsLayout];
+}
+
+- (void)setSuggestions:(NSArray *)suggestions
+{
+ [options removeAllObjects];
+
+ if ([suggestions respondsToSelector:
+ @selector(countByEnumeratingWithState:objects:count:)]) {
+ for (NSString *suggestion in suggestions) {
+ if (options.count < maxSuggestions) {
+ [options addObject:suggestion];
+ } else {
+ break;
+ }
+ }
+ }
+}
+
+- (void)layoutSubview:(NSString *)t at:(CGFloat)x width:(CGFloat)w
+{
+ UIButton *b = [[UIButton alloc]
+ initWithFrame:CGRectMake(x, 0.0f, w, self.bounds.size.height)];
+ [b setTitle:t forState:UIControlStateNormal];
+ b.titleLabel.adjustsFontSizeToFitWidth = YES;
+ b.titleLabel.textAlignment = NSTextAlignmentCenter;
+ [b setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
+ [b addTarget:self
+ action:@selector(buttonTouched:)
+ forControlEvents:UIControlEventTouchUpInside];
+ [self addSubview:b];
+
+ if (x > 0) {
+ UIView *line = [[UIView alloc]
+ initWithFrame:CGRectMake(0.0f, 0.0f, 0.5f, self.bounds.size.height)];
+ line.backgroundColor =
+ [UIColor colorWithRed:0.984 green:0.977 blue:0.81 alpha:1.0];
+ [b addSubview:line];
+ }
+
+ [buttons addObject:b];
+}
+
+- (void)layoutSubviews
+{
+ for (UIView *subview in buttons) {
+ [subview removeFromSuperview];
+ }
+ [buttons removeAllObjects];
+
+ CGFloat symbolWidth = 40.0f;
+ [self layoutSubview:@"+" at:0 * symbolWidth width:symbolWidth];
+ [self layoutSubview:@"-" at:1 * symbolWidth width:symbolWidth];
+ [self layoutSubview:@"*" at:2 * symbolWidth width:symbolWidth];
+ [self layoutSubview:@"/" at:3 * symbolWidth width:symbolWidth];
+
+ for (int i = 0; i < options.count; i++) {
+ NSString *suggestion = options[i];
+ CGFloat width =
+ (self.bounds.size.width - (4 * symbolWidth)) / options.count;
+ CGFloat x = (4 * symbolWidth) + (i * width);
+ [self layoutSubview:suggestion at:x width:width];
+ }
+}
+
+- (void)buttonTouched:(UIButton *)button
+{
+ NSTimeInterval duration = 0.08f;
+ [UIView
+ animateWithDuration:duration
+ animations:^{
+ [button setBackgroundColor:[UIColor whiteColor]];
+
+ if ([self.delegate
+ respondsToSelector:@selector(suggestionReplace:)]) {
+ NSString *t = text;
+ if (t == nil) {
+ t = @"";
+ }
+ if (button.currentTitle.length == 1) {
+ // Special case for +, -, *, /.
+ t = [t stringByAppendingString:button.currentTitle];
+ } else {
+ t = [text stringByReplacingCharactersInRange:
+ range withString:button.currentTitle];
+ }
+ [self performSelector:@selector(suggestionReplace:)
+ withObject:t
+ afterDelay:duration * 0.8f];
+ }
+ [button performSelector:@selector(setBackgroundColor:)
+ withObject:[UIColor clearColor]
+ afterDelay:duration];
+ }];
+}
+
+- (void)suggestionReplace:(NSString *)t
+{
+ [self.delegate performSelector:@selector(suggestionReplace:) withObject:t];
+}
+
+@end
diff --git a/example/ivy/ios/ivy/main.m b/example/ivy/ios/ivy/main.m
new file mode 100644
index 0000000..44798de
--- /dev/null
+++ b/example/ivy/ios/ivy/main.m
@@ -0,0 +1,11 @@
+#import <UIKit/UIKit.h>
+#import "AppDelegate.h"
+
+int main(int argc, char *argv[])
+{
+ @autoreleasepool
+ {
+ return UIApplicationMain(argc, argv, nil,
+ NSStringFromClass([AppDelegate class]));
+ }
+}
diff --git a/example/ivy/ios/ivy/tape.html b/example/ivy/ios/ivy/tape.html
new file mode 100644
index 0000000..67369b7
--- /dev/null
+++ b/example/ivy/ios/ivy/tape.html
@@ -0,0 +1,44 @@
+<!--
+Copyright 2015 The Go Authors. All rights reserved.
+Use of this source code is governed by a BSD-style
+license that can be found in the LICENSE file.
+-->
+<html>
+<head>
+<style>
+body {
+ padding: 0;
+ margin: 0;
+ font-family: Menlo, monospace;
+}
+
+.flowhide {
+ text-overflow: ellipsis;
+ word-break: break-all;
+ white-space: nowrap;
+ overflow: hidden;
+}
+
+.flowshow {
+ word-break: break-all;
+}
+</style>
+<script>
+function flowclick(el) {
+ el.classList.toggle("flowhide");
+ el.classList.toggle("flowshow");
+}
+function appendDiv(text) {
+ var el = document.createElement("div");
+ el.classList.add("flowhide");
+ el.innerHTML = text;
+ el.onclick = function() {
+ flowclick(el);
+ };
+ document.body.appendChild(el);
+ window.scrollBy(0, document.body.offsetHeight);
+}
+</script>
+<body>
+</body>
+</html>