<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Jille van der Weerd on Medium]]></title>
        <description><![CDATA[Stories by Jille van der Weerd on Medium]]></description>
        <link>https://medium.com/@JillevdWeerd?source=rss-78df6c0a7438------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*uucNYPnRjVrsdhYVeguUCg.png</url>
            <title>Stories by Jille van der Weerd on Medium</title>
            <link>https://medium.com/@JillevdWeerd?source=rss-78df6c0a7438------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Tue, 14 Jul 2026 05:58:46 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@JillevdWeerd/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[App Authentication with Laravel Airlock]]></title>
            <link>https://medium.com/@JillevdWeerd/app-authentication-with-laravel-airlock-36e3d2027994?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/36e3d2027994</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[laravel]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Sat, 11 Jan 2020 00:05:46 GMT</pubDate>
            <atom:updated>2020-01-13T13:46:49.558Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*bwwj9XnyFJEXEYa70r1BHg.png" /></figure><p>I often use Laravel to build the API that support the apps I build, because I can quickly create something that <em>just works</em> without writing too much code. However, dealing with authentication has (in my opinion) always been a cumbersome task.</p><p>So it doesn’t come as a surprise that after seeing <a href="https://twitter.com/taylorotwell/status/1215022507478138882?s=20">Taylor Otwell’s Tweet about Laravel Airlock</a> I wanted to give this a try for authentication in a simple mobile application.</p><p>A quick introduction: Airlock is a package for Laravel. It’s supposed to be a featherweight alternative to existing authentication methods (like Laravel Passport) for use in SPAs and simple APIs.</p><p>I’m going to briefly walk you through installing Airlock, but if you run into any problems, please refer to the <a href="https://github.com/laravel/airlock">documentation</a>. After installing Airlock and building a simple authentication system with it, I’ll show you how to use it in your iOS app. I’ll assume you have basic Swift knowledge, and I won’t go in depth on creating the interface I’ll show in my screenshots.</p><h3>Installation</h3><p>I created a new Laravel project to test this. I’ll assume you know how to do this, but for more information please refer to the <a href="https://laravel.com/docs/6.x">Laravel documentation</a>. cd into the project directory and execute the following commands in your terminal:</p><pre>composer <strong>require</strong> laravel/airlock<br>php <strong>artisan</strong> vendor:publish<br>php <strong>artisan</strong> migrate</pre><p>We’re now ready to dive into the Laravel project to finalise setting up Airlock.</p><p>In your User model, use the HasApiTokens trait as follows:</p><pre>use Laravel\Airlock\HasApiTokens;<br>// Other imports omitted.<br>class User extends Authenticatable<br>{<br>    use <strong>HasApiTokens</strong>, Notifiable;</pre><pre>    // Class body omitted.<br>}</pre><h3>Routes</h3><p>Now that we’ve finished setting up, let’s create some routes. Add the following routes to your routes/api.php file:</p><pre>Route::<strong>prefix</strong>(‘<strong>airlock</strong>’)-&gt;namespace(‘API’)-&gt;group(function() {<br>    Route::<strong>post</strong>(‘<strong>register</strong>’, ‘AuthController@register’);<br>    Route::<strong>post</strong>(‘<strong>token</strong>’, ‘AuthController@token’);<br>});</pre><p>The user will use these two routes to register an account and to request their token (basically, to log in). Let’s create the AuthController and write the implementation of these routes. Run the following line in your terminal:</p><p>php artisan make:controller API\AuthController</p><p>Open the app/Http/Controllers/API/AuthController.php file you just created and add the register function:</p><pre>public function register(Request $request)<br>{ <br>    $<strong>validator</strong> = <strong>Validator</strong>::make($<strong>request</strong>-&gt;all(), [<br>        ‘name’ =&gt; [‘required’, ‘string’, ‘max:255’],<br>        ‘email’ =&gt; [‘required’, ‘string’, ‘email’, ‘max:255’, ‘unique:users’],<br>        ‘password’ =&gt; [‘required’, ‘string’, ‘min:8’],<br>        ‘device_name’ =&gt; [‘required’, ‘string’]<br>    ]); <br>    // 1</pre><pre>    if ($<strong>validator</strong>-&gt;<strong>fails</strong>()) {<br>        return response()-&gt;json([‘error’ =&gt; $validator-&gt;errors()], 422);<br>    }<br>    // 2</pre><pre>    $input = $<strong>request</strong>-&gt;all();<br>    $input[‘password’] = <strong>bcrypt</strong>($input[‘password’]);<br>    $user = <strong>User</strong>::<strong>create</strong>($input);<br>    // 3</pre><pre>    $token = $user-&gt;<strong>createToken</strong>($request-&gt;device_name)-&gt;<strong>plainTextToken</strong>;<br>    // 4</pre><pre>    return <strong>response</strong>()-&gt;<strong>json</strong>([‘token’ =&gt; $token], <strong>200</strong>);<br>}</pre><p>Here’s a step by step breakdown of what this code does:</p><ol><li>We validate the incoming request.</li><li>Check the outcome of this validation. If it failed, return the errors in the response.</li><li>Hash the password and create the new User.</li><li>Create a token and save it so we can return it in the response.</li></ol><p>As you can see, performing a successful request to this endpoint will return the generated token in the body of the response. However, to make sure our users don’t need to create a new account whenever they want to use the app on another device, let’s create the function to allow our users to request their token:</p><pre>public function token(Request $request)<br>{<br>    $<strong>validator</strong> = <strong>Validator</strong>::make($<strong>request</strong>-&gt;all(), [<br>        ‘email’ =&gt; [‘required’, ‘string’, ‘email’, ‘max:255’],<br>        ‘password’ =&gt; [‘required’, ‘string’, ‘min:8’],<br>        ‘device_name’ =&gt; [‘required’, ‘string’]<br>    ]);</pre><pre>    if ($<strong>validator</strong>-&gt;<strong>fails</strong>()) {<br>        return response()-&gt;json([‘error’ =&gt; $validator-&gt;errors()], 422);<br>    }<br>    // 1<br> <br>    $user = <strong>User</strong>::<strong>where</strong>(‘<strong>email</strong>’, $request-&gt;email)-&gt;first();<br>    // 2<br> <br>    if (<strong>!$user</strong> || <strong>!Hash</strong>::<strong>check</strong>($request-&gt;password, $user-&gt;password) {<br>        return <strong>response</strong>()-&gt;<strong>json</strong>([‘error’ =&gt; ‘The provided credentials are incorrect.’], <strong>422</strong>);<br>    ƒ}<br>    // 3<br> <br>    return <strong>response</strong>()-&gt;<strong>json</strong>([‘token’ =&gt; $user-&gt;<strong>createToken</strong>($request-&gt;device_name)-&gt;<strong>plainTextToken</strong>]);<br>    // 4<br>}</pre><p>Here’s what this code does:</p><ol><li>We once again validate the request, and return the errors if validation fails.</li><li>We get the first User from our database for the submitted email address. The email is unique, so at most this will return one user.</li><li>If the user doesn’t exist (so the email was incorrect), or the password isn’t correct, we return an error the app can pick up.</li><li>We return the generated token.</li></ol><p>The scaffolding of our authentication is now complete! Let’s quickly create a route that’ll allow us to get the name of the logged in user.</p><p>In your routes/api.php file, add the following code:</p><pre><strong>Route</strong>::<strong>middleware</strong>(‘auth:airlock’)-&gt;get(<strong>‘/name’</strong>, function (Request $request) {<br>    return <strong>response</strong>()-&gt;<strong>json</strong>([‘name’ =&gt; <strong>$request-&gt;user()</strong>-&gt;name]);<br>});</pre><p>Here we use the ’auth:airlock’ middleware. This will make sure the api token is present in the headers of the request, and will return an error if it isn’t. When the request works, we can use $request-&gt;user() to get access to the user model.</p><p>There’s a lot more I’d like to write about the things we can easily do with Airlock, but I’ll refrain from doing so to keep this article short. If you want to learn more about the possibilities, like adding /abilities/ and revoking tokens, take a look at the <a href="https://github.com/laravel/airlock">documentation</a>.</p><h3>Let’s authenticate</h3><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*oHBdVt0F0w7EINfb_1okFw.gif" /><figcaption>hunter2 is not a valid password with the validation rules we defined.</figcaption></figure><p>It’s time to put our api to the test. Let’s open up Xcode and quickly whip up a login screen. After doing whatever client side validation you want to perform, pass the values from the fields in your register form to the function you’ll use to perform the HTTP request. To provide code that can be used regardless of your preferred architecture, I’ll simply show you how to construct the request to register a user. Make sure to include the application/json Content-Type header, or your requests won’t succeed.</p><pre>var request = URLRequest(url: URL(string: “<a href="http://yourprojecturl.test/api/airlock/register">http://yourprojecturl.test/api/airlock/<strong>register</strong></a>&quot;)!)<br>request.httpMethod = “<strong>POST</strong>”<br>request.addValue(“<strong>application/json</strong>”, forHTTPHeaderField: “<strong>Content-Type</strong>”)<br>guard let body = try? jsonEncoder.encode([<br>    “name”: name,<br>    “password”: password,<br>    “email”: email,<br>    “device_name”: deviceName<br>]) else {<br>    return<br>}</pre><pre>request.httpBody = body</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/350/1*NB_1qnFfpu1uhqL-jy6WWQ.gif" /></figure><p>The request to log in is very similar, we simply replace the final url endpoint with /token and remove the name from the body. A successful request to any of these endpoints will result in the following response body:</p><pre>{<br> “<strong>token</strong>”: “<strong>&lt;api token&gt;</strong>”<br>}</pre><p>After your user registers their account or logs in, make sure to store this token somewhere. We’ll use it in this next request to retrieve their name from the backend:</p><pre>var request = URLRequest(url: URL(string: “<a href="http://yourprojecturl.test/api/name">http://yourprojecturl.test/api/<strong>name</strong></a>&quot;))<br>request.httpMethod = “<strong>GET</strong>” <br>request.addValue(“<strong>Bearer \(apiToken)</strong>”, forHTTPHeaderField: “<strong>Authorization</strong>”)</pre><h3>A quick recap</h3><p>Let’s briefly go over the things I explained in this article, to make sure we’re on the same page.</p><ul><li>We set up some scaffolding with Laravel and Airlock to handle user authentication.</li><li>We can create routes using the ’auth:airlock’ middleware that will validate the token sent in the headers, and gives us access to the corresponding user model.</li><li>We can create the requests to consume our API.</li></ul><h3>Next steps</h3><p>As I mentioned before, this just scratches the surface of what we can do with Airlock. You can create tokens with different abilities so some users can perform actions while others can’t. Or you can set the token to expire after a certain amount of time, so the user has to request a new one. Airlock is still in beta, so expect things to change and new features to be added.</p><p>Hopefully this article gave you some insights in how Airlock can be a simple alternative to other authorization methods. If you’re a mobile developer and you have never worked with Laravel before, I highly recommend spending some time with it. You’ll be able to build the backend for your apps in no time.</p><p>If you have any questions, the best way to reach out to me is through <a href="https://twitter.com/jillevd_w">Twitter</a>. If you like reading this, please let me know if you’d like to read more about Laravel, Swift, or a combination of the two.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=36e3d2027994" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Making iOS animations feel just right]]></title>
            <link>https://blog.prototypr.io/making-animations-feel-just-right-673d797692c1?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/673d797692c1</guid>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[ios-app-development]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Fri, 06 Jul 2018 10:41:45 GMT</pubDate>
            <atom:updated>2018-07-11T09:08:26.374Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*t60BKBHLa5aOVA03Mm9m1g.png" /><figcaption>Image taken from the WWDC talk <a href="https://developer.apple.com/videos/play/wwdc2018/803/">designing fluid interfaces</a></figcaption></figure><p>This year’s WWDC had <a href="https://developer.apple.com/videos/play/wwdc2018/803/">a great talk on designing fluid interfaces</a>. My main takeaway from this talk was how important gesture driven animations are, but not just that: how important <em>velocity</em> is while designing an animation. As to why this talk motivated me to write this article; somewhere in the talk they show this image:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oWMx0jEe96QhJN_z5revSQ.png" /><figcaption>Image from WWDC talk <a href="https://developer.apple.com/videos/play/wwdc2018/803/">designing fluid interfaces</a></figcaption></figure><p>And this sparked my interest: “Could it really be this easy?”. Turns out it is, so in this article I’m going to try to shed some light on that subject, and help you to create animations that feel just right.</p><h3>Let’s get started</h3><p>As an example, I’m going to try to replicate the behaviour of PiP (picture in picture) view as seen in the FaceTime app. This view can be dragged and dropped in any of the corners of the screen, but when given a swipe it will take that velocity into account. Let’s get started on building that.</p><blockquote>Note: I’m not going to show everything (like initialising the positions for the corners to snap to) since that would be a lot of boilerplate code. I’m just going to show what is necessary to put the pieces everything together and implement this principle in your own application(s).</blockquote><p>The code shown below handles a naive way of dragging and dropping the view: when the drag gesture ends (so when the finger no longer touches the screen) the nearest corner is determined, and the view snaps to that position. All this code is placed in the UIView subclass of the PiP view, so properties like frame are readily available. The setupGestureRecognizer function is called when we initialise the View in the ViewController, the currentPosition property is used to save the corner the view is currently “snapped” to and the nearesCornerPosition function uses the current position of the view to determine the corner that is closest (and then returns that corner).</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/055cfe497fa8056c45720dab63fea87f/href">https://medium.com/media/055cfe497fa8056c45720dab63fea87f/href</a></iframe><p>So there’s not too much magic going on over here. The only cool thing you’ll notice is that we’re using <em>spring animations </em>here. From the <a href="https://developer.apple.com/documentation/uikit/uiview/1622594-animatewithduration">documentation</a>:</p><blockquote>Performs a view animation using a timing curve corresponding to the motion of a physical spring.</blockquote><p>This has some properties, like duration, delay, dampingRatio and velocity that you can play around with a bit to get the right feel. These properties are explained in the WWDC talk mentioned above.</p><p>This is the result:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2FcdIo5v9K4RX1CksRLP%2Ftwitter%2Fiframe&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FcdIo5v9K4RX1CksRLP%2Fgiphy.gif&amp;image=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FcdIo5v9K4RX1CksRLP%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="907" frameborder="0" scrolling="no"><a href="https://medium.com/media/38c07fd1ea21a51d84dec70bd6cdb75e/href">https://medium.com/media/38c07fd1ea21a51d84dec70bd6cdb75e/href</a></iframe><p>It works, but it doesn’t feel right. Let’s fix that.</p><h3>Velocity and deceleration</h3><p>Woo! Physics! Let’s make our PiP view less naive. Let’s scrap that; we’re going to make it <em>smart.</em> We’ll have it measure its own velocity, declare a deceleration rate and project the final position it will land in if it continues on that trajectory. Now we can use that position to determine to which corner to snap. Sounds good? Let’s get started!</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ba946eb5c848f3480125020e609674f0/href">https://medium.com/media/ba946eb5c848f3480125020e609674f0/href</a></iframe><p>Alright; let’s break this down. Dragging around the view is the same as it was before, but the real magic happens when the gesture.state == .ended block fires. We take the velocity from the gesture object, and we take the <a href="https://developer.apple.com/documentation/uikit/uiscrollview/1619438-decelerationrate">default </a><a href="https://developer.apple.com/documentation/uikit/uiscrollview/1619438-decelerationrate">decelerationRate from the </a><a href="https://developer.apple.com/documentation/uikit/uiscrollview/1619438-decelerationrate">UIScrollView class</a>. We then project the final position with a function called project:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/084d56b33675760831d48c708485071f/href">https://medium.com/media/084d56b33675760831d48c708485071f/href</a></iframe><p>Instead of using the frame.origin property to determine which corner to snap to, we now instead use the projected position we just created.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fgiphy.com%2Fembed%2FWgSEB1cQGgIC7SVWVF%2Ftwitter%2Fiframe&amp;url=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FWgSEB1cQGgIC7SVWVF%2Fgiphy.gif&amp;image=https%3A%2F%2Fmedia.giphy.com%2Fmedia%2FWgSEB1cQGgIC7SVWVF%2Fgiphy.gif&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=giphy" width="435" height="915" frameborder="0" scrolling="no"><a href="https://medium.com/media/3e2bf89a2dc8ac99bc1fdc2ae848c264/href">https://medium.com/media/3e2bf89a2dc8ac99bc1fdc2ae848c264/href</a></iframe><h3>And that’s it!</h3><p>So we’ve now covered a way to take velocity into account when building animations. I really recommend watching the WWDC talk mentioned above, since it gives a great explanation about the inner workings of the spring damping used in these animations.</p><h3>One more thing</h3><p>If you test the animations as shown above on a bigger phone (say, and iPhone X) you might notice the the animation feels a bit weird. I often do some phone specific checks to create an animation that is tailored to the device. In this case, a simple check like:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c700de4cb779fffcdbde845c8f6b966f/href">https://medium.com/media/c700de4cb779fffcdbde845c8f6b966f/href</a></iframe><p>can really make a huge difference. In this case, the animation will play for 0.5 seconds if the device is smaller than the iPhone X, and will play longer on the iPhone X and all iPads. You can also play around with the spring properties to get the animation <em>just right</em>.</p><p><em>If you liked this article, check out the </em><a href="https://medium.com/@JillevdWeerd"><em>other articles I wrote</em></a><em>! If you’d like to get in touch, you can follow me on </em><a href="https://twitter.com/Jillevd_W"><em>Twitter</em></a><em> or shoot me a message on </em><a href="https://www.linkedin.com/in/jillevanderweerd/"><em>LinkedIn</em></a><em>. Also, if you need any help with anything iOS Programming related, you can </em><a href="https://discord.gg/Vt7fQRz"><em>join this Discord server</em></a><em> created for exactly that purpose :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=673d797692c1" width="1" height="1" alt=""><hr><p><a href="https://blog.prototypr.io/making-animations-feel-just-right-673d797692c1">Making iOS animations feel just right</a> was originally published in <a href="https://blog.prototypr.io">Prototypr</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Creating links between WKWebView and Native code]]></title>
            <link>https://medium.com/@JillevdWeerd/creating-links-between-wkwebview-and-native-code-8e998889b503?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/8e998889b503</guid>
            <category><![CDATA[development]]></category>
            <category><![CDATA[web-development]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[swift]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Fri, 23 Feb 2018 10:24:29 GMT</pubDate>
            <atom:updated>2018-07-06T12:55:31.318Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*voQEpw4bvyNS--52-h6mfw.png" /></figure><p>The past month I’ve been working for a customer that had some pretty specific requirements for their product; they wanted an application that used a particular map component, the same one they use in their web app. That meant two things: javascript 🙈 and trying to find out how to communicate between the native code and the webview that would house the map.</p><h3>The Problem</h3><p>Let’s take some more time to sketch out the problem. We’re dealing with two major components here: a WKWebView to house the map and the native user interface. We want to send messages both ways between these components. When the user interacts with certain objects on the map, we need to trigger events in our native code to perform actions, like show an interface element, or trigger a segue to a new view. We also need to send messages to the webview from our native code, so we can delegate all the heavy lifting to the native application to minimize the amount of calculations javascript has to make.</p><h3>The Solution</h3><p>Lucky for us, WKWebView offers everything we need. With a bit of tinkering, we can configure it to do exactly what we need! Setting up a webview is trivial and well-documented, so I’m not going to explain that here. You can either instantiate it through code or simply drag one in your view in Interface Builder and create an outlet for it. I chose to do the latter.</p><p>Let’s make our ViewController conform to <a href="https://developer.apple.com/documentation/webkit/wkscriptmessagehandler">WKScripMessageHandler</a> and <a href="https://developer.apple.com/documentation/webkit/wknavigationdelegate">WKNavigationDelegate</a>:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/81c72f1de3b693e13f20d3268680872f/href">https://medium.com/media/81c72f1de3b693e13f20d3268680872f/href</a></iframe><p>Alright, now we’ve got part of our webview setup. We’ll come back to this later, but first, we will dive into some javascript.</p><h3>Javascript 🙈</h3><blockquote>A quick disclaimer: I am not a web developer. If you are a seasoned javascript professional, please shield your eyes from the code that follows.</blockquote><p>All we need to do in javascript is create a few functions for handling incoming responses, and sending responses back to the webview. I expect you’re comfortable with setting up an index.html file, so I won’t show you how to do that.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/558f38a1bbeb6fe384dbfd43cc236cfd/href">https://medium.com/media/558f38a1bbeb6fe384dbfd43cc236cfd/href</a></iframe><p>Now, we probably want to do something else than just logging the values we receive, but for the sake of explaining the concept, this will do. Say we’re keeping a list of people in the webview, the <em>addPerson</em> function will be called from the native context to add it to the webview. The <em>sendNameToNative</em> function can be called to send a message back to the native code. Let’s add the last building blocks to get everything working!</p><h3>The last few steps</h3><p>We need to do some things in our <em>loadView </em>function, so we will override that. In here we’ll set some properties for our webview to listen to the callback from javascript when the <em>sendNameToNative</em> function is called.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/46993aa7308ade7d00ff6f8d6a8317bc/href">https://medium.com/media/46993aa7308ade7d00ff6f8d6a8317bc/href</a></iframe><blockquote>You might need to fiddle with this a bit depending on how you set up your views. One thing you might to do is change the frame to be equal to <em>view.frame, </em>and another thing that might fix problems here is adding <em>view = webview </em>after the last line. This might create some other problems.</blockquote><p>There’s only one thing left to do here, which is actually sending a message to the webview.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e62ecab29d9c4c71d62b50f8f49482ed/href">https://medium.com/media/e62ecab29d9c4c71d62b50f8f49482ed/href</a></iframe><p>This is the final piece to our puzzle; actually sending the message to the webview. Calling this function allows us to execute javascript in the webview. As you can see in the example, when we want to send a String, we need to add ‘ ’ around it. We can now call functions like this in the <em>webView(_:didFinish:) </em>function to set up the webview on load, and call it after interaction with the native user interface to send more messages.</p><blockquote>Keep in mind that when you’re handling properties entered by the user, you might want to validate it so you don’t execute malicious javascript in the webview.</blockquote><h3>What’s next?</h3><p>Now that you’ve got the structure laid out, you can go wild:</p><ul><li><em>Actually</em> handle the response to trigger certain actions in native code, like animations or segues.</li><li>Add more handlers to handle different responses from the webview, creating a tightly-knit connection between the two</li></ul><p>Congratulations! If you’re reading this you’ve created a connection between a WKWebView and the native context of your application. This opens a lot of possibilities, and you can take this as far as you want. If you created something awesome with this, please share it with me!</p><p><em>If you liked this article, check out the </em><a href="https://medium.com/@JillevdWeerd"><em>other articles I wrote</em></a><em>! If you’d like to get in touch, you can follow me on </em><a href="https://twitter.com/Jillevd_W"><em>Twitter</em></a><em> or shoot me a message on </em><a href="https://www.linkedin.com/in/jillevanderweerd/"><em>LinkedIn</em></a><em>. Also, if you need any help with anything iOS Programming related, you can </em><a href="https://discord.gg/Vt7fQRz"><em>join this Discord server</em></a><em> created for exactly that purpose :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8e998889b503" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bringing animation to UITabBarController]]></title>
            <link>https://medium.com/@JillevdWeerd/bringing-animation-to-uitabbarcontroller-1b8780ced374?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/1b8780ced374</guid>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[ux]]></category>
            <category><![CDATA[user-experience]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[app-development]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Mon, 29 Jan 2018 18:22:29 GMT</pubDate>
            <atom:updated>2018-07-06T12:56:00.217Z</atom:updated>
            <content:encoded><![CDATA[<p>It’s important to animate the things the user interacts with most to give them a more natural feel. I decided I wanted to add some small animations to the UITabBarController. There are probably some libraries that do this but I decided to write my own implementation.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F268379794%3Fapp_id%3D122963&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F268379794&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2Fdefault_295x166&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="462" height="80" frameborder="0" scrolling="no"><a href="https://medium.com/media/1e709ffaaa3641fdec5ede1e9bacaef0/href">https://medium.com/media/1e709ffaaa3641fdec5ede1e9bacaef0/href</a></iframe><p>What you see in the video above is the TabBar’s <em>selectionIndicatorImage, </em>a property of the TabBar to indicate that tab is currently selected. I then drew an image at the bottom of the TabBar. What happens now is that every tab has their own image, and it just shows and hides them depending on the state. This isn’t what I wanted and couldn’t possibly solve the problem I had, since they are all different images.</p><h3>Lets subclass</h3><p>The solution to our problem lies in subclassing UITabBarController. I made a class called TabBarController and added a UIImageView to it. I then tried to find a way to move this image to the right position whenever a tab was selected. Thanks to the delegate functions UITabBarController provides, this was a piece of cake.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/a17536d4ace195c7cfd98859e6118f3c/href">https://medium.com/media/a17536d4ace195c7cfd98859e6118f3c/href</a></iframe><p>Let’s break this down. We create an empty <em>indicatorImage </em>variable. This will be the line that we move around. We initialise it in the <em>viewDidLoad</em> method. For that we use the <em>createSelectionIndicator </em>function that takes a color, a size (the width we want our bar to be, and the heigh of the TabBar itself) and a lineheight. The function draws this into an image an returns it, and we then add that to the TabBar.</p><p>We can now move the image that we created around! I use the <em>didSelect item </em>delegate method to check for touches and I use some boilerplate to check for the location the image needs to move to. Wrapping this all in an <em>UIView.animate</em> block gives us a smooth animation between the two points.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x7KtqHWi00FJFcTt8OC2jA.png" /></figure><p>When you’re done implementing all that, you can hook it up to Interface Builder by setting the custom class to your TabBarController.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/506/1*07t2LXTTpIz8ThCbHu5ofQ.png" /></figure><p>I had to tinker with a few of the options in the Attributes inspector. For me, these settings did the job for all different devices. If it doesn’t work for you, try to fiddle with the <em>Item Positioning </em>and the <em>Content Mode </em>attributes.</p><h3>The result</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fplayer.vimeo.com%2Fvideo%2F268379878%3Fapp_id%3D122963&amp;dntp=1&amp;url=https%3A%2F%2Fvimeo.com%2F268379878&amp;image=https%3A%2F%2Fi.vimeocdn.com%2Fvideo%2F699093240_295x166.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=vimeo" width="458" height="84" frameborder="0" scrolling="no"><a href="https://medium.com/media/f4446720ee55b705f18b23658b07e2e2/href">https://medium.com/media/f4446720ee55b705f18b23658b07e2e2/href</a></iframe><p>After walking through all these steps you’ve created an animated selectionIndicatorImage for the UITabBarController. This is simply a proof of concept to show how it can be done, and if you write a better implementation make sure to send it to me!</p><p><em>If you liked this article, check out the </em><a href="https://medium.com/@JillevdWeerd"><em>other articles I wrote</em></a><em>! If you’d like to get in touch, you can follow me on </em><a href="https://twitter.com/Jillevd_W"><em>Twitter</em></a><em> or shoot me a message on </em><a href="https://www.linkedin.com/in/jillevanderweerd/"><em>LinkedIn</em></a><em>. Also, if you need any help with anything iOS Programming related, you can </em><a href="https://discord.gg/Vt7fQRz"><em>join this Discord server</em></a><em> created for exactly that purpose :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1b8780ced374" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Android Dev Journey: Part 2]]></title>
            <link>https://medium.com/@JillevdWeerd/my-android-dev-journey-part-2-eef2f6d290f4?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/eef2f6d290f4</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[mobile]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Fri, 19 Jan 2018 12:44:48 GMT</pubDate>
            <atom:updated>2018-07-06T12:56:42.130Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*fIZO8_PvK3AdwoazQLRxZA.png" /></figure><p>A while ago I wrote an <a href="https://medium.com/@JillevdWeerd/bridging-the-gap-between-android-and-ios-development-f0e17582d2d3">article</a> regarding some of the differences between iOS and Android development I ran into. As a conclusion, I wrote that it’s important to understand that you need a completely different mindset doing both of them. The view lifecycle is different, the interface builder relies on some completely different principles (density independent pixels? What?), and last but not least: if you’re not using libraries, you’re missing out.</p><h3>Libraries</h3><p>Let’s kick this one off with an example: the app I built had to display images. Simple enough, lots of apps display images. In iOS I can just create UIImageViews and add UIImages to them whenever I please. I tried to do exactly that in my Android app; I had data classes with associated Bitmaps, and then I loaded them into ImageViews whenever necessary. The result: constant Out of Memory Exceptions resulting in hours upon hours of memory debugging because the Bitmaps weren’t being released!</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*B2SsIQVqIlHnnlOlGzhkKA.png" /><figcaption>The result of hours of memory debugging :-)</figcaption></figure><p>I never had to deal with any memory management before (and technically, I still didn’t have to) so I started crawling through StackOverflow(SO) for a solution. At some point, I got my memory footprint down by calling the <em>recycle</em> function on the Bitmaps I wasn’t going to use, and by calling the <em>System.GC</em> function whenever I knew a garbage collection would free up some allocations. This resulted in the memory graph as shown above.</p><p>In my quest through the Android section of SO, I had seen the name <a href="http://square.github.io/picasso/"><em>Picasso</em></a> dropped a lot. This is an image library that took care of the same problem I tried to fix and did more. After a while of struggling with doing my own memory management, I decided to jump the gun and import the dependency. I was amazed: not only did this manage my memory way better than I did by doing things like caching images that might be needed later instead of reloading them every time, it allowed me to remove all my networking code regarding images.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/1bf4a81cbdc3c8fb7b6dbd51669d0dc9/href">https://medium.com/media/1bf4a81cbdc3c8fb7b6dbd51669d0dc9/href</a></iframe><p>By using the Picasso library, I was able to remove the class shown above and all the code that took care of processing that image (resizing, scaling correctly, making it circular) and replace it with this single line of code:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c83ea4e2e673a843e9a9e0864d2c56d8/href">https://medium.com/media/c83ea4e2e673a843e9a9e0864d2c56d8/href</a></iframe><p>This was my eye-opener. If I could make my life that much easier with this one library, how many other brilliant libraries were still out there, waiting for me to integrate them into my code? That same day I replaced all my networking code with the <a href="https://square.github.io/okhttp/">OkHttp</a> library, further improving my memory footprint by not having countless strings allocated by a StringBuilder parsing a JSON file.</p><h3>API Levels</h3><p>This has been a hurdle, to say the least. It’s definitely just something you need to get used to, but it’s a hassle at first. We all know that great feeling of finding a solution to the <em>exact </em>problem you’re having on SO. Android developers know how that feeling can get crushed by looking up the specified methods in the documentation and finding out the required API level is higher than the lowest one they have to support. What follows is a scavenger hunt to find out the inner workings of the method, and to try to replicate its behaviour. An example of this is when I wanted to execute something after an AnimatorSet finished animating:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b6cc71f926bf7d1dcc3cbb02c1275d46/href">https://medium.com/media/b6cc71f926bf7d1dcc3cbb02c1275d46/href</a></iframe><p>With API level 24 and up, we can use the <em>getTotalDuration </em>method and use its result in a <em>postDelayed</em> block. Sadly, my app had to support lower API levels so I wrote the above implementation.</p><blockquote>Nice bonus: the extension function, as shown above, can also be called at points between animations while creating the AnimatorSet object, to fire code blocks at certain points during the animation! :-)</blockquote><p>This taught me a valuable lesson: take some more time looking through SO. Answers from 2013 might not be relevant at all anymore, and new answer are often a lot further down the page! An example I’ve used earlier is JSON parsing. This was one of the first things I built into the app, as it had to communicate with the backend I made for it. The first Google results wrote about using BufferedReaders and StringBuilders; a lot of boilerplate that does something very simple, allocates a lot of Strings and clogs up memory debugger with them. If I had taken some more time to scroll down the page of SO I would’ve found the great suggestions on using the <a href="https://github.com/google/gson">GSON</a> library or the OkHttp library for network requests.</p><h3>Wrap-up</h3><p>I could try to write a section about the interface builder, but the truth is I suspect I just don’t grasp that fully yet. I’ve embraced Xcode’s interface builder, and Android Studio just uses a lot of different terms and principles.</p><p>The most valuable lesson I’ve learned is to keep an open mind at all times. It’s easy to turn on tunnel vision and to try and find a solution similar to what works on iOS, but often it just isn’t that simple. You shouldn’t think about refactoring what works on iOS to Android, you should try to find a solution that works for Android. Whether that is using a library or writing your own solution to a problem, you shouldn’t be afraid to try new things.</p><p><em>If you liked this article, check out the </em><a href="https://medium.com/@JillevdWeerd"><em>other articles I wrote</em></a><em>! If you’d like to get in touch, you can follow me on </em><a href="https://twitter.com/Jillevd_W"><em>Twitter</em></a><em> or shoot me a message on </em><a href="https://www.linkedin.com/in/jillevanderweerd/"><em>LinkedIn</em></a><em>. Also, if you need any help with anything iOS Programming related, you can </em><a href="https://discord.gg/Vt7fQRz"><em>join this Discord server</em></a><em> created for exactly that purpose :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=eef2f6d290f4" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Bridging the gap between iOS and Android Development]]></title>
            <link>https://medium.com/@JillevdWeerd/bridging-the-gap-between-android-and-ios-development-f0e17582d2d3?source=rss-78df6c0a7438------2</link>
            <guid isPermaLink="false">https://medium.com/p/f0e17582d2d3</guid>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Jille van der Weerd]]></dc:creator>
            <pubDate>Mon, 18 Dec 2017 10:04:33 GMT</pubDate>
            <atom:updated>2018-07-06T12:57:03.554Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*4Eftu4jKL--pc0wji0y0fw.png" /></figure><p>Hey! My name is Jille van der Weerd. Recently I was asked to build the Android counterpart for an iOS app I made, so I had to dive into the rabbit hole that is Android development. Working with a new IDE, a new language and a whole new community of people to learn from, it was an eventful journey. The app is now nearly finished, and I decided to write down some of the important things to note when making the same leap from iOS to Android development.</p><blockquote>Note: I don’t claim to know everything about the things I write about. These are just my findings, and I hope some people can learn from them. If you have any suggestions or criticism, please let me know.</blockquote><h3>A quick introduction</h3><p>Just under a year ago I dropped out of my Computer Science (CS) education. That’s when I started teaching myself Swift and where my journey began. Since then I’ve built a series of apps that never saw the light of day and I’ve landed a job as an iOS developer. When making this Android application, I ran into a lot of issues because I got used to having certain features from Swift. This article is meant to give some clarity on how to do those things when migrating to Android, and hopefully give Android developers an insight in potential libraries to develop.</p><h3>The Codable Protocol</h3><p>We’ve only had it for a few months, but it saves so much time and makes my code so much more readable. I didn’t really get around to using it for a long time, but then a friend of mine told me about how awesome it is and now I try to use it whenever I can. In Swift, the code below is enough to decode a JSON string to an object, or even an array of objects.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0c8a9e096d1cce036a1a435c82180eca/href">https://medium.com/media/0c8a9e096d1cce036a1a435c82180eca/href</a></iframe><p>I haven’t found an equivalent to this in Kotlin/Java. Undoubtedly, there is a library that allows me to do something similar to this, but I prefer to use as few libraries as possible. In my Android app, I’m stuck to converting the JSON string to a Map and retrieving all properties from it line by line, increasing the workload significantly when changes to the backend occur.</p><h3>Grand Central Dispatch</h3><p>I didn’t know I could miss Grand Central Dispatch since I haven’t even scratched the surface of multithreading, but when I wanted to do a simple asynchronous network request on Android, I ran into a lot of trouble.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/efb7998f058a0e6627028ad9c8423707/href">https://medium.com/media/efb7998f058a0e6627028ad9c8423707/href</a></iframe><p>Having to subclass something for a task so trivial doesn’t feel “Swifty” at all, mainly because executing a task asynchronously in Swift is achieved simply by writing this block around it:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/f5676309cfa58e9f17b7a6946f1169ac/href">https://medium.com/media/f5676309cfa58e9f17b7a6946f1169ac/href</a></iframe><p>Now, <a href="https://github.com/hermanliang/AndroidGCD">I’m sure there’s a better way to do this</a>. My main annoyance hereis that I’d have to subclass AsyncTask for every different kind of request I want to make, whereas in Swift I can just execute whatever request I want in the above block and I’m set. It just feels like the logic approach.</p><h3>Interface Builder</h3><p>Love it or hate it, Xcode’s Interface Builder is a great tool for helping you visually layout views. Androids wildly different view structure (fragments? layouts? XML?) greatly contributes to this. I got the hang of building views in Xcode in under a week, and using constraints feels natural. If I want/need to add another layer of depth to this, I can use priorities. In Android Studio, the</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/257/1*PV31stfTw317GQfNVJohuQ.png" /><figcaption>Playing with the values in this window will open up a whole new world.</figcaption></figure><p>Activity Designer(?) uses some sort of relative values for constraints, where setting all layout margins to 8dp can result in them being different distances from the edge of the screen. And building the layouts in Android with XML is a blessing and a curse. While it’s great to layout your views with code, and it gives you a lot of freedom as to what you can do, I had the option to do so in Xcode while <em>also </em>using the great Interface Builder system.</p><h3>HTTP GET Requests</h3><p>In Swift, the first application I built used GET requests from an API, so naturally, I assumed doing the same in Android would be a piece of cake. Right? That’s where I was wrong. In Swift, implementing the basic functionality of making a GET request and retrieving the JSON file can easily be achieved by doing this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2f6cc61965ca4919d5c00d6b8a16f658/href">https://medium.com/media/2f6cc61965ca4919d5c00d6b8a16f658/href</a></iframe><p>However, In Android things are very different, daunting even. This is the Kotlin code I currently use to make a GET request and retrieving the JSON:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ed03f29693041f8e3d60226e9f7f4483/href">https://medium.com/media/ed03f29693041f8e3d60226e9f7f4483/href</a></iframe><p>…and this is just the subclass. When we actually want to perform the request, we still need to write this:</p><pre>HttpGetRequest<strong>()</strong>.execute<strong>(&quot;your api url here&quot;)</strong>.get<strong>()</strong></pre><p>This isn’t necessarily all that complicated, but it’s a lot more difficult than the approach taken in Swift.</p><blockquote>Sidenote: I’m sure there’s a better way to make these requests, be it with a library or otherwise. If you have any suggestions, please let me know.</blockquote><h3>That’s a wrap! For now.</h3><p>I’ve been working with Kotlin and Android Studio for about two weeks now, and I’ve learnt a lot about how to use these technologies. Every hour I spend working on this project, I learn new things, and I find out new things I like better about Android, or things I miss, coming from Swift. Give it a few weeks, and I’ll probably have enough ideas to write another article on this subject.</p><p><em>If you liked this article, check out the </em><a href="https://medium.com/@JillevdWeerd"><em>other articles I wrote</em></a><em>! If you’d like to get in touch, you can follow me on </em><a href="https://twitter.com/Jillevd_W"><em>Twitter</em></a><em> or shoot me a message on </em><a href="https://www.linkedin.com/in/jillevanderweerd/"><em>LinkedIn</em></a><em>. Also, if you need any help with anything iOS Programming related, you can </em><a href="https://discord.gg/Vt7fQRz"><em>join this Discord server</em></a><em> created for exactly that purpose :)</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f0e17582d2d3" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>