{"id":1131,"date":"2015-08-06T20:07:45","date_gmt":"2015-08-07T00:07:45","guid":{"rendered":"http:\/\/blog.agilityfeat.com\/?p=1127"},"modified":"2020-11-09T23:27:58","modified_gmt":"2020-11-09T23:27:58","slug":"using-webgl-in-ios-without-phonegap-or-ionic","status":"publish","type":"post","link":"http:\/\/34.200.113.64\/en\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/","title":{"rendered":"Using WebGL in iOS without PhoneGap or Ionic"},"content":{"rendered":"<p><script src=\"https:\/\/s3-us-west-2.amazonaws.com\/blogs-scripts\/util\/code-scripts.js\"><\/script><\/p>\n<p><script src=\"https:\/\/platform.vine.co\/static\/scripts\/embed.js\"><\/script><\/p>\n<p><script src=\"https:\/\/s3-us-west-2.amazonaws.com\/blogs-scripts\/vine\/vine-2015-08-001.js\"><\/script><\/p>\n<p>Let me preface this post by saying that <strong>PhoneGap<\/strong>, <strong>Ionic<\/strong>, <strong>Titanium<\/strong> and <strong>Cordova<\/strong> are fantastic, easy to use tools which allow us to garner the simplicity of <strong>Javascript within iOS<\/strong>. They bridge a very wide gap between web and native in several useful ways and each of those frameworks is optimized for different types and ranges of apps.<\/p>\n<p>At AgilityFeat we have been developing and experimenting with these and other tools quite successfully for a while now.<\/p>\n<p>With that said, the following is a quick and easy way of getting a <strong>javascript graphics app<\/strong> into an <strong>iOS app<\/strong>, with full access to a device&#8217;s <strong>GPU<\/strong>, without going through the trouble of relying on third party tools.<\/p>\n<p>This allows for great <strong>performance<\/strong> at a fraction of the disk space cost.<\/p>\n<p>\u00abWhy isn&#8217;t everyone doing this?\u00bb you ask. In short, it is because you basically have to build from scratch ways of communicating your Javascript app with native components.<\/p>\n<p>So if you&#8217;re looking to simply translate a Javascript app so that you can showcase it on your <strong>iPhone<\/strong> or <strong>iPad<\/strong>, or even to get it up on the app store, and you don&#8217;t need quick and easy access to native features, such as in-app purchases, GPS, multiple pre-built views, etc. This tutorial is for you.<\/p>\n<h2>What you&#8217;ll need<\/h2>\n<ul>\n<li>A Mac with XCode installed in it<\/li>\n<li>Git<\/li>\n<li>NodeJS<\/li>\n<\/ul>\n<p>And that&#8217;s basically it. This tutorial provides a basic app for you to use. I&#8217;d suggest sticking to that for now so you familiarize yourself with the workflow.<\/p>\n<h2>How the concept works<\/h2>\n<p>We&#8217;re going to be using an Open Source tool, built by yours truly, called <strong>iOSify<\/strong>.<\/p>\n<p>Essentially, iOSify is a base for you to build your html application within, and it includes a set of tools which grab your html, javascripts, css and even images, and compress them down to a single Objective C class.<\/p>\n<p>This Objective C class will hold a string which we will feed later to the WKWebView component for it to run.<\/p>\n<p>Go ahead and download iOSify using git:<\/p>\n<p><a href=\"https:\/\/github.com\/agilityfeat\/iOSify\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/agilityfeat\/iOSify<\/a><\/p>\n<p>and from your terminal navigate to the path of the downloaded repo.<\/p>\n<p>Follow the installation instructions, which are basically to install NodeJS, Grunt, and the dependencies.<\/p>\n<p>Now, before we move along let&#8217;s take a look at what iOSify looks like:<br clear=\u201cleft\u201d\/><\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/06-08-2015-01-57-36.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/06-08-2015-01-57-36.png\" alt=\"Folder Structure of iOSify\" style=\"max-width:262px; max-height:248px;\" width=\"262\" height=\"248\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>As you can see we have three folders and a few files. For now let&#8217;s ignore the files on the root of the repo and focus on the folders and their contents.<\/p>\n<p>The <code>dist<\/code> folder will contain the resulting Objective C header and class files which we will be using a little further down the line.<\/p>\n<p>The <code>lib<\/code> folder contains common library files (such as ThreeJS) just for convinience, making it easy to update all libraries using a script or even adding them as git submodules if you want.<\/p>\n<p>The <code>src<\/code> folder contains the app we are going to be compressing. If you open it up you will see it already has contents:<\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/06-08-2015-01-59-41.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/06-08-2015-01-59-41.png\" alt=\"Contents of src folder\" style=\"max-width:245px; max-height:145px;\" width=\"245\" height=\"145\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>Go ahead and double click the <code>index.html<\/code>. You should be looking at this:<br clear=\u201cleft\u201d\/><\/p>\n<p><script src=\"https:\/\/s3-us-west-2.amazonaws.com\/blogs-scripts\/vine\/vine-2015-08-002.js\"><\/script><br \/>\n<br clear=\u201cleft\u201d\/><\/p>\n<p>We&#8217;re not going to delve in detail about how the app itself is built. It is simply a demo app which uses nothing but ThreeJS.<\/p>\n<p>Two things are important about this app, though, which you should consider implementing on your own apps so that you percieve the best view and performance possible.<\/p>\n<p>The first thing is that within the HTML we&#8217;re using two meta tags to scale the app correctly to the viewport of our device:<\/p>\n<pre><code class=\"html\">&lt;meta content='width=device-width, initial-scale=1' name='viewport'&gt;&lt;\/meta&gt;\r\n&lt;meta content='yes' name='apple-mobile-web-app-capable'&gt;&lt;\/meta&gt;<\/code><\/pre>\n<p>Secondly, the cube which you see gleefully animated within the browser window is built using ThreeJS. But in order to optimize the performance by forcing GPU to be used to calculate its geometry and pixels rather than CPU (you know, the whole WebGL layer thing this is all about), we applied the texture and animated the cube using GLShaders; or at least a flavor of GLShaders, the ThreeJS flavor.<\/p>\n<p>For more details be sure to check out the file <code>transformation_shader.js<\/code> inside of the <code>src<\/code> folder and <a href=\"https:\/\/aerotwist.com\/tutorials\/an-introduction-to-shaders-part-1\/\" target=\"_blank\" rel=\"noopener noreferrer\">this fantastic post about using Shaders in ThreeJS<\/a>.<\/p>\n<p>Moving on, now that you have your iOSify app ready, lets fire up the compression of the files.<\/p>\n<p>Within the iOSify root folder, on your terminal, type the following command and hit enter:<\/p>\n<pre><code class=\"html\">$ grunt<\/code><\/pre>\n<p>That&#8217;s it, you should now see the following:<br clear=\u201cleft\u201d\/><\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/iosify_grunt.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/iosify_grunt.png\" alt=\"Grunt command result in terminal\" style=\"max-width:480px; max-height:342px;\" width=\"480\" height=\"342\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>Which means that on the <code>dist<\/code> folder you now have an up-to-date <code>MyCube.h<\/code> and <code>MyCube.m<\/code> files.<\/p>\n<h2>Now the fun begins<\/h2>\n<p>Let&#8217;s head on over to <strong>XCode<\/strong>.<\/p>\n<p>For agility&#8217;s sake (pun intended) I have pre-packaged an XCode project which you can download, using git, from:<\/p>\n<p><a href=\"https:\/\/github.com\/agilityfeat\/WebGL-iOS-Demo\" target=\"_blank\" rel=\"noopener noreferrer\">https:\/\/github.com\/agilityfeat\/WebGL-iOS-Demo<\/a><\/p>\n<p>Fire up XCode and open up the project you just downloaded.<\/p>\n<p>First thing to notice is that XCode will be complaining about the missing <code>MyCube.h<\/code> and <code>MyCube.m<\/code> files:<br clear=\u201cleft\u201d\/><\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-06-at-1.33.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-06-at-1.33.png\" alt=\"XCode error about missing MyCube.h and MyCube.m files\" style=\"max-width:480px; max-height:283px;\" width=\"480\" height=\"283\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>Let&#8217;s fix this, in our finder, by bringing over the files from the <code>dist<\/code> folder in iOSify to the WebGLDemo folder inside the project.<\/p>\n<p>Press <code>Command + Shift + k<\/code> or on the menu select <code>Product -> Clean<\/code> in order to force XCode to update the missing links.<\/p>\n<p>And she&#8217;s ready! Press <code>Command + R<\/code> or select <code>Product -> Run<\/code> on the menu to see your HTML app in action.<\/p>\n<p>Nice, isn&#8217;t it?<\/p>\n<h2>How the code works<\/h2>\n<p>First. If you notice on the project theres a class called <code>WebGLViewController<\/code>.<\/p>\n<p>This is a UIViewController which contains the WebGLView. It has a couple of extensions (<code>WKScriptMessageHandler<\/code> and <code>WKNavigationDelegate<\/code>) which are necessary to allow communication between native controllers and the Javascript app as well as to know when navigation events happen.<\/p>\n<p>The magic sauce within <code>WebGLViewController<\/code> which allows <strong>WKWebView<\/strong> to use our HTML app is this:<\/p>\n<pre><code class=\"objectivec\">  self.myCube = [[MyCube alloc] init];\r\n    \r\n  NSString *path = [NSString stringWithFormat:@\"%@.%@\", @\"com.example\", @\"index.html\"];\r\n  NSURL *url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:path]];\r\n  \r\n  NSData *data = [self.myCube.moduleString dataUsingEncoding:NSUTF8StringEncoding];\r\n  NSError *error = nil;\r\n  [data writeToURL:url options:NSDataWritingAtomic error:&error];\r\n  \r\n  NSURLRequest *request = [NSURLRequest requestWithURL:url];\r\n  \r\n  WKWebViewConfiguration *glConfig = [[WKWebViewConfiguration alloc] init];\r\n  [glConfig.userContentController addScriptMessageHandler:self name:@\"interOp\"];\r\n  \r\n  self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, frameRect.size.width, frameRect.size.height) configuration:glConfig];\r\n  self.webView.navigationDelegate = self;\r\n  \r\n  [self.webView loadRequest:request];\r\n  [self.view addSubview:self.webView];<\/code><\/pre>\n<p>Breaking it down, first I generate a temporary file name for our html file:<\/p>\n<pre><code class=\"objectivec\">  NSString *path = [NSString stringWithFormat:@\"%@.%@\", @\"com.example\", @\"index.html\"];\r\n  NSURL *url = [NSURL fileURLWithPath:[NSTemporaryDirectory() stringByAppendingPathComponent:path]];<\/code><\/pre>\n<p>On this occasion I&#8217;m prepending <code>@\"com.example\"<\/code> to the file name, but it should generally be an UUID specific to the app that is generated only once.<\/p>\n<p>Next we write the HTML string stored within the <code>MyCube<\/code> class to the file we just specified:<\/p>\n<pre><code class=\"objectivec\">  NSData *data = [self.myCube.moduleString dataUsingEncoding:NSUTF8StringEncoding];\r\n  NSError *error = nil;\r\n  [data writeToURL:url options:NSDataWritingAtomic error:&error];<\/code><\/pre>\n<p>We generate a request pointing to the file we just created:<\/p>\n<pre><code class=\"objectivec\">  NSURLRequest *request = [NSURLRequest requestWithURL:url];<\/code><\/pre>\n<p>And we load it:<\/p>\n<pre><code class=\"objectivec\">  [self.webView loadRequest:request];<\/code><\/pre>\n<p>Before I finish this article, let me guide you over a few important details.<\/p>\n<p>Heading on over to the storyboard, you can see I placed a container view, which is meant to hold the WebGLViewController as one of its subviews, in order to make it easier to add constraints.<br clear=\u201cleft\u201d\/><\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-05-at-1.50.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-05-at-1.50.png\" alt=\"Container view and constraints\" style=\"max-width:480px; max-height:428px;\" width=\"480\" height=\"428\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>Make sure to connect the outlet (using <code>Control + Click and hold<\/code>) to the main view you use so it can be called later from the code:<br clear=\u201cleft\u201d\/><\/p>\n<p><a href=\"https:\/\/agilityfeat.com\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-05-at-12.42.png\" target=\"_blank\" rel=\"noopener noreferrer\"><img loading=\"lazy\" src=\"\/wp-content\/uploads\/2015\/08\/Screen-Shot-2015-08-05-at-12.42.png\" alt=\"Connecting container view outlet\" style=\"max-width:480px; max-height:278px;\" width=\"480\" height=\"278\"\/><\/a><br clear=\"left\"\/><\/p>\n<p>When initializing the WebGLViewController, I made sure to make it as painless as possible by adding an <code>initWithFrame<\/code> method so you don&#8217;t have to worry yourself much with sizing and positioning issues.<\/p>\n<p>A good word of advice is to make sure to wait until the view appears before instantiating the WebGLViewController, like you&#8217;ll see on the <code>ViewController.m<\/code> file:<\/p>\n<pre><code class=\"objectivec\">-(void)viewDidAppear:(BOOL)animated {\r\n    self.webGLView = [[WebGLViewController alloc] initWithFrame:self.glContainer.frame];\r\n    [self.glContainer addSubview:self.webGLView.view];\r\n}<\/code><\/pre>\n<p>I have noticed some pretty wonky behavior otherwise.<\/p>\n<p>And that&#8217;s a wrap!<\/p>\n<p>Be sure to drop us a line if you have any comments or doubts.<\/p>\n<p>Happy coding.<\/p>","protected":false},"excerpt":{"rendered":"<p>Let me preface this post by saying that PhoneGap, Ionic, Titanium and Cordova are fantastic, easy to use tools which allow us to garner the simplicity of Javascript within iOS. They bridge a very wide gap between web and native in several useful ways and each of those frameworks is optimized for different types and [&hellip;]<\/p>","protected":false},"author":11,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_et_pb_use_builder":"","_et_pb_old_content":"","_et_gb_content_width":""},"categories":[128,57],"tags":[115,144,145,146,147],"jetpack_featured_media_url":"","yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v15.7 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>Using WebGL in iOS without PhoneGap or Ionic - AgilityFeat Panama Software Test Center<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Using WebGL in iOS without PhoneGap or Ionic - AgilityFeat Panama Software Test Center\" \/>\n<meta property=\"og:description\" content=\"Let me preface this post by saying that PhoneGap, Ionic, Titanium and Cordova are fantastic, easy to use tools which allow us to garner the simplicity of Javascript within iOS. They bridge a very wide gap between web and native in several useful ways and each of those frameworks is optimized for different types and [&hellip;]\" \/>\n<meta property=\"og:url\" content=\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/\" \/>\n<meta property=\"og:site_name\" content=\"AgilityFeat Panama Software Test Center\" \/>\n<meta property=\"article:published_time\" content=\"2015-08-07T00:07:45+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2020-11-09T23:27:58+00:00\" \/>\n<meta property=\"og:image\" content=\"http:\/\/34.200.113.64\/wp-content\/uploads\/2015\/08\/06-08-2015-01-57-36.png\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:label1\" content=\"Est. reading time\">\n\t<meta name=\"twitter:data1\" content=\"7 minutes\">\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebSite\",\"@id\":\"https:\/\/34.200.113.64\/#website\",\"url\":\"https:\/\/34.200.113.64\/\",\"name\":\"AgilityFeat Panama Software Test Center\",\"description\":\"AgilityFeat Panama offers customized, multilevel web and mobile software testing for a variety of industries.\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":\"https:\/\/34.200.113.64\/?s={search_term_string}\",\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/#primaryimage\",\"inLanguage\":\"en-US\",\"url\":\"\/wp-content\/uploads\/2015\/08\/06-08-2015-01-57-36.png\"},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/#webpage\",\"url\":\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/\",\"name\":\"Using WebGL in iOS without PhoneGap or Ionic - AgilityFeat Panama Software Test Center\",\"isPartOf\":{\"@id\":\"https:\/\/34.200.113.64\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/#primaryimage\"},\"datePublished\":\"2015-08-07T00:07:45+00:00\",\"dateModified\":\"2020-11-09T23:27:58+00:00\",\"author\":{\"@id\":\"https:\/\/34.200.113.64\/#\/schema\/person\/c34873e9bee22a4aab19b8a0e800aad0\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/agilityfeatpanama.com\/blog\/2015\/08\/using-webgl-in-ios-without-phonegap-or-ionic\/\"]}]},{\"@type\":\"Person\",\"@id\":\"https:\/\/34.200.113.64\/#\/schema\/person\/c34873e9bee22a4aab19b8a0e800aad0\",\"name\":\"Jean\",\"image\":{\"@type\":\"ImageObject\",\"@id\":\"https:\/\/34.200.113.64\/#personlogo\",\"inLanguage\":\"en-US\",\"url\":\"http:\/\/2.gravatar.com\/avatar\/?s=96&d=mm&r=g\",\"caption\":\"Jean\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","_links":{"self":[{"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/posts\/1131"}],"collection":[{"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/comments?post=1131"}],"version-history":[{"count":3,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/posts\/1131\/revisions"}],"predecessor-version":[{"id":1325,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/posts\/1131\/revisions\/1325"}],"wp:attachment":[{"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/media?parent=1131"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/categories?post=1131"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/34.200.113.64\/en\/wp-json\/wp\/v2\/tags?post=1131"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}