example/ivy/ios: enable demo functionality

And bring iOS app up to Android capabilities.

Fixes golang/go#48694

Change-Id: I0e853cab102e34053ecdb17edb99644aa2f8651a
Reviewed-on: https://go-review.googlesource.com/c/mobile/+/357977
Reviewed-by: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Hyang-Ah Hana Kim <hyangah@gmail.com>
Trust: Peter Weinberger <pjw@google.com>
diff --git a/example/ivy/ios/README.md b/example/ivy/ios/README.md
index 453ccbc..bfbaa19 100644
--- a/example/ivy/ios/README.md
+++ b/example/ivy/ios/README.md
@@ -17,7 +17,7 @@
 go mod init work
 go get -d golang.org/x/mobile/bind@latest
 go get -d robpike.io/ivy/mobile
-gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile
+gomobile bind -target=ios,iossimulator,maccatalyst,macos robpike.io/ivy/mobile robpike.io/ivy/demo
 ```
 
 Place the Mobile.xcframework directory in this directory, and
diff --git a/example/ivy/ios/ivy.xcodeproj/project.pbxproj b/example/ivy/ios/ivy.xcodeproj/project.pbxproj
index aeb1d30..f183085 100644
--- a/example/ivy/ios/ivy.xcodeproj/project.pbxproj
+++ b/example/ivy/ios/ivy.xcodeproj/project.pbxproj
@@ -3,7 +3,7 @@
 	archiveVersion = 1;
 	classes = {
 	};
-	objectVersion = 46;
+	objectVersion = 52;
 	objects = {
 
 /* Begin PBXBuildFile section */
@@ -316,12 +316,16 @@
 				);
 				INFOPLIST_FILE = ivy/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
 				);
 				ONLY_ACTIVE_ARCH = NO;
+				PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
 				PRODUCT_NAME = ivy;
 				STRIP_STYLE = debugging;
 				SUPPORTS_MACCATALYST = YES;
@@ -342,13 +346,16 @@
 				);
 				INFOPLIST_FILE = ivy/Info.plist;
 				IPHONEOS_DEPLOYMENT_TARGET = 14.0;
-				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
 				LIBRARY_SEARCH_PATHS = (
 					"$(inherited)",
 					"$(PROJECT_DIR)",
 				);
 				ONLY_ACTIVE_ARCH = NO;
-				PRODUCT_BUNDLE_IDENTIFIER = "com.google.$(PRODUCT_NAME:rfc1034identifier)";
+				PRODUCT_BUNDLE_IDENTIFIER = "com.google.${PRODUCT_NAME:rfc1034identifier}";
 				PRODUCT_NAME = ivy;
 				STRIP_STYLE = debugging;
 				SUPPORTS_MACCATALYST = YES;
diff --git a/example/ivy/ios/ivy/Base.lproj/Main.storyboard b/example/ivy/ios/ivy/Base.lproj/Main.storyboard
index db250c1..18a65a9 100644
--- a/example/ivy/ios/ivy/Base.lproj/Main.storyboard
+++ b/example/ivy/ios/ivy/Base.lproj/Main.storyboard
@@ -1,10 +1,10 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19158" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX">
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="19162" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="mTw-C8-NzX">
     <device id="retina6_1" orientation="portrait" appearance="light"/>
     <accessibilityOverrides isEnabled="YES"/>
     <dependencies>
         <deployment identifier="iOS"/>
-        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19141"/>
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="19144"/>
         <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
     </dependencies>
     <scenes>
@@ -55,15 +55,38 @@
                         <rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
                         <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="20" y="817" width="374" height="34"/>
-                                <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>
+                            <stackView opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" verticalCompressionResistancePriority="1000" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Cn1-rU-W3R">
+                                <rect key="frame" x="20" y="831" width="374" height="31"/>
+                                <subviews>
+                                    <textField opaque="NO" clipsSubviews="YES" tag="1" contentMode="scaleToFill" horizontalHuggingPriority="249" 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="0.0" y="0.0" width="301.5" height="31"/>
+                                        <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" smartDashesType="no" smartInsertDeleteType="no" smartQuotesType="no"/>
+                                    </textField>
+                                    <button opaque="NO" contentMode="scaleToFill" horizontalCompressionResistancePriority="1000" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="system" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="eO6-bM-CwW">
+                                        <rect key="frame" x="301.5" y="0.0" width="72.5" height="31"/>
+                                        <accessibility key="accessibilityConfiguration" label="OK"/>
+                                        <constraints>
+                                            <constraint firstAttribute="height" constant="31" id="1nb-CV-bfg"/>
+                                        </constraints>
+                                        <inset key="imageEdgeInsets" minX="0.0" minY="0.0" maxX="2.2250738585072014e-308" maxY="0.0"/>
+                                        <state key="normal" title="Button" image="return" catalog="system"/>
+                                        <connections>
+                                            <action selector="okPressed:" destination="BYZ-38-t0r" eventType="touchUpInside" id="258-zN-fwW"/>
+                                        </connections>
+                                    </button>
+                                </subviews>
+                                <constraints>
+                                    <constraint firstItem="9SS-TP-C7c" firstAttribute="trailing" secondItem="eO6-bM-CwW" secondAttribute="leading" id="9Fe-Sf-U5p"/>
+                                    <constraint firstItem="eO6-bM-CwW" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="top" id="9hv-aL-4z2"/>
+                                    <constraint firstItem="eO6-bM-CwW" firstAttribute="trailing" secondItem="Cn1-rU-W3R" secondAttribute="trailing" id="A4G-YY-4Gn"/>
+                                    <constraint firstItem="9SS-TP-C7c" firstAttribute="leading" secondItem="Cn1-rU-W3R" secondAttribute="leading" id="i2m-BU-Q4a"/>
+                                </constraints>
+                            </stackView>
                             <wkWebView opaque="NO" tag="2" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" layoutMarginsFollowReadableWidth="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ga1-Py-9re">
-                                <rect key="frame" x="20" y="88" width="374" height="721"/>
+                                <rect key="frame" x="20" y="88" width="374" height="737"/>
                                 <color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="calibratedWhite"/>
                                 <wkWebViewConfiguration key="configuration">
                                     <audiovisualMediaTypes key="mediaTypesRequiringUserActionForPlayback" none="YES"/>
@@ -73,29 +96,44 @@
                         </subviews>
                         <color key="backgroundColor" red="0.9882352941176471" green="0.98039215686274506" blue="0.81176470588235294" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
                         <constraints>
-                            <constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="9SS-TP-C7c" secondAttribute="trailing" id="3b6-rZ-W4R"/>
-                            <constraint firstItem="9SS-TP-C7c" firstAttribute="top" secondItem="ga1-Py-9re" secondAttribute="bottom" constant="8" id="B3O-hi-PRD"/>
+                            <constraint firstItem="Cn1-rU-W3R" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="7DU-Hc-uk7"/>
+                            <constraint firstItem="Cn1-rU-W3R" firstAttribute="bottom" secondItem="wfy-db-euE" secondAttribute="top" id="8mr-vY-R1v"/>
+                            <constraint firstItem="ga1-Py-9re" firstAttribute="bottom" secondItem="Cn1-rU-W3R" secondAttribute="top" constant="-6" id="BX0-OH-IJ4"/>
                             <constraint firstItem="ga1-Py-9re" firstAttribute="trailing" secondItem="8bC-Xf-vdC" secondAttribute="trailingMargin" id="Fxh-TE-BtG"/>
-                            <constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="9SS-TP-C7c" secondAttribute="leading" id="MhN-JS-MpC"/>
-                            <constraint firstItem="wfy-db-euE" firstAttribute="top" secondItem="9SS-TP-C7c" secondAttribute="bottom" constant="11" id="Yvi-br-VHk"/>
+                            <constraint firstItem="Cn1-rU-W3R" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="LPE-LO-nct"/>
                             <constraint firstItem="ga1-Py-9re" firstAttribute="top" secondItem="8bC-Xf-vdC" secondAttribute="topMargin" id="p9M-sp-ZwC"/>
                             <constraint firstItem="ga1-Py-9re" firstAttribute="leading" secondItem="8bC-Xf-vdC" secondAttribute="leadingMargin" id="pOi-3s-hrr"/>
                         </constraints>
                     </view>
                     <navigationItem key="navigationItem" title="Ivy" id="9fD-A5-WTg">
-                        <barButtonItem key="rightBarButtonItem" title="Help" id="aDa-GJ-VpX">
+                        <barButtonItem key="leftBarButtonItem" title="Clear" id="70x-xp-aiX">
                             <connections>
-                                <segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/>
+                                <action selector="clear:" destination="BYZ-38-t0r" id="n7k-fg-dil"/>
                             </connections>
                         </barButtonItem>
+                        <rightBarButtonItems>
+                            <barButtonItem title="Help" id="aDa-GJ-VpX">
+                                <connections>
+                                    <segue destination="rfr-rm-AXI" kind="show" id="kuc-xA-dyM"/>
+                                </connections>
+                            </barButtonItem>
+                            <barButtonItem title="Demo" id="ZN2-PQ-aPW">
+                                <connections>
+                                    <action selector="demo:" destination="BYZ-38-t0r" id="NpG-yh-wMO"/>
+                                </connections>
+                            </barButtonItem>
+                        </rightBarButtonItems>
                     </navigationItem>
                     <connections>
-                        <outlet property="bottomConstraint" destination="Yvi-br-VHk" id="EKm-WV-Y1w"/>
+                        <outlet property="bottomConstraint" destination="8mr-vY-R1v" id="BqX-Do-vtq"/>
+                        <outlet property="input" destination="9SS-TP-C7c" id="jUA-25-4WI"/>
+                        <outlet property="okButton" destination="eO6-bM-CwW" id="o9U-hD-NDr"/>
+                        <outlet property="tape" destination="ga1-Py-9re" id="00M-9S-MAl"/>
                     </connections>
                 </viewController>
                 <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
             </objects>
-            <point key="canvasLocation" x="990.40178571428567" y="382.60869565217394"/>
+            <point key="canvasLocation" x="989.85507246376824" y="382.36607142857139"/>
         </scene>
         <!--Navigation Controller-->
         <scene sceneID="ZgV-45-Pf8">
@@ -130,4 +168,7 @@
             <point key="canvasLocation" x="271" y="386"/>
         </scene>
     </scenes>
+    <resources>
+        <image name="return" catalog="system" width="128" height="101"/>
+    </resources>
 </document>
diff --git a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
index 466150b..b6cf353 100644
--- a/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
+++ b/example/ivy/ios/ivy/Images.xcassets/AppIcon.appiconset/Contents.json
@@ -151,6 +151,13 @@
       "size" : "29x29"
     },
     {
+      "idiom" : "watch",
+      "role" : "notificationCenter",
+      "scale" : "2x",
+      "size" : "33x33",
+      "subtype" : "45mm"
+    },
+    {
       "filename" : "icon-80.png",
       "idiom" : "watch",
       "role" : "appLauncher",
@@ -167,6 +174,13 @@
       "subtype" : "40mm"
     },
     {
+      "idiom" : "watch",
+      "role" : "appLauncher",
+      "scale" : "2x",
+      "size" : "46x46",
+      "subtype" : "41mm"
+    },
+    {
       "filename" : "icon-100.png",
       "idiom" : "watch",
       "role" : "appLauncher",
@@ -175,6 +189,13 @@
       "subtype" : "44mm"
     },
     {
+      "idiom" : "watch",
+      "role" : "appLauncher",
+      "scale" : "2x",
+      "size" : "51x51",
+      "subtype" : "45mm"
+    },
+    {
       "filename" : "icon-172.png",
       "idiom" : "watch",
       "role" : "quickLook",
@@ -199,6 +220,13 @@
       "subtype" : "44mm"
     },
     {
+      "idiom" : "watch",
+      "role" : "quickLook",
+      "scale" : "2x",
+      "size" : "117x117",
+      "subtype" : "45mm"
+    },
+    {
       "filename" : "icon-1024.png",
       "idiom" : "watch-marketing",
       "scale" : "1x",
diff --git a/example/ivy/ios/ivy/Info.plist b/example/ivy/ios/ivy/Info.plist
index aad4905..9438af6 100644
--- a/example/ivy/ios/ivy/Info.plist
+++ b/example/ivy/ios/ivy/Info.plist
@@ -11,7 +11,7 @@
 	<key>CFBundleExecutable</key>
 	<string>$(EXECUTABLE_NAME)</string>
 	<key>CFBundleIdentifier</key>
-	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<string>com.google.$(PRODUCT_NAME:rfc1034identifier)</string>
 	<key>CFBundleInfoDictionaryVersion</key>
 	<string>6.0</string>
 	<key>CFBundleName</key>
diff --git a/example/ivy/ios/ivy/IvyController.h b/example/ivy/ios/ivy/IvyController.h
index 0d78d9c..e76bf2f 100644
--- a/example/ivy/ios/ivy/IvyController.h
+++ b/example/ivy/ios/ivy/IvyController.h
@@ -14,8 +14,13 @@
 @property (weak, nonatomic) IBOutlet NSLayoutConstraint *bottomConstraint;
 
 // A text input field coupled to an output "tape", rendered with a WKWebView.
-@property (strong, nonatomic) UITextField *input;
+@property (weak, nonatomic) IBOutlet UITextField *input;
 @property (strong, nonatomic) Suggestion *suggestionView;
-@property (strong, nonatomic) WKWebView *tape;
+@property (weak, nonatomic) IBOutlet WKWebView *tape;
+@property (weak, nonatomic) IBOutlet UIButton *okButton;
+
+- (IBAction)clear:(id)sender;
+- (IBAction)demo:(id)sender;
+- (IBAction)okPressed:(id)sender;
 
 @end
diff --git a/example/ivy/ios/ivy/IvyController.m b/example/ivy/ios/ivy/IvyController.m
index 42e4dd7..ecac33f 100644
--- a/example/ivy/ios/ivy/IvyController.m
+++ b/example/ivy/ios/ivy/IvyController.m
@@ -9,50 +9,46 @@
 
 @end
 
-@implementation IvyController
+@implementation IvyController {
+    NSArray *demo_lines;
+    int demo_index;
+}
 
 - (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 = [self.view viewWithTag:2];
+    
     self.tape.UIDelegate = self;
-
+    self->demo_lines=NULL;
+    
+    [self.okButton setTitle:@"" forState:UIControlStateNormal];
+    [self.okButton setHidden:TRUE];
+    
     [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(textDidChange:)
-               name:UITextFieldTextDidChangeNotification
-             object:self.input];
+     addObserver:self
+     selector:@selector(textDidChange:)
+     name:UITextFieldTextDidChangeNotification
+     object:self.input];
     [[NSNotificationCenter defaultCenter]
-        addObserver:self
-           selector:@selector(keyboardWillShow:)
-               name:UIKeyboardWillShowNotification
-             object:nil];
+     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.UIDelegate = self;
+     addObserver:self
+     selector:@selector(keyboardWillHide:)
+     name:UIKeyboardWillHideNotification
+     object:nil];
+    
     [self.input becomeFirstResponder];
-}
-
-- (void)viewDidAppear:(BOOL)animated
-{
-    [self.view endEditing:YES];
+    [self clear:NULL];
 }
 
 - (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
@@ -75,33 +71,14 @@
 }
 
 - (BOOL)textField:(UITextField *)textField
-    shouldChangeCharactersInRange:(NSRange)range
-                replacementString:(NSString *)str
+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"];
-        NSError *err;
-        NSString *result = MobileEval(expr, &err);
-        if (err != nil) {
-            result = err.description;
-        }
-        result = [result
-            stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
-        result =
-            [result stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
-        result =
-            [result stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
-        NSMutableArray *lines =
-            (NSMutableArray *)[result componentsSeparatedByString:@"\n"];
-        for (NSMutableString *line in lines) {
-            [self appendTape:line];
-        }
-        self.input.text = @"";
+        [self enterPressed];
         return NO;
     }
-
+    
     return YES;
 }
 
@@ -121,51 +98,98 @@
     // 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];
+    [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
     NSNumber *duration =
-        [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
-
+    [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
+    
     UIViewAnimationCurve keyboardTransitionAnimationCurve;
     [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
-        getValue:&keyboardTransitionAnimationCurve];
+     getValue:&keyboardTransitionAnimationCurve];
     UIViewAnimationOptions options =
-        keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
-
+    keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
+    
     [UIView animateWithDuration:duration.floatValue
-        delay:0
-        options:options
-        animations:^{
-        self.bottomConstraint.constant = kbFrame.size.height;
+                          delay:0
+                        options:options
+                     animations:^{
+        self.bottomConstraint.constant = 0 - kbFrame.size.height;
         [self.view layoutIfNeeded];
-        }
-        completion:^(BOOL finished) {
+    }
+                     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];
+    [info objectForKey:UIKeyboardAnimationDurationUserInfoKey];
 
     UIViewAnimationCurve keyboardTransitionAnimationCurve;
     [[info valueForKey:UIKeyboardAnimationCurveUserInfoKey]
-        getValue:&keyboardTransitionAnimationCurve];
+     getValue:&keyboardTransitionAnimationCurve];
     UIViewAnimationOptions options =
-        keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
-
+    keyboardTransitionAnimationCurve | keyboardTransitionAnimationCurve << 16;
+    
+    int offset = self.input.inputAccessoryView != NULL ? self.suggestionView.frame.size.height : 0;
+    
     [UIView animateWithDuration:duration.floatValue
-        delay:0
-        options:options
-        animations:^{
-        self.bottomConstraint.constant = 32;
+                          delay:0
+                        options:options
+                     animations:^{
+        self.bottomConstraint.constant = 0 - offset;
         [self.view layoutIfNeeded];
-        }
-        completion:^(BOOL finished) {
+    }
+                     completion:^(BOOL finished) {
         [self scrollTapeToBottom];
-        }];
+    }];
+}
+
+- (void)enterPressed
+{
+    NSString *text = self.input.text;
+    if ([text isEqual:@""]){
+        if(self->demo_lines==NULL){
+            return;
+        }
+        while (demo_index < self->demo_lines.count) {
+            NSString *line = self->demo_lines[self->demo_index++];
+            if([line hasPrefix:@"#"]) {
+                [self appendTape:line tag:@"comment"];
+            } else {
+                self.input.text = line;
+                break;
+            }
+        }
+    } else if (self->demo_lines!=NULL && [text isEqual:@"quit"]) {
+        [self unloadDemo];
+    } else {
+        [self appendTape:text tag:@"expr"];
+        NSString *expr = [text stringByAppendingString:@"\n"];
+        NSError *err;
+        NSString *result = MobileEval(expr, &err);
+        if (err != nil) {
+            result = err.description;
+        }
+        result = [result
+                  stringByTrimmingCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+        result =
+        [result stringByReplacingOccurrencesOfString:@"<" withString:@"&lt;"];
+        result =
+        [result stringByReplacingOccurrencesOfString:@">" withString:@"&gt;"];
+        NSMutableArray *lines =
+        (NSMutableArray *)[result componentsSeparatedByString:@"\n"];
+        for (NSMutableString *line in lines) {
+            if ([line hasPrefix:@"#"])
+                [self appendTape:line tag:@"comment"];
+            else
+                [self appendTape:line tag:@"result"];
+        }
+        self.input.text = @"";
+    }
 }
 
 - (void)scrollTapeToBottom
@@ -174,11 +198,46 @@
     [self.tape evaluateJavaScript:scroll completionHandler:nil];
 }
 
-- (void)appendTape:(NSString *)text
+- (void)appendTape:(NSString *)text tag:(NSString *)tag
 {
-    NSString *injectSrc = @"appendDiv('%@');";
-    NSString *runToInject = [NSString stringWithFormat:injectSrc, text];
+    NSString *injectSrc = @"appendDiv('%@','%@');";
+    NSString *runToInject = [NSString stringWithFormat:injectSrc, text, tag];
     [self.tape evaluateJavaScript:runToInject completionHandler:nil];
+    [self scrollTapeToBottom];
 }
 
+- (void)loadDemo
+{
+    [self.okButton setHidden:FALSE];
+    NSString *text = DemoText();
+    
+    self->demo_lines = [text componentsSeparatedByCharactersInSet:[NSCharacterSet newlineCharacterSet]];
+    self->demo_index = 0;
+    self.input.text = @"";
+    [self enterPressed];
+}
+- (void)unloadDemo
+{
+    [self.okButton setHidden:TRUE];
+    self->demo_lines=NULL;
+    self.input.text = @"";
+}
+- (IBAction)okPressed:(id)sender {
+    [self enterPressed];
+}
+
+- (IBAction)demo:(id)sender {
+    if (self->demo_lines) { // demo already running
+        [self enterPressed];
+    } else {
+        [self loadDemo];
+    }
+}
+
+- (IBAction)clear:(id)sender {
+    [self unloadDemo];
+    NSString *string = [NSString stringWithContentsOfFile:[[NSBundle mainBundle]
+                                                            pathForResource:@"tape" ofType:@"html"] encoding:NSUTF8StringEncoding error:NULL];
+    [self.tape loadHTMLString:string baseURL:NULL];
+}
 @end
diff --git a/example/ivy/ios/ivy/tape.html b/example/ivy/ios/ivy/tape.html
index 4b9c796..7340e90 100644
--- a/example/ivy/ios/ivy/tape.html
+++ b/example/ivy/ios/ivy/tape.html
@@ -5,40 +5,56 @@
 -->
 <html>
 <head>
-<meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no'>
-<style>
-body {
-	padding: 0;
-	margin: 0;
-	margin-top: 20px;
-	font-family: Menlo, monospace;
+    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no" charset="UTF-8">
+    <style>
+        body {
+            font-family: sans-serif;
+        }
+        div {
+            padding: 1;
+            white-space: pre-wrap;
+            font-family: "Roboto Mono", monospace;
+        }
+        .comment {
+            color: grey;
+        }
+        .expr {
+            font-weight: bold;
+        }
+        .flow-hide {
+            text-overflow: ellipsis;
+            word-break: break-all;
+            white-space: nowrap;
+            overflow: hidden;
+        }
+        .flow-show {
+            word-break: break-all;
+        }
+    </style>
+
+    <script>
+
+function flowClick(el) {
+    el.classList.toggle("flow-hide");
+    el.classList.toggle("flow-show");
 }
 
-.flowhide {
-	text-overflow: ellipsis;
-	word-break: break-word;
-	overflow-wrap: break-word;
+function appendDiv(txt, tag) {
+    var el = document.createElement("div");
+    el.innerHTML = txt;
+    if (tag == "comment") {
+        el.classList.add("comment");
+    } else if (tag == "expr") {
+        el.classList.add("expr");
+    } else {
+        el.classList.add("flow-show");
+        el.onclick = function() {
+                flowClick(el);
+        };
+    }
+    document.body.appendChild(el);
 }
 
-.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>