https://roundwallsoftware.com Zola en Wed, 05 Mar 2025 00:00:00 +0000 Deep Linking Now Available in Lumberyard Wed, 05 Mar 2025 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/url-support/ https://roundwallsoftware.com/blog/url-support/ <p>Lumberyard version 1.2.0 is available now in the <a href="https://apps.apple.com/us/app/lumberyard-maintenance-logs/id6446218913">App Store</a>.</p> <p>This update adds support for deep linking. That is a link that will quickly take you back to Lumberyard and jump you directly to an item in your list. This will only work on your own iPhone, but it will work in any app that recognizes links in text or allows for triggering a link when you tap a button. This allows for even more options to streamline your workflow.</p> <p>In the menu for each item in your list, you can now copy the unique identifier (UUID) for the item and use this to form a url.</p> <center> <img src="/uuid-screenshot.png" width="300" alt="Screenshot of the context menu showing the new button for copying a UUID."> </center> <p>With this UUID, you can create links directly to that item in your notes or TODO app or some such with the format:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>lumberyard://items/&lt;the-identifier-here&gt; </span></code></pre> Shortcuts in Lumberyard Mon, 03 Mar 2025 00:00:00 +0000 Samuel Goodwin https://roundwallsoftware.com/blog/lumberyard-shortcuts/ https://roundwallsoftware.com/blog/lumberyard-shortcuts/ <p>I almost completely forgot that <a href="https://apps.apple.com/us/app/lumberyard-maintenance-logs/id6446218913">Lumberyard</a> supports Shortcuts. This means you can automate interacting with the app and quickly use that information in other apps.</p> <p>As of now, Lumberyard supports the following actions:</p> <h3 id="last-time-chore-done">Last Time Chore Done</h3> <p>This one will ask which chore you're curious about, such as "clean the filter" on your dishwasher or "clear out the drain" on your roof, and tells you the last time it was done. You could generate a handy summary report with this or just quickly find out without needing to look around in Lumberyard.</p> <h3 id="mark-chore-as-done">Mark Chore as Done</h3> <p>With this action, you can mark any given chore as done. Maybe you want to trigger this with a Shortcute so that you do this every time you also do some other recording or work in another app.</p> <h3 id="urgent-chores">Urgent Chores</h3> <p>This action will give you a list of chores the app considers urgent. This means the task is either overdue or due today. You might pipe this directly into your TODO app's list for today or maybe just ask to see if there's something you need to check on.</p> <h2 id="siri">Siri</h2> <p>Any Shortcut you setup using actions from Lumberyard can also be called up with Siri. Without even looking at your phone you can record you've washed the inside of the dishwasher or make sure there's nothing you need to do today.</p> <p>You don't get full on Applescript on the phone, but this will get you quite a bit of the way. If you do something nifty in a shortcut, please do let me know, I'd love to hear about it!</p> Lumberyard's Widget Mon, 10 Feb 2025 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/widget/ https://roundwallsoftware.com/blog/widget/ <p><a href="/lumberyard">Lumberyard</a> is an app made to make keeping up with maintenance as convenient as an app can. You still need to check the filters, but Lumberyard will make sure you don't forget.</p> <p>One feature I put in to help is a widget. You can add it to your lock screen or even your home screen and easily know when there's urgent maintenance to do without even needing to open the app.</p> <center> <img src="/widget.png" width="300" alt="Screenshot showing the widget on a home screen with two tasks overdue."> </center> Dishwasher Maintenance Wed, 18 Dec 2024 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/dishwasher/ https://roundwallsoftware.com/blog/dishwasher/ <p>A a child, the dishwasher in our house almost never worked. It was instead my sister and my job to wash them by hand. The only time I remember it ever working was when we moved to a new house with a relatively new dishwasher. For a time it worked, but eventually that one also became just wasted space in our kitchen. It was our family's belief that dishwashers didn't work very well and so us children were made to wash just like my parents were made to wash before us.</p> <p>Only later as an adult would I learn that actually, we just never did any dishwasher maintenance, so of course they didn't work. It's not necessary to wash dishes before you load them or to avoid the dishwasher entirely, they just need routine cleaning and maintenance to keep working well. They're actually more water-efficient than washing by hand (<a href="https://www.water.org.uk/news-views-publications/news/vast-majority-brits-have-no-idea-how-much-water-they-use-each-day">UK Water</a>).</p> <p>For dishwashers there's a few things to do every so often:</p> <ul> <li>Clean spray arms, make sure spray holes are clear</li> <li>Clean inside, including the inside of the door</li> <li>Clear out drainage area</li> <li>Clear out limescale</li> <li>Clean out filter if there is one</li> </ul> <p>To make things easier, I've made an app called Lumberyard (an app to keep up with maintenance <em>logs</em> haha). With Lumberyard, you'll never need to wonder when the last time you cleaned the spray arms on the dishwasher or cleaned the filter on the washing machine. You'll still have to do the actual maintenance, your phone can't do that yet, but with helpful reminders and tracking your stuff can stay working the way it was meant to for much longer.</p> <p>This is only one example of the sort of thing you'd keep up with with Lumberyard. It's coming soon and it will help save money and stress by keeping up with your logs!</p> <p><img src="/blog/lumberyard.png" alt="Lumberyard Icon" /></p> Split Keyboards Wed, 06 Sep 2023 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/split-keyboards/ https://roundwallsoftware.com/blog/split-keyboards/ <p>If you do work on a computer for a living (which is a ton of us, even if we're not programmers). It could be fun to consider adopting an ergonomic/split keyboard. Traditional keyboards encourage a sideways angle of the wrist, and tension in the shoulders and full-size keyboards also encourage moving hands around to reach everything. These default keyboards also lead to contortions for key-combos, such as control-alt-delete on Windows and command-shift-3 on Macs. But, like many things in life, things don't have to be this way. Keyboards, for example can adopt whatever shape and size works for you, you don't need to work for it.</p> <p><img src="/blog/microsoft_keyboard.png" alt="Microsoft Natural Keyboard" /></p> <p>A gentler, easier step might be something like the Microsoft Natural Keyboard. The last time I was an employee, one of these helped save our lead developer who developed some pretty bad RSI while working at the company. They had such faith in the keyboard that they would happily and quickly buy one for you if you started to complain about any pain in your hands, wrists, or arms. You might find that this does the trick, wrists don't need to have an angle thanks to the keyboard effectively being split in half, each half adjusted to an angle people at Microsoft decided would be good. If this does the trick for you, you're all set. It's pretty easily available and doesn't require a ton of adjustment unless you don't touch-type.</p> <p>Now, I'm not personally a fan of this option because I like mechanical keyboards, but conveniently these days there are a ton of options for something like Microsoft's keyboard, but with mechanical keys.</p> Fancy Math In Swift Mon, 23 May 2022 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/math-in-swift/ https://roundwallsoftware.com/blog/math-in-swift/ <h1 id="fancy-math-in-swift">Fancy Math In Swift</h1> <p>Like Objective-C before it, Swift has access to the Accelerate framework. This is a framework made to use a computer's video card for what it does best: doing math with a bunch of floating point numbers. Commonly this is used to do all the math necessary for 3D graphics and such, but here's another handy use: signal processing.</p> <p>Music, or any audio really, can be analyzed by looking at the frequency of the vibrations that make the sound we hear. For example, instruments are often tuned to the musical note A, or a frequency of 440hz. If we took an arbitrary sound file and did the math to determine the frequencies present, we could programatically determine if that note A was present. Here is how to do that kind of math:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">import </span><span>Accelerate </span><span> </span><span style="color:#b48ead;">let</span><span> bufferData = UnsafeBufferPointer&lt;Float&gt;(buffer) </span><span style="color:#b48ead;">let</span><span> signal: [Float] = bufferData.map({ $</span><span style="color:#d08770;">0</span><span> }) </span></code></pre> <p>First we need to get an array of floating point values. Depending on how you're accessing the audio file (is it local? is it streaming? is it from the microphone?), there are several ways to obtain this. Perhaps a subject for another article.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">let</span><span> n = vDSP_Length(</span><span style="color:#d08770;">44100</span><span>) </span><span style="color:#b48ead;">let</span><span> log2n = vDSP_Length(log2(Float(n))) </span><span style="color:#b48ead;">guard let</span><span> fftSetUp = vDSP.FFT(log2n: log2n, radix: .radix2, ofType: DSPSplitComplex.</span><span style="color:#b48ead;">self</span><span>) </span><span style="color:#b48ead;">else</span><span> { </span><span> fatalError(</span><span style="color:#a3be8c;">&quot;Failed to setup FFT&quot;</span><span>) </span><span>} </span></code></pre> <p>Here we need to setup the FFT. FFT stands for "fast fourier transform", which is the mathematical process we'll use to detect frequencies in the audio signal. An important detail here is our value for <code>n</code>. Audio is typically recorded with a sampling frequency of 44.1khz, this <code>n</code> value needs to be that same number. If you don't want to get into the calculus that does this, just make sure this <code>n</code> matches the sample rate of the audio you're analyzing.</p> <p>Next we have some more setup to do before we can run the actual math. Because of the way swift works with pointers and the way the framework uses pointers this gets a little weird, but at least this part won't change regardless of what kind of audio you're analyzing.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">let</span><span> halfN = Int(n/</span><span style="color:#d08770;">2</span><span>) </span><span style="color:#b48ead;">var</span><span> forwardInputReal = [Float](repeating: </span><span style="color:#d08770;">0</span><span>, count: halfN) </span><span style="color:#b48ead;">var</span><span> forwardInputImag = [Float](repeating: </span><span style="color:#d08770;">0</span><span>, count: halfN) </span><span style="color:#b48ead;">var</span><span> forwardOutputReal = [Float](repeating: </span><span style="color:#d08770;">0</span><span>, count: halfN) </span><span style="color:#b48ead;">var</span><span> forwardOutputImag = [Float](repeating: </span><span style="color:#d08770;">0</span><span>, count: halfN) </span><span>forwardInputReal.withUnsafeMutableBufferPointer { forwardInputRealPtr </span><span style="color:#b48ead;">in </span><span> forwardInputImag.withUnsafeMutableBufferPointer { forwardInputImagPtr </span><span style="color:#b48ead;">in </span><span> forwardOutputReal.withUnsafeMutableBufferPointer { forwardOutputRealPtr </span><span style="color:#b48ead;">in </span><span> forwardOutputImag.withUnsafeMutableBufferPointer { forwardOutputImagPtr </span><span style="color:#b48ead;">in </span><span> </span><span> </span><span style="color:#65737e;">// Create a `DSPSplitComplex` to contain the signal. </span><span> </span><span style="color:#b48ead;">var</span><span> forwardInput = DSPSplitComplex(realp: forwardInputRealPtr.baseAddress!, imagp: forwardInputImagPtr.baseAddress!) </span><span> </span><span> </span><span style="color:#65737e;">// Convert the real values in `signal` to complex numbers. </span><span> signal.withUnsafeBytes { </span><span> vDSP.convert(interleavedComplexVector: [DSPComplex]($</span><span style="color:#d08770;">0</span><span>.bindMemory(to: DSPComplex.</span><span style="color:#b48ead;">self</span><span>)), toSplitComplexVector: &amp;forwardInput) </span><span> } </span><span> </span><span> </span><span style="color:#65737e;">// Create a `DSPSplitComplex` to receive the FFT result. </span><span> </span><span style="color:#b48ead;">var</span><span> forwardOutput = DSPSplitComplex(realp: forwardOutputRealPtr.baseAddress!, imagp: forwardOutputImagPtr.baseAddress!) </span><span> </span><span> </span><span style="color:#65737e;">// Perform the forward FFT. </span><span> fftSetUp.forward(input: forwardInput, output: &amp;forwardOutput) </span><span> } </span><span> } </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">var</span><span> autospectrum = [Float](unsafeUninitializedCapacity: halfN) { </span><span>autospectrumBuffer, initializedCount </span><span style="color:#b48ead;">in </span><span> </span><span> </span><span style="color:#65737e;">// The `vDSP_zaspec` function accumulates its output. Clear the </span><span> </span><span style="color:#65737e;">// uninitialized `autospectrumBuffer` before computing the spectrum. </span><span> vDSP.clear(&amp;autospectrumBuffer) </span><span> </span><span> forwardOutputReal.withUnsafeMutableBufferPointer { forwardOutputRealPtr </span><span style="color:#b48ead;">in </span><span> forwardOutputImag.withUnsafeMutableBufferPointer { forwardOutputImagPtr </span><span style="color:#b48ead;">in </span><span> </span><span> </span><span style="color:#b48ead;">var</span><span> frequencyDomain = DSPSplitComplex(realp: forwardOutputRealPtr.baseAddress!, imagp: forwardOutputImagPtr.baseAddress!) </span><span> vDSP_zaspec(&amp;frequencyDomain, autospectrumBuffer.baseAddress!, vDSP_Length(halfN)) </span><span> } </span><span> } </span><span>initializedCount = halfN </span><span>} </span><span> </span><span style="color:#b48ead;">let</span><span> value = vDSP.rootMeanSquare(autospectrum) </span><span>autospectrum = vDSP.divide(autospectrum, value) </span><span> </span></code></pre> <p>The final result sits in our variable <code>autospectrum</code> here at the end. It is an array of floats where each value represents the amount of signal (or power) detected at a given frequency. For example, if our source audio was a pure A note for tuning, we would see 0's in every position in the array except for the value at index 440. Keep in mind most tones aren't pure singular frequencies, so you may see smaller values in other spaces.</p> <p>Now your app can have the power to detect notes, approximate volume, all kinds of things. You could even modify this output (zero out values you don't want maybe) and run the FFT operation in reverse to make samples you could tell the system to play out of the speaker. This is how apps do things like filter out some of the background noise from recordings. This is also how some digital effects work for musical instruments like electrical guitar. Thanks to the Accelerate framework, we can do this fast enough to process the signal in real time. Fun stuff!</p> Removing Publicist Wed, 05 May 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/removing-publicist/ https://roundwallsoftware.com/blog/removing-publicist/ <p>Publicist first arrived in the app store on April 14, 2019. Now, a little more than two years later, I'm removing it.</p> <p>Publicist started as a free app with a subscription model to support it. The idea was that this would allow for users to have a free trial to see if the app worked for their workflow before they were expected to pay any money. This seemed like a nicer plan than expecting users to pay up front. However, even with a steady stream of downloads since version 1.0, in the end only about 10 people even activated the free trial.</p> <p>This year I released a new version which changed this free trial to a single purchase. I know many people are allergic to subscriptions, especially for software, so I thought a single one-time purchase to use the app might be more widely accepted. I was wrong and months later exactly 0 people have paid to unlock the app. Downloads continue at a steady pace, but not a single person thought the app was worth a one time purchase to use.</p> <p>For these reasons, Publicist is going away. If you happen to be one of the few people who thought the app was worth paying for and still wish to use it, I have made the code open-source on <a href="https://github.com/sgoodwin/Publicist">github</a> and if you write to me, I can help you set it up (or maybe just make you a special build).</p> <p>I originally wrote Publicist because this blog was running on <a href="https://ghost.org">Ghost</a>, but their business model and priorities have since moved far away from my own. They seem to be doing well, so I'm happy for them, but it gives even more reason for Publicist to be removed: individuals with blogs are not using Ghost. Now this blog is running on <a href="https://www.getzola.org">zola</a>, a static site generator. This means there's no API to interact with and nothing Publicist can do. Rather than drop my markdown files into the app, I just put them in the proper folder and Zola handles the rest. If you're one of the few people still trying to use Ghost (or even wordpress really) for their blog as an individual, you might like this option better as well.</p> <p>My thanks to those of you who have paid for software I've published and I hope to have new and interesting offerings for you in the future!</p> Testing Subscriptions Tue, 23 Mar 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/testing-subscriptions/ https://roundwallsoftware.com/blog/testing-subscriptions/ <p>If you are working on subscriptions through Apple's in-app-purchase system, here are some tips that might help you:</p> <h2 id="1-making-new-sandbox-users">1. Making New Sandbox Users</h2> <p>While Apple officially recommends using the trick where inserting the plus sign and any addition before the name part of your email (aka <a href="https://adayinthelifeof.nl/2014/02/04/email-subaddressing/">subaddressing</a>) to make new accounts, the form in App Store Connect will not let you do it. Apple's new security requirements affect even these sandbox accounts, so you <em>will</em> need to provide a valid email, but it can't be one used for an Apple ID before. So here's the trick, you can put in any email-looking gibberish in the form on the web page to make the account. When you try to use that account on your phone, it will prompt you to configure 2-factor authentication and <em>there</em> it will give you a chance to chose a different email address. Here you can use the "+" to give it a valid email by subaddressing your own email address. I don't know why this works, but the web page does not let you do it. Especially since Apple endorses this practice. I do know that now this won't suck up more hours of your day!</p> <h2 id="2-accessing-the-receipt-file">2. Accessing the Receipt File</h2> <p>You may want to have the receipt file to use for your own tests or to send off to someone on your backend team. If you're working on an iOS app, this isn't super convenient to access without some tricks, but here is some code that will let you get to the file without any additional setup.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">let</span><span> shareController = UIActivityViewController(activityItems: [ </span><span> Bundle.main.appStoreReceiptURL as Any </span><span>], applicationActivities: </span><span style="color:#d08770;">nil</span><span>) </span><span>present(shareController, animated: </span><span style="color:#b48ead;">true</span><span>, completion: </span><span style="color:#d08770;">nil</span><span>) </span></code></pre> <p>Have this code run on launch or when some button in your app is tapped and you'll have a share sheet with options like AirDrop to pass the file off to whoever needs it. This also works when you want to send off database files and such in your app.</p> <h2 id="3-expiring-subscriptions">3. Expiring Subscriptions</h2> <p>Even if your subscriptions are auto-renewing, sandbox subscriptions will expire. They will renew, but only a limited number of times. Sandbox subscriptions also operate on an accelerated time-scale, so a year's worth of subscription will only take an hour. Once a subscription expires, you cannot just buy a new subscription from inside the app. Your users will need to go into the App Store App app on their phones and re-subscribe from the subscription interface there. Your sandbox tester account, however, behaves differently. You'll need to go into the Settings app, find the entry for the App Store, and look for the sandbox-specific bits there. <a href="https://developer.apple.com/documentation/storekit/in-app_purchase/testing_in-app_purchases_with_sandbox">Apple's Documentation</a> covers this process fairly well.</p> New Publicist Mon, 15 Mar 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/new-publicist/ https://roundwallsoftware.com/blog/new-publicist/ <p>A new version of Publicist is now live in the <a href="https://apps.apple.com/us/app/publicist/id1449350225?mt=12">app store</a>! This version includes a few big changes I wanted to tell you about.</p> <h2 id="1-removing-subscriptions">1. Removing Subscriptions</h2> <p>Since the Mac App Store does not allow for upgrade pricing, I thought subscriptions might be the best fit for the app. This way I could provide a free trial of the app's full functionality and give customers a chance to really test the app. Customers could pay once per year for an app that stayed up to date and I could have fairly predictable revenue which would make it possible to keep the app up to date. Everyone wins or so I thought. The reality is, a very small fraction of the downloads Publicist receives results in someone even starting the one month free trial. Instead I've changed things to a one-time purchase to unlock the app. Until it is unlocked, users won't be able to actually publish their posts, but I'm hoping that will give them a chance to see enough of the app to decide to unlock it. For those of you who did subscribe, the app will continue to work for you and you will no longer need to pay for your subscription. I really appreciate your support and you will get full use of the app for as long as it lives.</p> <h2 id="2-swiftui">2. SwiftUI!</h2> <p>Apple has provided a new UI framework that allows me to produce the app with less room for interface-related bugs as well as get close to an iOS version of Publicist. I've re-written the app's interface to use this which gives the app a more "Big Sur" feel. I hope you enjoy it!</p> <p>Thank you again for everyone who has supported Roundwall Software! If you are enjoying Publicist, please feel free to tell your friends and leave a review in the Mac App Store.</p> 2021 Formatter Update Mon, 01 Feb 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/2021-formatter-update/ https://roundwallsoftware.com/blog/2021-formatter-update/ <p>This weekend I published a new version of <a href="/formatter/">Formatter</a>. A recent version of Xcode caused the extension to stop working, but it's ok now. If you're using Xcode 12.2 or higher, you'll want to download this update to continue enjoying having a handy JSON-format button while you're working on your iOS and Mac projects. Feel free to tell your friends and co-workers how handy Formatter is and thank you for being a customer!</p> Progress Expectations Thu, 21 Jan 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/progress-expectations/ https://roundwallsoftware.com/blog/progress-expectations/ <p>Expectations are pretty common when you're writing unit tests. Generally an expectation represents an event you want to wait for in your test. You can tell the test to wait until that expectation is fulfilled.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testSomethingWorks() { </span><span> </span><span style="color:#b48ead;">let</span><span> itHappened = expectation(description: </span><span style="color:#a3be8c;">&quot;The event should happen&quot;</span><span>) </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> testSubject = TestSubject() </span><span> testSubject.makeItHappen { </span><span> itHappened.fulfill() </span><span> } </span><span> waitForExpectations(timeout: </span><span style="color:#d08770;">1</span><span>.</span><span style="color:#d08770;">0</span><span>) </span><span>} </span></code></pre> <p><img src="/content/images/2021/01/1-IMG_0001-2.jpeg" alt="1-IMG_0001" title="1-IMG_0001" /> Tests for iOS and Mac generally look like this. You setup an expectation, setup whatever part of your code you're trying to test that does something asynchronous, and then tell the test to wait until the expectation is fulfilled. This is probably the most common use case, so you'll end up seeing it often. Sometimes this is not enough and we need to dig for other ways to fulfill expectations. One I found especially handy was with <code>keyValueObservingExpectation(for:keyPath:handler:)</code>. This function creates an exception that is fulfilled based on observed values from your test subject. I use it to make expectations that fulfill based on Progress objects.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>progressExpectation(_ progress: Progress) { </span><span> keyValueObservingExpectation(</span><span style="color:#b48ead;">for</span><span>: progress, keyPath: </span><span style="color:#a3be8c;">&quot;completedUnitCount&quot;</span><span>) { (value, info) -&gt; Bool </span><span style="color:#b48ead;">in </span><span> </span><span style="color:#b48ead;">let</span><span> current = value as! Progress </span><span> </span><span style="color:#b48ead;">return</span><span> current.totalUnitCount == current.completedUnitCount </span><span> } </span><span>} </span></code></pre> <p>This way I can make tests wait for operations to complete even if they don't have completion handlers I can use to fulfill the expectation. You could also use something like this to wait for partial completion of operations rather than total completion. A handy thing to keep in mind for your next tests!</p> New Feature Coming Thu, 14 Jan 2021 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/new-feature-coming/ https://roundwallsoftware.com/blog/new-feature-coming/ <p>Hello Friends! You might be wondering about Publicist as there hasn't been an update for some time. I want you all to know that Publicist is not dead. This last year has been rough with lockdowns and reduced business, but I am working an an update. This new version will have improved insides, a nicer look to fit in with Big Sur, and a new feature. To help cope with the lockdowns, I've started to get into being more serious about taking photos. This caused me realize how important captions are for images and I'm a bit embarassed that the current shipping version does not give a way to specify them. A remedy to this problem is on the way! <img src="/content/images/2021/01/An-example-of-photos-I-ve-been-taking.jpeg" alt="An example of photos I&#39;ve been taking" title="An example of photos I&#39;ve been taking" /></p> A Dream Client Thu, 22 Oct 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/a-dream-client/ https://roundwallsoftware.com/blog/a-dream-client/ <p>I'm happy with the clients I've worked with in the last years. Most projects have been interesting and filled with nice people. I'd like to take it a step further if possible and share more of what it's like to be a consulting software engineer.</p> <p>Most of us have to sign NDA's and such to work with a client and in the end have nothing to show people. We're unable to open source any of the code we wrote to share with the world and in some cases aren't even able to share that we worked with the client at all. This isn't necessarily bad, we're getting paid to do job and that's fine.</p> <p>Previously I worked the startup <a href="https://oya.sg">OYA</a> which is unfortunately now shut down. This client was a first for me because I didn't just get to share some of the code I wrote for them, but we shared the code for the <a href="https://github.com/ostelco/ostelco-ios-client">entire app</a>. So now, even though the company has closed its doors, I can still share the work we did and everyone can see a production iOS app written in Swift using GraphQL and trying to make use of SwiftUI in parts.</p> <p>I'd like to take this even further on a new client, with their permission, of course. Livestreaming coding and pair programming sessions and maybe even some meetings. To many people, software engineering is either a complete unknown or has been glorified unrealistically. I'd like to shatter that with real depictions of real projects. This wouldn't need to be every hour of every day. I think an a day a week would be plenty.</p> <p>This could potentially even be good for the client. The only startup that I know of that has made use of twitch and live-streaming to build attention for their product is Minecraft. There must be others, but it does not appear to be very common at all. I think it would also help recruiting new engineers because they'll have a chance to see how the team works in a way no interview process can show. If this describes your company, definitely get in touch!</p> Obscure APIs Wed, 21 Oct 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/obscure-apis/ https://roundwallsoftware.com/blog/obscure-apis/ <p>Here are 5 APIs provided by Apple that you may not know about:</p> <h1 id="1-searchkit">1. SearchKit</h1> <p>SearchKit is a C-level API that provides quick full-text search for your Mac apps. I've seen many questions about how to enable the full-text search extension in SQLite to use in CoreData or how to install and setup some third party library to enable search in their apps. None of this is necessary because Apple provided SearchKit back in MacOS 10.3. While it is a C API, it's a very specific API so I only find myself needing about 4 functions to enable search in my apps.</p> <h1 id="2-language-detection">2. Language Detection</h1> <p>Since iOS 3 and MacOS 10.5 we have been able to detect the language used in text using <code>CFStringTokenizerCopyBestStringLanguage</code>. This is much more accurate than looking in your strings for Arabic or Japanese characters, for example. This comes in handy when you want to do content-aware text alignment, like I mention in my article about <a href="/rtl-considerations/">RTL Support</a>. This is one I only recently discovered even though it has been in CoreFoundation for almost as long as I have been working on Apple platforms!</p> <p><strong>Update</strong> My friend Paul has pointed out to me there's an even nicer way to do this with the <a href="https://developer.apple.com/documentation/naturallanguage/identifying_the_language_in_text">Natural Language framework</a>.</p> <h1 id="3-asynchronous-fetching">3. Asynchronous Fetching</h1> <p>I continue to hear complaints about CoreData in apps related to performance. I find that many of these people haven't heard about the improvements to CoreData made in the last 10 years to improve this. One of these additions is <code>NSAsynchronousFetchRequest</code>. For elaborate models and large piles of data, some of your fetches might be entirely too slow to do synchronously, but since iOS 8 and MacOS 10.10 you don't need to! With an asynchronous fetch, your app can remain responsive while you wait for the data, much like your app already waits for asynchronous network requests.</p> <h1 id="4-progress">4. Progress</h1> <p>Progress was added in iOS 7 and MacOS 10.9. I only realized it existed and was useful when I started work on my app, <a href="/publicist/">Publicist</a>. Progress is an object made for keeping track of the state of long running tasks. They can be linked to progress indicators in UIKit and AppKit and in SwiftUI they can just be directly handed to a <code>ProgressView</code>. They're already adopted by framework items like <code>OperationQueue</code> and <code>URLSessionTask</code> to show progress to the user in a simpler fashion. I've worked on apps before that wanted to show download progress and computed this manually, but now that's not necessary because you can just get the progress object from a url task and report it to the user (or monitor it yourself).</p> <h1 id="5-custom-bindings">5. Custom Bindings</h1> <p>SwiftUI provides the new <code>@Binding</code> property wrapper to link data displayed in views to their source of truth. The system knows to update the displayed view when data changes and the view using the wrapper is able to change the data as well without owning it. I just found recently you can make your own <code>Binding</code> objects and you don't need to only send property-wrapper variables to views expecting a binding. For example, let's say you have an array of names in your data model, but want to display them in one single text field. You cannot just give your array to a text field, but you <em>can</em> provide a custom binding to do the translation work for you like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>TextField(</span><span style="color:#a3be8c;">&quot;Names&quot;</span><span>, text: Binding(</span><span style="color:#b48ead;">get</span><span>: { </span><span> names.joined(separator: </span><span style="color:#a3be8c;">&quot;, &quot;</span><span>) </span><span>}, </span><span style="color:#b48ead;">set</span><span>: { newValue </span><span style="color:#b48ead;">in </span><span> names = newValue.components(separatedBy: </span><span style="color:#a3be8c;">&quot;, &quot;</span><span>) </span><span>}) </span></code></pre> <p>This will join your array of names into one string to display in the text field and also translate whatever the user types back into an array of names in real time. I nearly wrote quite a bit of code to do this before I found this method!</p> Buttons Over Views Tue, 20 Oct 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/buttons-over-views/ https://roundwallsoftware.com/blog/buttons-over-views/ <p>One enemy of accessible apps is the use of views instead of buttons. A button, <code>UIButton</code> or <code>NSButton</code>, handles taps and clicks from users. For users who have trouble doing that, iOS and MacOS have accessibility support to enable those users to get their work done. Many developers (me included before) have thought to themselves, "Oh hey, there's this tap gesture recognizer, I can just add this to a view rather than user a button!"</p> <p>At first glance, this seems to do the same thing: if a user taps or clicks on the view, the gesture recognizer will execute your code. However, this is only one part of a button's job replaced. The accessibility system doesn't know about your fake button! If a user is blind, for example, and tries to use the feature hidden behind this gesture recognizer, they are out of luck. They won't hear the hints VoiceOver supplies, they won't be able to ask their Mac to click on the view with <a href="https://support.apple.com/en-us/HT210539">Voice Control</a>.</p> <p>It can be easy to only consider the happy path for our code. This includes the able-bodied-user path. As many complaints as we may have about them, engineers at Apple worked hard for a long time on UIKit and AppKit. They put together such a comprehensive system to make it easy for us to help out users with low/no vision, low mobility, etc. Wouldn't it be a shame to throw out that work because we were clever and thought we could replace the button?</p> RTL Considerations Mon, 19 Oct 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/rtl-considerations/ https://roundwallsoftware.com/blog/rtl-considerations/ <p>Here are some things to keep in mind so that you app can handle right-to-left languages like Arabic and Hebrew.</p> <h1 id="guidelines">Guidelines</h1> <p>While Apple provides information about supporting RTL (right to left), I generally refer to <a href="https://developer.mozilla.org/en-US/docs/Mozilla/Developer_guide/RTL_Guidelines">Mozilla's RTL Guidelines</a> and <a href="https://material.io/design/usability/bidirectionality.html">Google's</a> for information about what should flip for RTL and what should not.</p> <h1 id="rtl-images">RTL Images</h1> <p>Mirroring images which are packaged with the app are handled automatically by the system. All we need to do is mark the images in the asset catalog as left-to-right versions and the system will take care of flipping them for RTL.</p> <p>The default value for Direction is <code>Fixed</code> which is perfect for images that do not need to be flipped such as smiley-faces and images of clocks. Select <code>Left to Right</code> for images such as navigation arrows which should flip in RTL.</p> <h1 id="trailing-constraints">Trailing Constraints</h1> <p>Views generally need trailing constraints specified in Autolayout and not only leading constraints. By specifying trailing constraints even when they’re seemingly not necessary, you can make sure that in RTL mode, elements will layout in a nice way.</p> <h1 id="textcontenttype">textContentType</h1> <p>Text fields should have their <code>textContentType</code> set according to the content you expect in the field. This will help iOS to flip the proper fields as well as offer better auto-complete suggestions. Phone numbers, for example, should not be flipped in RTL.</p> <h1 id="uicollectionview-layouts">UICollectionView Layouts</h1> <p>Horizontally-scrolling collection view layouts usually will need to be able to scroll in the opposite direction. This can be handled automatically by adding one override to your layout subclass:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">override var</span><span> flipsHorizontallyInOppositeLayoutDirection: Bool { </span><span> </span><span style="color:#b48ead;">return true </span><span>} </span></code></pre> <p>If you do not allow the layout to flip, the page indicator below likely will. If you mean for the collection view not to flip, you'll need to prevent the page indicator from flipping to match to avoid confusion.</p> <h1 id="directional-insets">Directional Insets</h1> <p>Where possible, use <code>NSDirectionalEdgeInsets</code> instead of <code>UIEdgeInsets</code>. If you are supporting a version of iOS so old you cannot use this (directional insets were added in iOS 11), you'll need to write your own convenience function to flip your right and left insets based on the component's <code>effectiveUserInterfaceLayoutDirection</code>.</p> <h1 id="un-mirrored-ui-elements">Un-mirrored UI Elements</h1> <p>Some elements, such as phone numbers, should not be mirrored. When creating UI elements like this, remember to use right and left anchors explicitly in Autolayout. This includes in views where the element is used. Consider the margins and constraints used to position the element on the screen, if they are based on neighboring leading or trailing anchors, the element will not position correctly in its parent-view.</p> <h1 id="text-alignment">Text Alignment</h1> <p>For labels and such which should flip for RTL languages, using <code>textAlignment = .left</code> will not display like you'd expect. You can instead chose <code>.natural</code> which will align text to the left for LTR languages and to the right for RTL languages. This is the default for UILabels, so in most case you won't need to write code to change it at all.</p> <h1 id="manual-frame-math">Manual Frame Math</h1> <p>Sometimes, for performance reasons for example, you may want to avoid using Autolayout altogether and instead use frames to position elements manually. In this case, it's important to factor in the current value of the <code>UIView</code> property <code>effectiveUserInterfaceLayoutDirection</code>. A function like this could be used to adjust their frame math based on direction:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>directionAdjustedFrame(leading: CGFloat, y: CGFloat, width: CGFloat, height: CGFloat) -&gt; CGRect { </span><span> </span><span style="color:#b48ead;">if</span><span> effectiveUserInterfaceLayoutDirection == .leftToRight { </span><span> </span><span style="color:#b48ead;">return</span><span> CGRect( </span><span> x: leading, </span><span> y: y, </span><span> width: width, </span><span> height: height </span><span> ) </span><span> } </span><span style="color:#b48ead;">else</span><span> { </span><span> </span><span style="color:#b48ead;">return</span><span> CGRect( </span><span> x: bounds.width - width - leading, </span><span> y: y, </span><span> width: width, </span><span> height: height </span><span> ) </span><span> } </span><span>} </span></code></pre> <h1 id="language-based-detection">Language-Based Detection</h1> <p>For some content, such as chat conversations, you may want the text alignment to match the language of the content instead of the alignment the system would chose to match the system language. In this case, you can use functions like <code>CFStringTokenizerCopyBestStringLanguage</code> to attempt to detect the language of the content to decide alignment. This is not 100% accurate, but they can offer a good-enough solution without dramatically impacting performance.</p> Swift Package Testing Tue, 23 Jun 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/swift-package-testing/ https://roundwallsoftware.com/blog/swift-package-testing/ <p>Swift 5.3 brings new improvements to the package manager so it finally supports including resources in your packages. You can read the proposal on Swift Evolution <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0278-package-manager-localized-resources.md">here</a> if you're into that.</p> <p>This has been the only reason I haven't moved libraries I maintain, such as MobiledocKit, to Swift Package Manager. In my tests, I often use example files to test that they are parsed correctly by the library and this was not easily done before. Now it is except there's one small caveat: the generated function <code>Bundle.module</code> will not work in tests. The function SPM generates looks about like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">import class </span><span>Foundation.Bundle </span><span> </span><span style="color:#b48ead;">extension</span><span> Foundation.Bundle { </span><span> </span><span style="color:#b48ead;">static var</span><span> module: Bundle = { </span><span> </span><span style="color:#b48ead;">let</span><span> bundlePath = Bundle.main.bundlePath + </span><span style="color:#a3be8c;">&quot;/&quot; </span><span>+ </span><span style="color:#a3be8c;">&quot;MobiledocKit_MobiledocKitTests.bundle&quot; </span><span> </span><span style="color:#b48ead;">guard let</span><span> bundle = Bundle(path: bundlePath) </span><span style="color:#b48ead;">else</span><span> { </span><span> fatalError(</span><span style="color:#a3be8c;">&quot;could not load resource bundle: </span><span>\(bundlePath)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> } </span><span> </span><span style="color:#b48ead;">return</span><span> bundle </span><span> }() </span><span>} </span></code></pre> <p>This does not work because the "main" bundle's path is nowhere near where SPM places the test bundle. If you try to use this in your tests, you'll get an error that starts like so:</p> <pre data-lang="plaintext" style="background-color:#2b303b;color:#c0c5ce;" class="language-plaintext "><code class="language-plaintext" data-lang="plaintext"><span>swift package &quot;Fatal error: could not load resource bundle:&quot; </span></code></pre> <p>That's ok though, because we can fix the problem by making our own convenience method to find the right bundle:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">extension</span><span> Bundle { </span><span> </span><span style="color:#b48ead;">static let</span><span> testBundle: Bundle = { </span><span> </span><span style="color:#b48ead;">let</span><span> baseBundle = Bundle(</span><span style="color:#b48ead;">for</span><span>: &lt;TEST CLASS NAME GOES HERE&gt;.classForCoder()) </span><span> </span><span style="color:#b48ead;">return</span><span> Bundle(path: baseBundle.bundlePath + </span><span style="color:#a3be8c;">&quot;/../&lt;NAME OF THE GENERATE BUNDLE GOES HERE&gt;.bundle&quot;</span><span>)! </span><span> }() </span><span>} </span></code></pre> <p>If you use this method instead, your tests will be able to find the resources you listed in your <code>Package.swift</code> file and you can go about your day testing as you please.</p> New Podcast Wed, 20 May 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/new-podcast/ https://roundwallsoftware.com/blog/new-podcast/ <p>I started a podcast to tell the story of Roundwall Software and my efforts to become an independent developer. Episodes will come weekly on Thursdays and I'm keeping them fairly short, never more than 15 minutes each. This is not a sugar-coated fluff piece and I'll be talking about nearly going bankrupt, failed apps, mistakes with clients, and the like.</p> <p>It should be visible on whatever you use for podcasts, just search for "Lipslide". If this is something you might be into, I'd love it if you gave it a listen.</p> <p><a href="https://podcasts.apple.com/nl/podcast/lipslide/id1513521869">Here is a link to Lipslide on Apple Podcasts</a></p> Release Day With Migration Sat, 21 Mar 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/release-day-with-migration/ https://roundwallsoftware.com/blog/release-day-with-migration/ <p>Publicist version 1.4 is now live! You can find it in the <a href="https://apps.apple.com/us/app/publicist/id1449350225?mt=12">App Store</a>. As usual, it is free to use with a yearly subscription to unlock posting to Ghost and Wordpress.</p> <p>This version adds two major features:</p> <h2 id="migration">Migration</h2> <p>Publicist can now move all your blog posts over to a new blog. This way you can avoid working with import/export scripts and you should have a more consistent experience. (My own blog still has errors in old posts from various migrations.)</p> <h2 id="share-extension">Share Extension</h2> <p>With our new Share Extension, you'll be able to quickly publish from the Finder or your text editor. Any markdown file can now be quickly sent off to your blog without minimal disruption to your flow.</p> Core Data Errors Mon, 09 Mar 2020 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/core-data-errors/ https://roundwallsoftware.com/blog/core-data-errors/ <p>If you're trying to use Core Data in your project, especially if you're testing, you might find some obscure errors. Here are a few and how to fix them:</p> <h2 id="multiple-entity-descriptions">Multiple Entity Descriptions:</h2> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>+[BlogEngine.Account entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass[error] warning: Multiple NSEntityDescriptions claim the NSManagedObject subclass &#39;BlogEngine.Account&#39; so +entity is unable to disambiguate. </span></code></pre> <p>This happens when you have more than one <code>NSPersistentStoreContainer</code> working in an app. In your app, it might be because different parts of the app are trying to spin up their own Core Data stacks rather than passing that information between them. This is an architecture problem.</p> <p>In tests this can more easily happen. Some of your tests may need to create a Core Data stack. This also needs to be shared, especially if you enable parallel testing (which you should, it's good for speeding up your tests and uncovering concurrency bugs). I fixed this issue in my own tests by introducing a single container configured for in-memory storage that all test cases can access.</p> <p>Even if your tests all use the same container, if your tests are using your app for a test host, you will see the same error again. This is because the your app has it's own container open at the same time as your tests.</p> <h2 id="desired-type-and-given-type">Desired Type and Given Type</h2> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span> - Unacceptable type of value for to-one relationship: property = &quot;account&quot;; desired type = BlogEngine.Account; given type = BlogEngine.Account; (followed by crash logs) </span></code></pre> <p>Especially if you are using Swift for your Core Data code, the compiler helps to make sure you're using the right types, right? Except the error there says the expected type as <code>BlogEngine.Account</code>, but also the given type was <code>BlogEngine.Account</code>. They look the same, that shouldn't be an issue. This can be because of the same issue as above. An Account from one container and an Account from another container are different, even if they have the same name. Make sure you are using Core Data objects from the same context and same stack when assigning relationships.</p> <p>Now go forth and use Core Data for good!</p> Publicist 1.3.0 Thu, 19 Dec 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/publicist-1-3-0/ https://roundwallsoftware.com/blog/publicist-1-3-0/ <p>Publicist 1.3.0 is live finally!</p> <p>There was quite some delay due to my fall holiday, traveling to visit clients, and a mysterious crashing bug, but that's all worked out now. Thank you for your patience.</p> <p>This new version mostly fixes bugs like issues saving certain kinds of blog posts, incorrectly displaying extra nonsense on previews of Wordpress posts, and handling YAML front-matter when it shows up in the middle of your article.</p> <p>I also included <a href="https://red-sweater.com/blog/3508/app-movement-monitoring">Daniel Jalkut's</a> solution for preventing issues for users who decide to move the app on their computer.</p> <p>With this version finally out the door, I can get back to work on the next major feature coming: migration.</p> Peer Lab Closing Tue, 20 Aug 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/peer-lab-closing/ https://roundwallsoftware.com/blog/peer-lab-closing/ <p>The first <a href="https://www.meetup.com/Appsterdam/events/130341492/">Peer Lab</a> meeting was on July 20th, 2013. Just over 6 years ago. Peer Lab in Amsterdam is finished.</p> <p>It was originaly created as a non-alchoholic developer gathering where we could do more than just talk and argue about tabs vs spaces. I wanted an event where we actually worked on things and benefit from either the direct help of our peers or at least the energy of everyone working on their projects. I called it Peer Lab because I wanted to make it clear that it wasn't simply "Ask Samuel Time". It was meant to be an event for developers in Appsterdam to support each other and encourage each other.</p> <p>My friend Ash Furrow lived in Amsterdam for some time after Peer Lab started. When he left for New York, he took the idea with him and hosted in own at his <a href="https://www.meetup.com/CocoaPods-NYC/">employer's office</a>. He even setup a <a href="https://peerlab.community">website</a> where people can find other Peer Labs running in cities around the world. It's cool to see other people running similar events in their cities. I think every city would benefit from having one.</p> <p>In that time, I've met many people and they have found the help they needed. Some have gone from asking questions about their own first projects to becoming gainfully employed. Others have finished the app they dreamed of making. All thanks to support from their peers.</p> <p>Now after 6 years though, I'm shutting down the Amsterdam Peer Lab. It was meant to be a sustainable event that didn't take so much time and effort that I could not keep it going week after week, but I think even then it cannot go on forever.</p> Publicist Sat, 13 Jul 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/publicist/ https://roundwallsoftware.com/blog/publicist/ <p><img src="/blog/Screen-Shot-2019-06-22-at-10.08.07-1.png" alt="Screenshot of Publicist&#39;s main window" /> <img src="/blog/AppIcon_Compact_Dark-3.png" alt="Publicist&#39;s Icon" /> Publicist currently supports posting, including images, to:</p> <ul> <li>WordPress</li> <li>Ghost<center>It's available now! <br/><a href="https://apps.apple.com/us/app/publicist/id1449350225?mt=12"> <img src=/content/images/2017/01/Download_on_the_App_Store_Badge_US-UK_135x40.svg width="300" height="100" alt="Available on the app store"/></a></center></li> </ul> Publicist 1.2 Wed, 03 Jul 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/publicist-1-2/ https://roundwallsoftware.com/blog/publicist-1-2/ Markdown Support In WordPress Sat, 15 Jun 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/markdown-support-in-wordpress/ https://roundwallsoftware.com/blog/markdown-support-in-wordpress/ <p>If you are using a self-hosted installation of WordPress, you may notice your blog doesn't seem to understand markdown. Unless you really enjoy only using plain text or writing HTML yourself, here are ways I have found to remedy the situation:</p> <h1 id="option-1-install-jetpack">Option 1: Install Jetpack</h1> <p>Jetpack is the WordPress company's plugin to add all kinds of behavior to your blog. It makes your blog behave more like one hosted by wordpress.com directly. One of the many things it provides is markdown support. If this sounds like something you might like, you can find it <a href="https://wordpress.org/plugins/jetpack/">here</a>. For my own WordPress blog I run, I did not want all the other behavior and so I chose option 2.</p> <h1 id="option-2-install-wp-markdown">Option 2: Install WP-Markdown</h1> <p>WP-Markdown is a much smaller plugin which only provides markdown support. After you activate the plugin, clicking a checkbox in settings will allow Publicist (and any other app) to work as expected. I chose this version because it seemed a better fit for my blog. You can find information about it <a href="https://wordpress.org/plugins/wp-markdown/">here</a>.</p> <p>No matter which option you pick, you can get your blog fixed up quickly and get back to what's important: actually writing. Good luck!</p> The Roundwall Guide To Client Work Wed, 12 Jun 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/the-roundwall-guide-to-client-work/ https://roundwallsoftware.com/blog/the-roundwall-guide-to-client-work/ <p>Things to consider if you want to do client work.</p> <p>So you want to consult or you are consulting (I say consult because "freelance" has such a casual and kind of negative/lazy connotation in some places). Traveling down this path means you've decided to trade a more "permanent" position in a company where you can mostly worry about programming with your team for a permanent position in your own company where you now have to consider the business, the finances, the organization, and still program with the team. In exchange for more things to consider, you gain control over these things. Here is a collection of things I think you should keep in mind to help keep your company afloat and make sure your experience is as pleasant as possible.</p> <h1 id="rates">Rates</h1> <p>Not everyone enjoys talking about and negotiating rates, but it's one of the most important parts of doing client work. You are running a business and it's your job to keep that business running.</p> <p>As much as possible, I avoid hourly rates. They don't fit the nature of the work we do and the last thing I want to do is have a discussion with a client where we argue about exactly how many hours something took. Fixed-price is also a terrible idea. When is the last time you were able to predict exactly how long a feature (much less a whole app) would take to finish? If you agree to a fixed-price rate, too small a number could crush you as you spend weeks beyond your estimate essentially working for free. Too large an estimate and your client might complain and feel cheated. At Roundwall Software, I take one client at a time and schedule each client in terms of weeks. For the weeks they pay me for, they have my attention and I work on their project each day.</p> <p>You might find resistance from clients who would prefer to work on a fixed-price or even hourly basis. In the 8 years Roundwall Software has existed, only one or two clients could not be convinced after I gave them an explanation why that's not ok for me.</p> <p>Another thing to consider is that over time you should raise your rates. With each passing year, inflation makes the same amount of money worth less. With each completed project, your experience and value increases. You'll need to increase your rates to adjust for these two changes. In theory you might creep them up, say raise your rates with each project by 3% or so, but my own rate-changes are less organized. Don't let inflation and time hurt your business.</p> <h1 id="example-work">Example Work</h1> <p>The people who will be hiring you for help will come from a range of backgrounds and have a range of technical experience of their own. Each person is going to need to see something from you that assures them you know what you are talking about. Just as it can be risky or even scary to hire a random plumber just by looking for a phone number in the phone book, few people will want to hire you from nothing.</p> <p>To help attract customers, it's good to build up a collection of example work. Probably your last client/employer won't let you freely share all the code you wrote on your last project, so you'll need to think of a way to make up for this.</p> <p>For some clients, simply hearing that you've worked with other people they know or have heard of might be enough. For others, reading blog posts you've written explaining a technical thing or seeing a talk you've given about a technical thing will do the trick. Building up this kind of work is good for most people, even employees. As a consultant, you'll have time in between clients (unless you're swamped which is great). With that time, you should be doing these things.</p> <p>I ignored this advice for the first years of my career and did not write and did not make any apps or libraries or example work on my own. This was fine just after college when I would rather be skating than working anyway, but in the long run I slowed myself down. Business became much better as I wrote, spoke, and published work people could see.</p> <h1 id="client-expectations">Client Expectations</h1> <p>Client projects can generally be grouped into two categories: one where you (and people you may have hired) handle the project completely and one where you join the client's existing team to help them with their project.</p> <p>In the case of these "solo" projects, you'll want to make sure the lines of communication stay open. It can be tempting to retreat into a cave and be silent until you emerge with a working app to deliver to the client. This makes clients nervous and can often be bad for you. For projects I run like this, I provide the client with a build of the current state of the app at least once per week, if not more. Frequent communication and frequent updates give the client an idea of where the project is going and invites the client into the process. This way there's less surprises when the project takes longer than expected and can often prevent projects from going too long. It's important to remember that if your client fully understood and knew exactly what to do, they wouldn't have reason to call you. It's up to you to help them understand, not just to produce code.</p> <p>For other projects, you'll be joining existing teams, maybe as the only developer for your platform, to help them finish a project. In this case, you'll need to participate in their planning meetings, speak up when decisions aren't good for your platform or the users, and make sure they don't forget you exist.</p> <h1 id="remote-setup">Remote Setup</h1> <p>One of the joys of consulting is most of your work can be done at home or at your own private office. Since you'll be communicating with your client through things like Skype or Google Hangouts, it's worth taking some time to make yourself look better by setting up a better microphone and possibly a better camera. The built-in microphone on your computer is a hilarious joke and will make you sound like you're in a wind tunnel under a major highway. Even a 40€ USB mic would make you sound much better. I personally have a fancy audio interface and just got a fancier mic that makes me look like a radio personality, but going that far is not necessarily needed. Have something better than the built-in mic for as many calls as possible.</p> <p>The built in cameras on computers these are old. For less than 100€, you could replace this with a 1080p USB web cam and look even nicer when you're talking to your client. Even if your client does not explicitly acknowledge your efforts to look and sound better on calls, their perception of you will be influenced and you'll be seen as more professional. Considering the kind of money even a fairly junior engineer can charge, I think this is a great investment for everyone who plans to work remotely ever.</p> <h1 id="good-luck">Good Luck</h1> <p>This isn't necessarily everything you need to consider, but these are the things I think many people are not considering and should. If you're getting started, talk to other people who run a business and see what they have to say as well. Feel free to email me as well if you have more questions.</p> Sharing Test Mon, 27 May 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/sharing-test/ https://roundwallsoftware.com/blog/sharing-test/ <p>This is a test of the new sharing extension. If you're seeing this, it means that it appears to be working just fine. You can expect to be able to use it yourself soon in Publicist which you can find <a href="https://itunes.apple.com/us/app/publicist/id1449350225?mt=12">here</a>.</p> Publicist Update Sun, 05 May 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/publicist-update/ https://roundwallsoftware.com/blog/publicist-update/ <p>Publicist version 1.1.0 is now live in the <a href="https://itunes.apple.com/us/app/publicist/id1449350225?mt=12">App Store</a>. This version corrects the rather embarassing problem where Publicist did not work with newer versions of Ghost.</p> <p>Development on Publicist started over a year ago and most of my work to support Ghost was tested against my own blog. Ghost before version 2.16.1 did not have an easily-accessible API, but I worked around that. I did not expect that they would change this any time soon and did not want to wait to release Publicist until they did.</p> <p>In my excitement to finally release 1.0 though, I did not check to see if Ghost had changed anything and it turns out they did back in February with the release of 2.16.1. 2.16.1 adds a more officially supported API which makes setup in Publicist much simpler.</p> <p>If you're like me and run your Ghost blog yourself, you maybe haven't updated. Publicist will work on older releases of Ghost still, so you're taken care of. Anyone who <em>has</em> updated or is paying for hosting directly from Ghost.org will be on the latest version of Ghost and you'll need this latest update to Publicist.</p> Terms Of Use Tue, 23 Apr 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/terms-of-use/ https://roundwallsoftware.com/blog/terms-of-use/ You Should Write Wed, 17 Apr 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/you-should-write/ https://roundwallsoftware.com/blog/you-should-write/ <p>If you are a developer, even if you are just getting started learning to program, you should be writing. I didn't take this advice myself for the first years of my career and I regret it. When I did finally start writing, I was embarassed by how bad it was. If I had started sooner, I'd be even better than I am now and you'd be enjoying the writing of a much better writer. Fortunately, starting late is better than never starting and here you are reading my work.</p> <p>If you are an employee somewhere, you probably can't show your work since it's all proprietary and secret. When you go to find your next job, you won't have code samples to show, but if you were writing, you would have a way to show that you know what you're talking about. Writing skill can also be the difference between promotion and stagnation.</p> <p>If you're a freelancer, writing can play a big role in your ability to find clients. Many clients I've had over the past 7 years found me through my writing (and my writing isn't even that good!). You might not have code samples to show either because your clients all had you sign NDAs. Some clients even ask that you keep the fact that you worked with them a secret. If you were writing, you'd have some proof that you understand programming concepts. Being able to say you were on a team that built some app is good, but proving that you actually understand what you're doing gets you clients.</p> <p>If you're new and you're learning, even if you just started last week, there is an ever-increasing number of people who started after you. You might think to yourself, "nobody wants to hear what I have to say, I just barely understand what a variable is!", but that is silly. Your explanation of variables or strings could be exactly the thing that devs who come after you (and even some who came before you) needed to read in order to understand the topic. When blocks were first introduced to Objective-C, I read so many different attempts to explain what they were and how they worked. One well-written explanation was not enough, it took several from big places like Apple itself all the way down to random stranger devs writing on their personal blogs to finally understand. You're also helping yourself. Attempting to explain a topic helps to solidify your own understanding. Some great historical figure once said that you don't truly understand a topic unless you can write about it.</p> <p>Regardless of what path you're on as a developer, your ability to communicate in written form is critical. In any team project it is essential for working nicely together (even if you share an office). Being able to make the computer do things is pretty important if you want to call yourself a developer, but if you cannot write about it, your path is likely to be rough.</p> <p>The easiest way to get better at writing is to write. Conveniently, you can set up a blog and practice frequently in an environment where being bad at it won't cost you your job or cause your teammates to yell at you. Write, write often. Maybe don't even write about only programming, write about your dog if you want to. You'll get better quickly and everyone you work with will notice the difference.</p> Release Day! Sun, 14 Apr 2019 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/release-day/ https://roundwallsoftware.com/blog/release-day/ <p><img src="/blog/Screen-Shot-2019-04-13-at-09.27.09.png" alt="Screenshot of Publicist" /></p> Mobiledoc Sample file Sat, 22 Dec 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/mobiledoc-sample-file/ https://roundwallsoftware.com/blog/mobiledoc-sample-file/ <blockquote class="twitter-tweet"><p lang="no" dir="ltr">Hello!!<br>Hello.<br>Hello.<br>Hello....</p>&mdash; Ash Lindquist (@A_Lindquist) <a href="https://twitter.com/A_Lindquist/status/1056095160600211457?ref_src=twsrc%5Etfw">October 27, 2018</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <blockquote class="instagram-media" data-instgrm-captioned data-instgrm-permalink="https://www.instagram.com/p/BpbcoB8Fbtm/?utm_source=ig_embed&amp;utm_medium=loading" data-instgrm-version="12" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a href="https://www.instagram.com/p/BpbcoB8Fbtm/?utm_source=ig_embed&amp;utm_medium=loading" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div><div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" xmlns="https://www.w3.org/2000/svg" xmlns:xlink="https://www.w3.org/1999/xlink"><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;"> View this post on Instagram</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div></a> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BpbcoB8Fbtm/?utm_source=ig_embed&amp;utm_medium=loading" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">Conehead (I think) mantis nymph. Looks like it was invented for a big-budget sci-fi flick.</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A post shared by <a href="https://www.instagram.com/tikitudej/?utm_source=ig_embed&amp;utm_medium=loading" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px;" target="_blank"> Tikitu de Jager</a> (@tikitudej) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2018-10-27T07:53:02+00:00">Oct 27, 2018 at 12:53am PDT</time></p></div></blockquote> <script async src="//www.instagram.com/embed.js"></script> Formatter 1.3 Fri, 21 Dec 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/formatter-1-3/ https://roundwallsoftware.com/blog/formatter-1-3/ <p>A new version of Formatter is available on the App Store now. It includes a few improvements I hope you'll enjoy.</p> <p>You can find it on the <a href="https://itunes.apple.com/us/app/formatter/id1190228172?mt=12">App Store</a> now!</p> <h1 id="front-matter-ignoring">Front-matter Ignoring</h1> <p>It can be common for files that have JSON to include some other information above it. Formatter previously would fail to handle these files because they're technically not valid JSON files. This problem comes up frequently enough, I decided to improve the formatter to handle it anyway. If you have such files, you can drop them on the app window as well as format them using the Xcode Extension. The quicklook plugin will also now display something more useful than "(null)" for these files as well.</p> <p><img src="/blog/Screen-Shot-2018-12-21-at-20.20.54.png" alt="" /></p> <h1 id="format-selection">Format selection</h1> <p>Previously, Formatter's Xcode extension would only attempt to format entire files. Now you can select the lines of text which actually contain the JSON you wish to format and use the extension on those lines by themselves. Handy for formatting inline JSON you might have included in your unit tests for example.</p> <iframe width="560" height="315" src="https://www.youtube.com/embed/UWhylaLTncU" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> Privacy Policy Thu, 20 Dec 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/privacy-policy/ https://roundwallsoftware.com/blog/privacy-policy/ <p>Roundwall Software retains no personal information about their users except for email addresses. Send us an email requesting deleting this information and it will be done within 24 hours no questions asked.</p> With An Image Tue, 18 Dec 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/with-an-image/ https://roundwallsoftware.com/blog/with-an-image/ <p>If you are reading this, Publisher (the upcoming blog publishing app) has successfully posted its first image. That means we're making progress towards the initial release!</p> <p><img src="/blog/UNADJUSTEDNONRAW_thumb_3b-7.jpg" alt="" /></p> Introducing Publisher Sat, 24 Nov 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/introducing-publisher/ https://roundwallsoftware.com/blog/introducing-publisher/ <p>Way back nearly a year ago, December 16th I started work on a blog publishing app. There are several approaches to this on the Mac, but I felt the need to build another.</p> <h2 id="competition">Competition</h2> <p>Apps like <a href="https://red-sweater.com/marsedit/">MarsEdit 4</a> and <a href="https://www.getblogo.com">Blogo</a> offer the complete package. You can write, edit, publish, and then manage the articles on your blogs. There are also text editors like <a href="https://bywordapp.com">Byword</a> that allow you to publish to blogs when you're finished writing. There are also apps from companies like <a href="https://ghost.org">Ghost</a> and <a href="https://wordpress.com">Wordpress</a> who make apps themselves, but these appear to be electron shells and defeat part of the reason anyone would want a desktop app for their blog. There are even others who were either shut down or acquired and are gone forever.</p> <p>My plan has a few advantages over these.</p> <ul> <li>I can support different blogging systems. Many support Wordpress, but few support systems like Ghost (which this site uses).</li> <li>They provide editors when not everyone needs an editor. Many people have a way they like to write; maybe they use <a href="https://www.barebones.com/products/bbedit/index.html">BBEdit</a>, <a href="https://www.vim.org">Vim</a>, <a href="https://bear.app">Bear</a>, or any other excellent editor. By leaving this part out of my app, writers can use the editor they want to use and Publisher (the tentative name for my app) can take care of that part it is good at.</li> <li>I won't write it in Electron, so accessibility, performance, integration, and a native appearance wouldn't be such a hassle.</li> </ul> <p>I think my app and I has a shot in participating in an important space: internet publishing.</p> <h2 id="initial-plans">Initial Plans</h2> <center> <img src="/content/images/2018/11/IMG_0001-2.jpeg" alt="Picture of my initial notes on the project" width="50%" /> </center> <p>This is how it started. The primary motivation for the app was to provide an offline backup of all my posts that could easily be published somewhere else. Some blogs have a way to export their contents, some blogs have a way to import contents, but the format one blog can export doesn't always match the formats the new blog can import. Even when import does work, there's often glitches. My own blog moved from tumblr onto various blog engines to its current home on Ghost. I've spent quite some time fixing errors created by this mis-match. Publisher will be an app that solved this and make sure this worked nicely on supported blogs.</p> <h2 id="progress-so-far">Progress so Far</h2> <center> <img src="/content/images/2018/11/Screen-Shot-2018-02-03-at-10.03.39-1.jpg" alt="Screenshot of an early version of Publisher" width="50%" /> </center> <p>This is how the initial UI looked as I planned. This did not consider some of the features I planned and came before Mojave was announced with Dark Mode. Mojave's dark colors were way nicer than these, so I was happy to adopt them once they arrived.</p> <p>Time passed and I made good progress on a working core of an app. Since I'm unfortunately not independently wealthy, I needed to continue my regular client work throughout the year as well. Even so, since March my app has been in a usable state. Every post on this site since then was published using my app, often each post published would spawn a small list of bugs to fix and features to add or adjust.</p> <p>Development-wise, this might be some of my best work. As of right now, the app uses a framework I created which is 100% tested to handle the Mobiledoc format and the rest of the app is 84.7% tested (though that would be slightly higher if there wasn't a bug in how coverage was calculated in Xcode). Code coverage isn't the only way to measure quality and can often be a problematic way, especially on a team that is not enjoying their work, but for me it means less bugs in the future (including regressions of the bugs I've already found and fixed), confidence in releasing on new versions of Mac OS and Swift, and street cred when people talk about testing.</p> <center> <img src="/content/images/2018/11/Screenshot-2018-10-24-at-22.47.56-1.jpg" alt="Screenshot of the app as of publishing" width="50%" /> </center> <p>This is how the app looks now. It's not quite ready for you to enjoy, but it's getting there. Be sure to check back here for updates as we get closer to the release!</p> JSON Handling Thu, 08 Nov 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/json-handling/ https://roundwallsoftware.com/blog/json-handling/ <h2 id="the-problem">The Problem</h2> <p>Let's say you have a handful of items in your code where the items seem <em>quite</em> similar, but are slightly different. For example, bus tickets: You might have paper tickets, monthly passes, and senior discount cards.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">struct</span><span> Paper { </span><span> </span><span style="color:#b48ead;">let</span><span> validUntil: Date </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> currency: String </span><span> </span><span style="color:#b48ead;">let</span><span> price: Double </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> Monthly { </span><span> </span><span style="color:#b48ead;">let</span><span> validUntil: Date </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> SeniorDiscount { </span><span> </span><span style="color:#b48ead;">let</span><span> validUntil: Date </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> age: Int </span><span>} </span></code></pre> <p>Let's say you're making the program that reads these different kinds of tickets when the bus driver scans them. The driver doesn't care about the details, they just want to know if the ticket is valid and maybe what kind of ticket it is. We do need to <em>store</em> the different details to send to the server though.</p> <p>Since you're hip and writing things in Swift, you might first try to make a protocol which defines the common bits between them. You might also add an enumeration for the type because that's important information too.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">enum</span><span> TicketType { </span><span> </span><span style="color:#b48ead;">case</span><span> paper </span><span> </span><span style="color:#b48ead;">case</span><span> monthly </span><span> </span><span style="color:#b48ead;">case</span><span> seniorDiscount </span><span>} </span><span> </span><span style="color:#b48ead;">protocol</span><span> Ticket { </span><span> </span><span style="color:#b48ead;">var</span><span> validUntil: Date </span><span> </span><span style="color:#b48ead;">var</span><span> type: TicketType </span><span>} </span></code></pre> <p>This unfortunately does not work though. Since the <code>Codable</code> protocol in Swift is generic, you cannot simply encode the type <code>Array&lt;Ticket&gt;</code>. Here's how I solved the problem on a similar problem.</p> <h2 id="the-solution">The Solution</h2> <p>Although each of these tickets are technically different things, they're all tickets. I <em>could</em> have made these classes and use subclassing for this, but subclassing for data feels weird to me. There's no behavior here, just data that needs to be stored and partly checked. I chose instead to make a single type that stores all the data for all the kinds of tickets. This method comes with some benefits:</p> <ul> <li>Uses the type enum you defined already, so defining a new type will generate a compiler warning anywhere you aren't considering this new case. A new sub-class or other kind of type would not generate the same warning.</li> <li>Makes testing simpler.</li> </ul> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">enum</span><span> Value { </span><span> </span><span style="color:#b48ead;">case</span><span> date(Date) </span><span> </span><span style="color:#b48ead;">case</span><span> string(String) </span><span> </span><span style="color:#b48ead;">case</span><span> integer(Int) </span><span> </span><span style="color:#b48ead;">case</span><span> floatingPoint(Double) </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> Ticket { </span><span> </span><span style="color:#b48ead;">let</span><span> validUntil: Date </span><span> </span><span style="color:#b48ead;">let</span><span> type: TicketType </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> userInfo: [String: Value] </span><span>} </span></code></pre> <p>This single struct can store the information for all types of ticket. Since the app only really cares about what kind of ticket it is and when it stops being valid, the rest can be tucked into a dictionary so we don't lose the information when we send to the server. There's precedent for this type of design all over Cocoa. <code>NSError</code> and <code>NSNotification</code> immediately come to mind as objects that work like this.</p> <p>Constructing new tickets now is a bit tedious, so it's useful to make convenient constructors for each type:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">extension</span><span> Ticket { </span><span> </span><span style="color:#b48ead;">static func </span><span>paper(validUntil: Date, currency: String, price: Double) { </span><span> </span><span style="color:#b48ead;">return</span><span> Ticket(validUntil: validUntil, type: .paper, userInfo: [ </span><span> </span><span style="color:#a3be8c;">&quot;currency&quot;</span><span>: .string(currency), </span><span> </span><span style="color:#a3be8c;">&quot;price&quot;</span><span>: .floatingPoint(price) </span><span> ]) </span><span> } </span><span>} </span></code></pre> <p>This gives similar benefit to a subclass or multiple-struct type organization. There's one single place to construct a paper ticket and make sure you don't include the necessary values. Since this is all the same type though, we don't need to also define how this paper ticket encodes and decodes from JSON.</p> <p>All that's left is to make <code>Ticket</code> and <code>Value</code> conform to <code>Codable</code>. This is fine for <code>Ticket</code>, but <code>Value</code> has some trouble. In JSON, there is no difference between an int or a float or a double. JSON only has a few types, one of them is "some number", so when you decode it takes a little bit to tell ints apart from floating point values. I solved this like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">if let</span><span> value = </span><span style="color:#b48ead;">try</span><span>? container.decode(Double.</span><span style="color:#b48ead;">self</span><span>), value.rounded() == value { </span><span> </span><span style="color:#b48ead;">self </span><span>= .int(Int(value)) </span><span>} </span></code></pre> <p>A <code>JSONDecoder</code> will allow you to decode floating point values as <code>Int</code> and just quietly remove anything after the decimal. This will lead to some confusion if you don't specifically consider and test for it.</p> <p>This makes testing simpler overall because you no longer need to test encoding and decoding each of your ticket types. If you manually create a ticket with each kind of <code>Value</code> (or seperately test that each kind of <code>Value</code> can be encoded properly), then a single test that checks encoding and decoding from JSON is sufficient to be confident your server will be happy with these tickets.</p> <h2 id="conclusion">Conclusion</h2> <p>So there you have it, a refactoring that saved me quite some headache on a project that I think leaves the codebase in a nicer place. I think it's important when you're working in codebases to consider that sometimes a reorganization will make things much nicer rather than trying to patch a confusing implementation.</p> More Types Mon, 22 Oct 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/more-types/ https://roundwallsoftware.com/blog/more-types/ <p>Sometimes the answer to your programming problem is "use more types". I spoke at <a href="https://2018.mobilization.pl">Mobilization 8</a> this past weekend and I discussed a particular programming issue with someone. Here is a version of that problem (with the details changed to protect the innocent).</p> <h1 id="the-setup">The Setup</h1> <p>Let's say you have a struct like so:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>struct CreditCard { </span><span> let number: String </span><span> let expiration: Date </span><span>} </span></code></pre> <p>Now let's also say that whenever you display the number in your app, you need to mask part of the number. Rather than display <code>4242 4242 4242 4242</code> on the screen, you want to only display <code>xxxx xxxx xxxx 4242</code>. The person I spoke with had a project where they accomplished this goal by adding to their general <code>CreditCardUtils</code> struct.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>struct CreditCardUtils { </span><span>/// various other methods here, at least 100 lines of code, some that depend on system objects. </span><span> </span><span> static func mask(number: String) -&gt; String { </span><span> } </span><span>} </span><span> </span><span>extension CreditCard { </span><span> var maskedNumber: String { </span><span> return CreditCardUtils.mask(number: number) </span><span> } </span><span>} </span><span> </span></code></pre> <p>This way technically worked. Any time you needed to display the number on the screen, you could ask a card for the masked version. However, when he tried to write tests for it, he ran into a problem. This masking method was a static function of a struct which could not be created without access to system objects. Even if you did go through the effort to create one, it did not matter because you could not replace the call with your fake in a <code>CreditCard</code>.</p> <p>After some discussion, here's what I came up with:</p> <h1 id="the-fix">The Fix</h1> <h2 id="a-new-type">A New Type</h2> <p>Replace the super-generic type <code>String</code> with a new custom type <code>CreditCardNumber</code> there are two ways to do that depending on what else you need to do.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>struct CreditCardNumber { </span><span> let number: String </span><span>} </span><span> </span><span>/// or </span><span> </span><span>typealias CreditCardNumber = String </span></code></pre> <h2 id="move-the-behavior-to-the-type">Move The Behavior To The Type</h2> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>extension CreditCardNumber { </span><span> var masked: String { </span><span> } </span><span>} </span></code></pre> <p>Rather than depending on some generic Util type to do the work, we move that behavior onto the object that <em>has</em> this behavior. A card number can create a masked representation. This is easily testable and no longer requires elaborate gymnastics in your tests. Make a card number with a value, verify the masked version it generates looks correct, move on to your next task. This <em>does</em> have the consequence of making your code a bit more verbose though:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>let card = CreditCard(number: CreditCardNumber(number: &quot;4242 4242 4242 4242&quot;), expiry: Date()) </span></code></pre> <p>Slightly more messy looking, but if you really want to fix that you can! If you make the new type conform to <code>ExpressibleByStringLiteral</code>, you can create cards in much the same way as you would before.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>struct CreditCardNumber: ExpressibleByStringLiteral { </span><span> typealias StringLiteralType = String </span><span> </span><span> let rawValue: String </span><span> </span><span> init(stringLiteral: String) { </span><span> self.rawValue = stringLiteral </span><span> } </span><span>} </span><span> </span><span>let number: CreditCardNumber = &quot;4242 4242 4242 4242&quot; </span></code></pre> <p>Now that you can create card numbers with string literals, creating new cards looks like it did originally:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>let card = CreditCard(number: &quot;4242 4242 4242 4242&quot;, expiry: Date()) </span></code></pre> <p>I don't personally think this step is necessary, but I've seen projects where they avoid creating the necessary types to represent their information nicely because their inits look messier. If you're one of those people, this trick can help.</p> <h2 id="use-a-formatter">Use A Formatter</h2> <p>After thinking about this some more, I realized that maybe this masked value should not be computed by the <code>CreditCardNumber</code>. We are trying to format data for display on the screen, so we should use a formatter, just like we would if we wished to display dates, currencies, or weights.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>class CreditCardNumberFormatter: Formatter { </span><span>} </span></code></pre> <p>A formatter is a fairly small object with no dependencies so it is still easy to test. Using a <code>Formatter</code> subclass also allows you to use the same logic in your Mac app with Cocoa Bindings and such. Generally any time you need to format your data to display on the screen, there's likely an associated formatter. If one does not exist, like in this example with credit card numbers, perhaps you should make one. There are quite a few already:</p> <ul> <li>DateFormatter</li> <li>PersonNameComponentsFormatter (because in some languages, the last name comes first and such)</li> <li>MassFormatter</li> <li>EnergyFormatter (for joules and calories and such)</li> <li>LengthFormatter</li> <li>NumberFormatter</li> <li>MeasurementFormatter</li> <li>ByteCountFormatter</li> <li>ISO6801DateFormatter</li> <li>DateIntervalFormatter</li> </ul> <p>These formatters consider units, different use cases, as well as language. All testable in a fairly neat package.</p> <h1 id="fin">Fin</h1> <p>So now maybe you're armed with a bit more knowledge and you can go forth and test your things. Enjoy!</p> Strong Self in Swift Tue, 18 Sep 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/strong-self-in-swift/ https://roundwallsoftware.com/blog/strong-self-in-swift/ <p>Swift 4.2 has arrived which gives us the ability to replace code bits in your weak-reference blocks like <code>guard let strongSelf = self else { return }</code> or <code>guard let 'self' = self else { return }</code> with new-fangled, more direct things like <code>guard let self = self else { return }</code>. This is generally nicer, easier to read and helps to prevent arguments about wether to use variables like <code>strongSelf</code> vs <code>otherSelf</code> or whatever your teammates might want.</p> <p>However! I've seen quite some discussion about how this is much nicer and not much discussion about the more glaring problem. When you replace <code>self?.doSomething()</code> with <code>guard let self = self else { return }</code>, you're doing effectively the same thing. Either of these and any of the above examples will result in silent failure if the <code>self</code> in question has disappeared by the time your block is executing. If all you do in the else portion of your <code>guard</code> statements is <code>return</code>, I generally think this is a problem. If that else statement is triggered, isn't that usually an error? Should there be some logging? a <code>fatalError</code> or some such? Some alternate behavior. If all you do is return, you're making it hard on yourself later when you try to figure out why your app isn't doing what you expect.</p> <p>Another minor note, if you write <code>guard self = self else { return }</code> all in one line as I've commonly seen, you're also making it a pain to put a breakpoint on that <code>else</code> statement so that you can pause your app when it does happen. This makes me sad.</p> <p>So in conclusion, consider what happens in your elses, you probaly don't want to just <code>return</code>. Also maybe don't put it all on one line, it's ok if you take more lines, scrolling is fairly easy on modern computers.</p> Diversity Sat, 14 Jul 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/diversity/ https://roundwallsoftware.com/blog/diversity/ <p>We in the Apple-platform space suffer from a lack of diversity in our conferences. I’ve seen this vastly improve in the US, but over here in Europe it still seems fairly bad. Every month I hear about a new conference which promotes their all-white and often all-male speaker lineup. They often brag about diversity because some women are among their speakers, but their lineups do not represent our community and it makes me sad. Here in Amsterdam, some <a href="http://worldpopulationreview.com/world-cities/amsterdam-population/">49.5%</a> of people have "foreign ancestry" (which is a funny term if you ask me). In just the Netherlands, there are people of all kinds of shades of brown. Throughout history, the Netherlands has invited groups of people from Jamaica, Curaçao, Morocco and so on to live here. Anyone who thinks any country in Europe is all-white is simply ignoring statistics. Every other European country has similar history, people of all shades have been all over Europe as far back as anyone knows.</p> <p>Now if you organize a conference, you’re taking on quite a bit of risk and work (hopefully not completely alone). There’s a lot to organize and you may even do all that work to find that nobody bought a ticket. It confuses me then why a person or group would volunteer for such a challenge, but then claim that finding an actually-diverse speaker lineup is too hard. If you sold tickets to a conference, but failed to book the venue, you have failed to organize a conference. If you present a speaker lineup that is all white or even worse all male, then I think you have similarly failed.</p> <p>If you host an open call for papers, many conferences have had improved success by anonymizing proposals to help prevent automatic biases, but also consider where you talk about your CFP. If only your friends hear about it, you won't get quite the variety of submissions you might hope for. IIf this happens, you need to do something else. Simply saying "Well, no women submitted a proposal, so I guess we'll have no women." is a lazy excuse.</p> <p>If you're not hosting an open call, but inviting speakers directly, make sure to invite people beyond your immediate circle of colleagues/programmer-friends. Last year I was invited to speak at a small first-time <a href="http://www.iquestgroup.com/en/event/mobile-unplugged-2017/">conference</a> in Cluj, Romania and I did not know anyone inolved in the conference. They blindly reached out to developers from across Europe hoping to find people who would come and speak. Somehow they managed to find people who were not all white males (or all young/single people even). If these (super cool) inexperienced people can work it out, surely you can too! We're all smart, problem-solving people in this industry, right?</p> <p>I've also spoken at <a href="https://www.tryswift.co/events/2019/tokyo/en/">trySwift</a> in Tokyo. They are an excellent example of diverse lineups. Scroll down this list of previous speakers there and admire all the colors, backgrounds, and experiences represented there!</p> <p>Also, organizers: when someone brings up these concerns to you who isn’t a co-organizer of your conference, it is not ok to turn to them and reply, “well can <em>you</em> recommend a diverse person we can invite?” This allows you the organizer to sound like you care about solving the problem while pushing the actual work of solving this problem off onto someone who’s job is <em>not</em> organizing a conference. Especially if your conference is the sort that specifically invites speakers rather than hosting a call for papers, it is your job to invite a good group of people, not the underrepresented people who raise their concerns.</p> Making a Date Server With SwiftNIO Thu, 15 Mar 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/making-a-date-server-with-swiftnio/ https://roundwallsoftware.com/blog/making-a-date-server-with-swiftnio/ <p>Apple just released <a href="https://github.com/apple/swift-nio">SwiftNIO</a>. A framework for low-level asynchronous IO in Swift. It was based on a familiar called <a href="https://netty.io">Netty</a> (which fun fact is written by one of the devs now working on SwiftNIO at Apple). When I heard the presentation to announce the library at try!Swift Tokyo this year, it sounded super familiar. The rationale for the library was similar to when Node.js was announced by <a href="https://www.youtube.com/watch?v=ztspvPYybIY">Ryan Dahl</a>.</p> <p>SwiftNIO has already been integrated into web frameworks like <a href="https://vapor.codes">Vapor</a>, so you can benefit for free if you're using the latest in your server-side Swift projects. For many web projects, you probably won't need to use SwiftNIO directly, but if you're doing something different like the sort of things you'd use websockets for and want to use Swift, this is the framework for you.</p> <p>There is some sample code included in the <a href="https://github.com/apple/swift-nio">git repository</a>, but I thought a tutorial friendly walkthrough explanation might be handy so here you go:</p> <h2 id="some-paperwork-first">Some Paperwork First</h2> <p>SwiftNIO is intended to be added to your project via the swift package manager.</p> <p>Add SwiftNIO to your <code>Package.swift</code></p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#65737e;">// swift-tools-version:4.0 </span><span style="color:#65737e;">// The swift-tools-version declares the minimum version of Swift required to build this package. </span><span> </span><span style="color:#b48ead;">import </span><span>PackageDescription </span><span> </span><span style="color:#b48ead;">let</span><span> package = Package( </span><span> name: </span><span style="color:#a3be8c;">&quot;InfoServer&quot;</span><span>, </span><span> dependencies: [ </span><span> </span><span style="color:#65737e;">// Dependencies declare other packages that this package depends on. </span><span> .package(url: </span><span style="color:#a3be8c;">&quot;https://github.com/apple/swift-nio.git&quot;</span><span>, from: </span><span style="color:#a3be8c;">&quot;1.0.0&quot;</span><span>) </span><span> ], </span><span> targets: [ </span><span> </span><span style="color:#65737e;">// Targets are the basic building blocks of a package. A target can define a module or a test suite. </span><span> </span><span style="color:#65737e;">// Targets can depend on other targets in this package, and on products in packages which this package depends on. </span><span> .target( </span><span> name: </span><span style="color:#a3be8c;">&quot;InfoServer&quot;</span><span>, </span><span> dependencies: [</span><span style="color:#a3be8c;">&quot;NIO&quot;</span><span>]), </span><span> ] </span><span>) </span></code></pre> <p>Note we mention SwiftNIO in two places: one to include it as a dependency for the project, and a second time to specifically use the library in our <code>InfoServer</code> target. If you leave out the second one, the package manager will download and compile SwiftNIO for you, but it won't be avaiable to your code yet. This held me up for about 10 minutes as I had not spent much time using this package manager before this.</p> <h2 id="the-fun-bits">The Fun Bits</h2> <p>Now you get to go directly to the fun bits and decide the behavior of your server. Activity in NIO is handled by a pipeline of channel handlers, each gets a chance to handle incoming or outgoing data in the order they were setup. We'll implement one of these to give our server app some behavior. First we'll make a class, call it whatever you want, but make it implement the protocol <code>ChannelInboundHandler</code>. This class will handle incoming data (asynchronously of course) and give us a chance to do something about it.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> DateHandler: ChannelInboundHandler { </span><span>} </span></code></pre> <p>We'll need to add some associated types so the pipeline knows what type of data we expect to recieve and send back out. Since we're only using one handler in our simple example, we'll need to accept and produce the type <code>ByteBuffer</code> which is NIO's custom type for handling data efficiently. I tried using <code>String</code> instead, but that silently failed because there was nothing in the pipeline to convert a buffer into a string for me.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">typealias</span><span> InboundIn = ByteBuffer </span><span style="color:#b48ead;">typealias</span><span> OutboundOut = ByteBuffer </span></code></pre> <p>Now for the actual work:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">func </span><span>channelRead(ctx: ChannelHandlerContext, data: NIOAny) { </span><span> </span><span style="color:#b48ead;">let</span><span> message = </span><span style="color:#a3be8c;">&quot;Date is </span><span>\(Date()</span><span style="color:#a3be8c;">)</span><span style="color:#96b5b4;">\n</span><span style="color:#a3be8c;">&quot; </span><span> </span><span style="color:#b48ead;">var</span><span> buffer = ctx.channel.allocator.buffer(capacity: message.utf8.count) </span><span> buffer.write(string: message) </span><span> </span><span> ctx.write(</span><span style="color:#b48ead;">self</span><span>.wrapOutboundOut(buffer), promise: </span><span style="color:#d08770;">nil</span><span>) </span><span> } </span></code></pre> <p><code>channelRead</code> is the function that gets executed any time our inbound handler revieves data. The data parameter there is our <code>ByteBuffer</code> type object wrapped in a NIO-specific version of <code>Any</code>. For our example here, we don't even care what's in there! We only care that someone tried to send us data, not what they actually sent. Regardless of the incoming message, we bundle up a string into a buffer and write that out through our channel context. We use nil for the promise parameter because we don't care if there are any errors or when it finishes. In real life you might want to do something after the data is sent, like if you needed to send the data back in chunks because it was a whole gigabyte of information, but for now we don't care at all.</p> <p>The <code>ChannelInboundHandler</code> protocol requires two more methods, so we begrudgingly add some bare minimum implementations:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>channelReadComplete(ctx: ChannelHandlerContext) { </span><span> ctx.flush() </span><span>} </span><span> </span><span style="color:#b48ead;">func </span><span>errorCaught(ctx: ChannelHandlerContext, error: Error) { </span><span> print(</span><span style="color:#a3be8c;">&quot;error: </span><span>\(error)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> ctx.close(promise: </span><span style="color:#d08770;">nil</span><span>) </span><span>} </span></code></pre> <p>When the channel finishes reading, we tell the context to flush. This sends a flush event down to any other handlers and eventually attempts to write the data we've supplied to the outbound socket. We also need to handle errors, so an easy way is just to print the error and ditch the connection. This is a bad idea in real life, but this is just play time, so it's fine.</p> <p>The rest of the code needed to make things actually work is fairly cookie cutter. Most projects will need something very similar to it. It does seem like a bunch of boilerplate that could be avoided, but it's also an opportunity to control the important details of your important high-performance server app.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">let</span><span> group = MultiThreadedEventLoopGroup(numThreads: System.coreCount) </span><span style="color:#b48ead;">let</span><span> bootstrap = ServerBootstrap(group: group) </span><span> .serverChannelOption(ChannelOptions.backlog, value: </span><span style="color:#d08770;">256</span><span>) </span><span> .serverChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: </span><span style="color:#d08770;">1</span><span>) </span><span> .childChannelInitializer { channel </span><span style="color:#b48ead;">in </span><span> channel.pipeline.add(handler: DateHandler()) </span><span> } </span><span> .childChannelOption(ChannelOptions.socket(IPPROTO_TCP, TCP_NODELAY), value: </span><span style="color:#d08770;">1</span><span>) </span><span> .childChannelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: </span><span style="color:#d08770;">1</span><span>) </span><span> .childChannelOption(ChannelOptions.maxMessagesPerRead, value: </span><span style="color:#d08770;">16</span><span>) </span><span> .childChannelOption(ChannelOptions.recvAllocator, value: AdaptiveRecvByteBufferAllocator()) </span><span> </span><span style="color:#b48ead;">defer</span><span> { </span><span> </span><span style="color:#b48ead;">try</span><span>! group.syncShutdownGracefully() </span><span>} </span><span> </span><span style="color:#b48ead;">let</span><span> channel = </span><span style="color:#b48ead;">try</span><span> bootstrap.bind(host: </span><span style="color:#a3be8c;">&quot;localhost&quot;</span><span>, port: </span><span style="color:#d08770;">8080</span><span>).wait() </span><span> </span><span>print(</span><span style="color:#a3be8c;">&quot;Server is alive!&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">try</span><span> channel.closeFuture.wait() </span><span> </span><span>print(</span><span style="color:#a3be8c;">&quot;Server closed&quot;</span><span>) </span></code></pre> <p>First we need to create a group and for now there's only one option for the type of group so it's an easy choice, here we can limit the number of threads we want our app to use. My app uses one for each core in the server's CPU because I want it to use the whole machine for the best performance. You could limit yours to only one thread maybe if you wanted some small worker process instead. Next we make a <code>ServerBootstrap</code> with our group. The bootstrap object lets us specify all kinds of settings from socket-level things that should look familiar if you've ever done socket programming down to even specifying a specific buffer allocator you'd like to use. If hardcore low level performance is important, it's possible you'll need your own custom allocator. If you don't know what these options do, it's probably pretty safe to just use the ones I used until you get told otherwise. This is also where we plug in our channel handler, so don't forget that. The <code>childChannelInitializer</code> function creates the channel and we add our handler to its pipeline. If you miss this step, your server will start up with no behavior which is probably not what you want for the type of group.</p> <p>Lastly we bind our bootstrap to localhost and run the server. This is just a fun demo so I didn't see any need for anything short of a good old fashioned hard-code. We defer the attempt to gracefully shutdown our thread group because we want it to happen even if any of the functions below it throw errors.</p> <p>And there you have it! A working amazing sever side app which very quickly and efficiently tells you the time and date any time you send literally anything over a socket connection. You can test it by running <code>telnet</code> from your terminal and type things to your server app. Fun, right?</p> The 30 Day Music Challenge you're going to do Thu, 22 Feb 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/the-30-day-music-challenge-you-re-going-to-do/ https://roundwallsoftware.com/blog/the-30-day-music-challenge-you-re-going-to-do/ <h1 id="why">Why</h1> <p>Sometimes a cheesy random challenge helps can help you get out of your rut and start something new. I propose we do exactly that. All of you who play/sing music: record yourself playing every day for 30 days. Put the videos on Instagram. The time limit Instagram imposes (max 1 minute) will help to make this doable since you can't spend the afternoon trying to get a whole song perfect on camera.</p> <p>I don't care what instrument you use or how good you are. Sing, strum, blow, whatever you've got.</p> <p>The point of this is to get over mental barriers to recording. So if you record and publish, you win. This isn't about getting the perfect take or having the best ever recording quality, this is about showing up. I figure if you do this every day, your recordings will improve on their own even if you don't put a ton of concious effort into it.</p> <h1 id="what">What</h1> <p>Here are some examples to clarify further:</p> <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/Bfgk00QBq5X/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/Bfgk00QBq5X/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by Samuel Goodwin (@mukmantheorigina)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2018-02-22T18:27:23+00:00">Feb 22, 2018 at 10:27am PST</time></p></div></blockquote> <script async defer src="//www.instagram.com/embed.js"></script> <p>This isn't full speed and I mess up a little bit. I may have even forgotten a bit of the fill. This counts. You can do it. Bonus points though if you can see sweet bikes in the background of your videos (not really).</p> <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/BfglKILBZFk/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/BfglKILBZFk/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by Samuel Goodwin (@mukmantheorigina)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2018-02-22T18:30:18+00:00">Feb 22, 2018 at 10:30am PST</time></p></div></blockquote> <script async defer src="//www.instagram.com/embed.js"></script> <p>This one is way slower than it should be and missing effects because I like how my guitar sounds clean. Still counts!</p> <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/Bfglfe-h3p_/" data-instgrm-version="8" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAAMUExURczMzPf399fX1+bm5mzY9AMAAADiSURBVDjLvZXbEsMgCES5/P8/t9FuRVCRmU73JWlzosgSIIZURCjo/ad+EQJJB4Hv8BFt+IDpQoCx1wjOSBFhh2XssxEIYn3ulI/6MNReE07UIWJEv8UEOWDS88LY97kqyTliJKKtuYBbruAyVh5wOHiXmpi5we58Ek028czwyuQdLKPG1Bkb4NnM+VeAnfHqn1k4+GPT6uGQcvu2h2OVuIf/gWUFyy8OWEpdyZSa3aVCqpVoVvzZZ2VTnn2wU8qzVjDDetO90GSy9mVLqtgYSy231MxrY6I2gGqjrTY0L8fxCxfCBbhWrsYYAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/Bfglfe-h3p_/" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by Samuel Goodwin (@mukmantheorigina)</a> on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2018-02-22T18:33:18+00:00">Feb 22, 2018 at 10:33am PST</time></p></div></blockquote> <script async defer src="//www.instagram.com/embed.js"></script> <p>This one isn't even a song. It's me tooling around with a looper pedal because I don't entirely understand how soloing works. This also counts! If you were a super pro artist who could pull amazing-sounding full songs out of your butt every day, you'd be hopefully busy making money playing music and being amazing.</p> <h1 id="when">When</h1> <p>This challeng starts March 17. Why so far in advance? Because I'm traveling and it's convenient. This is also convenient for you as it gives you time to dust off your equipment, replace your strings and such if needed, and maybe even make a list of the things you want to work on. Challenges are doomed to fail if you don't first take care of the logistics. Make sure you have plectrums, make sure your setup is ok, make sure you have a decent reed (if you play a woodwind). Make sure you have a way to record: your phone on a stand or your laptop is fine, better is nice too if you can work it out quick. Get ready so we can party in March! I will be making a web page devoted to the challenge so we can all listen to and celebrate our peers as we all defeat a challenge together.</p> <h1 id="how">How</h1> <p>To participate, simply send me a link to your posts via twitter @samuelgoodwin or email at [email protected].</p> Wacky JSON Tue, 23 Jan 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/wacky-json/ https://roundwallsoftware.com/blog/wacky-json/ <p>In Swift, turning JSON content from a file or a server into structs or classes in code is much friendlier now that we have the Encodable and Decodable protocols. For simple cases you need to write almost no code and can quickly get on to something else.</p> <p>For example, if you see this:</p> <pre data-lang="json" style="background-color:#2b303b;color:#c0c5ce;" class="language-json "><code class="language-json" data-lang="json"><span>{ </span><span> &quot;</span><span style="color:#a3be8c;">posts</span><span>&quot;: [ </span><span> &quot;</span><span style="color:#a3be8c;">this is a tweet maybe</span><span>&quot;, </span><span> &quot;</span><span style="color:#a3be8c;">this is something political</span><span>&quot;, </span><span> &quot;</span><span style="color:#a3be8c;">I&#39;m angry about something</span><span>&quot;, </span><span> &quot;</span><span style="color:#a3be8c;">I made something cool</span><span>&quot; </span><span> ] </span><span>} </span></code></pre> <p>You can decode it with very little code:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">struct</span><span> PostPage: Decodable { </span><span> </span><span style="color:#b48ead;">let</span><span> posts: [String] </span><span>} </span><span> </span><span style="color:#b48ead;">let</span><span> data = </span><span style="color:#65737e;">// Get this JSON from a file or the internet or your imaginination </span><span style="color:#b48ead;">let</span><span> decoder = JSONDecoder() </span><span style="color:#b48ead;">let</span><span> page = </span><span style="color:#b48ead;">try</span><span> docoder.decode(PostPage.</span><span style="color:#b48ead;">self</span><span>, from: data) </span></code></pre> <p>This is all great for most cases, but what happens when API-makers stop being polite and things get real?</p> <p>What if your JSON looks like this:</p> <pre data-lang="json" style="background-color:#2b303b;color:#c0c5ce;" class="language-json "><code class="language-json" data-lang="json"><span>{ </span><span> &quot;</span><span style="color:#a3be8c;">posts</span><span>&quot;: [ </span><span> &quot;</span><span style="color:#a3be8c;">image</span><span>&quot;, </span><span> { </span><span> &quot;</span><span style="color:#a3be8c;">url</span><span>&quot;: &quot;</span><span style="color:#a3be8c;">http://somesite/image.jpg</span><span>&quot;, </span><span> &quot;</span><span style="color:#a3be8c;">caption</span><span>&quot;: &quot;</span><span style="color:#a3be8c;">this is a horse</span><span>&quot; </span><span> }, </span><span> &quot;</span><span style="color:#a3be8c;">text</span><span>&quot;, </span><span> &quot;</span><span style="color:#a3be8c;">this is just text</span><span>&quot; </span><span> ] </span><span>} </span></code></pre> <p>For some reason whoever designed this JSON structure felt it necessary to have an array of items where each item is not the same kind of thing. In this array of posts, there is a string which indicates a kind of post followed by a dictionary or a string or something which represents the data for that kind of post. Decoding this in swift, a language that loves type-safety and hates arrays of different types, will require a bit more code, but it <em>can</em> be done.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#65737e;">//: Playground - noun: a place where people can play </span><span> </span><span style="color:#b48ead;">import </span><span>Cocoa </span><span> </span><span style="color:#b48ead;">let</span><span> raw = </span><span style="color:#a3be8c;">&quot;&quot;&quot; </span><span style="color:#a3be8c;">{ </span><span style="color:#a3be8c;"> &quot;</span><span>posts</span><span style="color:#a3be8c;">&quot; : [ </span><span style="color:#a3be8c;"> &quot;</span><span>image</span><span style="color:#a3be8c;">&quot;, </span><span style="color:#a3be8c;"> { &quot;</span><span>url</span><span style="color:#a3be8c;">&quot;:&quot;</span><span>http:</span><span style="color:#65737e;">//somesite/image.jpg&quot;, &quot;caption&quot;:&quot;this is a horse&quot;}, </span><span> </span><span style="color:#a3be8c;">&quot;text&quot;</span><span>, </span><span> </span><span style="color:#a3be8c;">&quot;this is just some text&quot; </span><span> ] </span><span>} </span><span style="color:#a3be8c;">&quot;&quot;&quot;.data(using: .utf8)! </span><span> </span><span style="color:#a3be8c;">protocol Post: Decodable { </span><span style="color:#a3be8c;"> // This is where any common info between the items would be required. </span><span style="color:#a3be8c;">} </span><span> </span><span style="color:#a3be8c;">struct Image: Post { </span><span style="color:#a3be8c;"> let url: URL </span><span style="color:#a3be8c;"> let caption: String </span><span style="color:#a3be8c;">} </span><span> </span><span style="color:#a3be8c;">struct Paragraph: Post { </span><span style="color:#a3be8c;"> let text: String </span><span style="color:#a3be8c;">} </span><span> </span><span style="color:#a3be8c;">struct PostsPage: Decodable { </span><span style="color:#a3be8c;"> let posts: [Post] </span><span> </span><span style="color:#a3be8c;"> enum CodingKeys: String, CodingKey { </span><span style="color:#a3be8c;"> case posts = &quot;</span><span>posts</span><span style="color:#a3be8c;">&quot; </span><span style="color:#a3be8c;"> } </span><span> </span><span style="color:#a3be8c;"> // An error to throw if the JSON changes later and we don&#39;t know </span><span style="color:#a3be8c;"> // how to handle a new type of post. </span><span style="color:#a3be8c;"> enum PostsErrors: Error { </span><span style="color:#a3be8c;"> case unknownPostType </span><span style="color:#a3be8c;"> } </span><span> </span><span style="color:#a3be8c;"> init(from decoder: Decoder) throws { </span><span style="color:#a3be8c;"> // First dig out the array under the key posts </span><span style="color:#a3be8c;"> let rawPage = try decoder.container(keyedBy: CodingKeys.self) </span><span style="color:#a3be8c;"> var rawPosts = try rawPage.nestedUnkeyedContainer(forKey: .posts) </span><span> </span><span style="color:#a3be8c;"> var posts = [Post]() </span><span> </span><span style="color:#a3be8c;"> // each time you decode an element from an unkeyed container, </span><span style="color:#a3be8c;"> // currentIndex is incremented </span><span style="color:#a3be8c;"> while rawPosts.currentIndex &lt; (rawPosts.count ?? 0) { </span><span style="color:#a3be8c;"> // each element is preceded by a string which specifies it&#39;s type </span><span style="color:#a3be8c;"> let type = try rawPosts.decode(String.self) </span><span style="color:#a3be8c;"> switch type { </span><span style="color:#a3be8c;"> case &quot;</span><span>image</span><span style="color:#a3be8c;">&quot;: </span><span style="color:#a3be8c;"> let post = try rawPosts.decode(Image.self) </span><span style="color:#a3be8c;"> print(post) </span><span style="color:#a3be8c;"> posts.append(post) </span><span style="color:#a3be8c;"> case &quot;</span><span>text</span><span style="color:#a3be8c;">&quot;: </span><span style="color:#a3be8c;"> // text posts are just strings, so we have to decode the string </span><span style="color:#a3be8c;"> // and then create a paragraph post from that. </span><span style="color:#a3be8c;"> let text = try rawPosts.decode(String.self) </span><span style="color:#a3be8c;"> posts.append(Paragraph(text: text)) </span><span style="color:#a3be8c;"> default: </span><span style="color:#a3be8c;"> // for all types we don&#39;t know about that were maybe added </span><span style="color:#a3be8c;"> throw PostsErrors.unknownPostType </span><span style="color:#a3be8c;"> } </span><span style="color:#a3be8c;"> } </span><span style="color:#a3be8c;"> self.posts = posts </span><span style="color:#a3be8c;"> } </span><span style="color:#a3be8c;">} </span><span> </span><span style="color:#a3be8c;">let decoder = JSONDecoder() </span><span style="color:#a3be8c;">let page = try? decoder.decode(PostsPage.self, from: raw) </span><span style="color:#a3be8c;">print(page ?? &quot;</span><span>this failed</span><span style="color:#a3be8c;">&quot;) </span></code></pre> <p>Way more code than the simple case, but it does work. The end result is an array of different types of posts with their own info. Hopefully this sort of work won't be necessary for your projects, but now you know just in case.</p> Hiring And Retaining Thu, 18 Jan 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/hiring-and-retaining/ https://roundwallsoftware.com/blog/hiring-and-retaining/ <p>I've heard from a great many companies here in the Netherlands (and elsewhere) that hiring and retaining engineers is super difficult. Here are some things to consider to make things easier for your company:</p> <h2 id="is-your-company-involved-in-the-right-communities">Is your company involved in the right communities?</h2> <p>If you're looking for, let's say, Ruby developers, you're going to need to be involved in Ruby communities. Here are some ways to get in:</p> <ul> <li>Send your engineers to local meetups. Like Appsterdam here in Amsterdam, Cocoaheads near you, and so on.</li> <li>Send your engineers to conferences. Send your engineers to at least one conference a year, even if it's just a nearby one. Not only does this get you exposure for your company, a policy like this can be the difference between someone choosing to apply for your company or not.</li> <li>Encourage your engineers to speak. If you can send them to a conference <em>and</em> they get on stage to talk about something interesting, that's two birds with one stone.</li> <li>Contribute to open source. Your team is very likely making use of open source projects to get work done every day. Find a bug? Contribute a fix. Even minor changes can get people's attention. This also shows that your company is the type that contributes, which is important for many of those potential applicants.</li> </ul> <h2 id="do-your-job-listings-mention-the-important-parts">Do your job listings mention the important parts?</h2> <p>Today's engineer, especially the experienced ones, care less about ping pong tables and kegerators and more about the things that actually affect their daily life much more.</p> <ul> <li>Do you allow remote work? You definitely should, if you do, make sure you mention that.</li> <li>What kind of vacation policy do you have? Whatever it is, make it clear.</li> <li>Do you offer parental leave? How much? The more experienced an engineer is, the older they might be and the more likely they are to have children. If you don't treat this like they have a disease, it will help to get those experienced engineers in the door.</li> <li>Also be sure to mention those community-involvement things you do that we discussed above. It's amazing how many job listings I see that don't specifically call these out.</li> </ul> <p>With all of these things, don't be vague. Call out specicially what you offer as much as possible. "We take care of our engineers" could mean anything from "we give our engineers a beer on friday" to "we offer 3 months of parental leave". When your job listing is vague, it can make it sound like you're hiding something.</p> <h2 id="do-your-requirements-fit-the-position">Do your requirements fit the position?</h2> <p>If you're looking for an iOS engineer, but also require 4 years of Java experience, this doesn't make any sense and will often make engineers pass up on your company. If there is some legitimate reason why this is actually needed, explain that in the listing. "We need engineers with Java experience for this iOS team because we're using this tool which compiles our Java code for iOS." makes things way more clear. (Side note, if your iOS app is made from cross-compiling Java, that by itself might be a reason you're having trouble hiring engineers).</p> <h2 id="we-can-help">We can help.</h2> <p>There's quite a bit that can change, and you most likely can't do it all at once and that's ok. If you'd like, we at Roundwall Software can help identify what needs changing and help fix it. Starting in 2018, we are now offering this as a service. More than simply some management consultant, we offer an actual developer. Someone who knows firsthand what it means to be a part of the developer community. <a href="/contact/">Contact us</a> and we can begin.</p> Core Data Performance Tue, 09 Jan 2018 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/core-data-performance/ https://roundwallsoftware.com/blog/core-data-performance/ <p>Last year I spoke at <a href="http://www.pragmaconference.com">PragmaConf</a> in Italy and the video of my talk is now available:</p> <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/I2oGeiYik3E?rel=0" frameborder="0" gesture="media" allow="encrypted-media" allowfullscreen></iframe> How I Approach Rat-Nest CoreData Projects Sun, 05 Nov 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-i-approach-rat-nest-coredata-projects/ https://roundwallsoftware.com/blog/how-i-approach-rat-nest-coredata-projects/ <p>Part of my job involves working with existing projects, some with overly complicated Core Data implementations. Usually clients request that I help fix performance problems in the project or at least clean them up. Here is how I approach each project:</p> <p>#####1: Clear out the cobwebs</p> <p>First thing I do is look through the whole project for unused classes, methods, variables, etc. For Objective-C projects, this is the one thing I use <a href="https://www.jetbrains.com/objc/">AppCode</a> for. For Swift projects, there are tools such as <a href="https://github.com/tsabend/fus">fus</a> to help. For one project, I was able to delete 30 <em>thousand</em> lines of code this way. No refactoring yet, simply deleting unused stuff. Most projects that have been around for longer than 6 months have unused bits to delete I've found.</p> <p>#####2: Refactor</p> <p>Next I look for eveywhere an <code>NSManagedObjectContext</code> is used. Anywhere a context is used, the operation/function/class should be told what context to use. Commonly in project that need help, this is not the case and operations are assuming certain contexts. If everything is neatly factored to be told which context to use, I'm empowered to change which context they use at will.</p> <p>#####3: Simplify</p> <p>Once I have control over which context everything is using, I move everything to use one main context on the main thread. Conventional wisdom is that this is wrong and it technically is <em>but</em> this is only a temporary step. Some of what causes instability, performance problems, or confusion in projects is an entirely too-complicated context arrangement in the name of performance. Usually there are entirely too many in an unnecessary arrangement.</p> <p>#####4: Measure</p> <p>Once everything is on one context, I can whip out one of my favorite programs, Instruments, and run the time profiler on the app. There's no need to guess or speculate or brute force your way to performance. Instruments can instead find out exactly where the worst places are and I can do something about those specifically. During this phase, I determine which tasks in the app are slow and save a few runs for each tasks. It's hard to say if anything is improved if there is no "before" picture to compare to.</p> <p>#####5: Attack</p> <p>Armed with the knowledge of exactly what tasks are slow and exactly why these takss are slow, I can go about fixing them. Maybe the solution really is to move Core Data operations to a background context, but maybe it's not. It could be something more like fetching 1000 objects once instead of fetching one object at a time 1000 times. Maybe that slow fetch request needs to have a batch-size set or maybe the fetch needs to pre-fetch related entities. I try to only make the change that actually helps performance problems and leave behind simpler code everywhere else.</p> Ghost's API Tue, 24 Oct 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/ghost-s-api/ https://roundwallsoftware.com/blog/ghost-s-api/ <p><a href="https://ghost.org">Ghost</a> is a blogging platform written in Node JS that was released a few years ago after a successful Kickstarter campaign. They promised a blogging platform without all the complications of Wordpress and even partnered with companies like <a href="https://www.digitalocean.com">Digital Ocean</a> to make it easy for users to try and use.</p> <p>One of the promises of Ghost was that the web admin tool was built using an API from day one rather than adding an API after the fact. It used <a href="https://www.emberjs.com">EmberJS</a> and provided a fairly nice web interface. The API, however, remained private and only worked with this JS front-end. <em>Years</em> later, they added the ability for third party clients to authenticate and access the API, but it was still a private API and therefore subject to change whenever they felt like it.</p> <p>Last year, they even released a "desktop" client. It made use of <a href="https://electron.atom.io">Electron</a> to essentially just provide the web interface in a desktop shell. In their <a href="https://blog.ghost.org/desktop/">blog post</a> they talk about how this was great because it allowed users to write their posts away from the clutter of their other blog posts. They explain chosing Electron because it enabled them to build the desktop app with the same language and tools they build the web parts with. Javascript all around! All of this convenience comes at a cost: without an internet connection, this web shell won't work. Say goodbye to working on that draft on the airplane! With that Electron shell, there's yet another app in the list with Slack and others in using up resources even when they're idle.</p> <p>What they could have done was make the whole API public. I discussed this with their developers and it seemed this was the plan some time ago, but it has yet to happen. This would have enabled developers to make third-party clients. We'd have desktop, iOS, and even Android apps to publish and edit posts on our Ghost blogs. Makers of apps like <a href="https://red-sweater.com/marsedit/">MarsEdit</a> or <a href="https://www.getblogo.com">Blogo</a> could have added support for Ghost even. But what developer would take a risk adding support for an API that's undocumented and subject to arbitrary change?</p> <p>I guess instead, it's time to switch to another platform. Wordpress may be famous for all kinds of problems, but they got an API worked out a long time ago.</p> Speaking Fri, 20 Oct 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/speaking/ https://roundwallsoftware.com/blog/speaking/ <h2 id="upcoming">Upcoming</h2> <ul> <li>I'm available to speak at future conferences. Let me know!</li> </ul> <h2 id="previous">Previous</h2> <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/NPdEx4HxTI4" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> <ul> <li><a href="https://www.youtube.com/watch?v=ryKlEdddi6E">360iDev 2019</a>: No Progress Like Slow Progress</li> <li><a href="http://2018.mobilization.pl/index.html#ts-schedule">Mobilization 2018</a>: Testing the Tricky Stuff</li> <li><a href="https://www.tryswift.co/events/2018/tokyo/en/">trySwift! Tokyo 2018</a>: Getting to Know The Responder Chain</li> <li><a href="https://pragmaconference.com">Pragma Conf 2017</a>: Core Data Performance</li> <li><a href="http://cluj.techfest.ro">Techfest Cluj</a>: Core Data Performance</li> </ul> The Case of the Broken Buttons Sat, 23 Sep 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/the-case-of-the-broken-buttons/ https://roundwallsoftware.com/blog/the-case-of-the-broken-buttons/ <p>On a client project the other day, I spent most of the day tracking down a rather serious bug. All UIBarButtonItems were nearly un-tappable on iOS 11. These were not especially crafty buttons, most of them were using either an icon of reasonable size or a title. Some were even using the system standard items. None of them worked. Icons were un-tappable, back buttons were only tappable on the edges. All users on iOS 11 were left out in the cold and would <em>not</em> be happy.</p> <p>Instantly I assumed there was some clever code hiding somewhere that was causing the problem. I searched the project (this was a fairly new client so I did not know all of what lurked there) for subclasses of UINavigationController and UINavigationBar. I looked for categories and extensions on UIBarButtonItem as well as the navigation classes. I even assumed the problem was with one of the libraries included in the app. Surely third party code is doing something silly and breaking every button.</p> <p>In the end none of that was correct. Here's how I actually found the problem:</p> <p>All UIView subclasses implement a method called <code>-hitTest:withEvent:</code>. Documentation explains that this is how the system determines which view a user is tapping on when they tap on the screen. The message is first sent to the key UIWindow which in turn calls the method for each of its subviews. Each subview recursively searches its own subviews to try to find the front-most view which contains the point the user has tapped on.</p> <p>I tried replacing the key UIWindow of the app with a custom subclass which implements this method and prints out the resulting view before returning it. No change in logic, just displaying for debug purposes.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> HitTestWindow: UIWindow { </span><span> </span><span style="color:#b48ead;">open override</span><span> hitTest(_ point: CGPoint, with event: UIEvent?) -&gt; UIView? { </span><span> </span><span style="color:#b48ead;">let</span><span> view = </span><span style="color:#b48ead;">super</span><span>.hitTest(point, with: event) </span><span> print(view ?? </span><span style="color:#a3be8c;">&quot;No view!&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">return</span><span> view </span><span> } </span><span>} </span></code></pre> <p>Tapping on an item with this in place yielded an object of class _UIModernBarButton. The underscore implied it was not a public class which was a relief because I've never heard of it.</p> <p>When I did the same thing on a dummy project which worked as expected on iOS 11, I instead got an object of class <code>_UIButtonBarButton</code>. Aha! Something is changing what gets returned when the hit test is executed.</p> <p>A search of the project for the phrase <code>hitTest</code> yielded the perpetrator:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">extension</span><span> UIButton { </span><span> </span><span style="color:#b48ead;">open override func </span><span>hitTest(_ point: CGPoint, with event: UIEvent?) -&gt; UIView? { </span><span> </span><span style="color:#b48ead;">if self</span><span>.isHidden { </span><span> </span><span style="color:#b48ead;">return </span><span style="color:#d08770;">nil </span><span> } </span><span> </span><span style="color:#b48ead;">let</span><span> buttonSize = </span><span style="color:#b48ead;">self</span><span>.frame.size </span><span> </span><span style="color:#b48ead;">let</span><span> widthToAdd = (</span><span style="color:#d08770;">44 </span><span>- buttonSize.width &gt; </span><span style="color:#d08770;">0</span><span>) ? </span><span style="color:#d08770;">44 </span><span>- buttonSize.width : </span><span style="color:#d08770;">0 </span><span> </span><span style="color:#b48ead;">let</span><span> heightToAdd = (</span><span style="color:#d08770;">44 </span><span>- buttonSize.height &gt; </span><span style="color:#d08770;">0</span><span>) ? </span><span style="color:#d08770;">44 </span><span>- buttonSize.height : </span><span style="color:#d08770;">0 </span><span> </span><span style="color:#b48ead;">let</span><span> largerFrame = CGRect(x: </span><span style="color:#d08770;">0 </span><span>- (widthToAdd / </span><span style="color:#d08770;">2</span><span>), y: </span><span style="color:#d08770;">0 </span><span>- (heightToAdd / </span><span style="color:#d08770;">2</span><span>), width: buttonSize.width + widthToAdd, height: buttonSize.height + heightToAdd) </span><span> </span><span style="color:#b48ead;">return</span><span> largerFrame.contains(point) ? </span><span style="color:#b48ead;">self </span><span>: </span><span style="color:#d08770;">nil </span><span> } </span><span>} </span></code></pre> <p>This extension seems harmless and even came with comments explaining how the goal was to make sure buttons had a minimum touchable size of 44x44pts even if they appeared smaller. It misses out on one critical part of the requirements of the method though, the hit test should ignore views which have their <code>isUserInterationEnabled</code> property set to false.</p> <p>The modern button in the project's <code>isUserInteractionEnabled</code> property <em>was</em> set to false, but was returning itself as the result of the hit test anyway. This means the view underneath it which actually should be recieving the touch event never got it. The recursive search stopped with a disabled view and nothing happened. No working back buttons, no working cancel buttons, no working search buttons.</p> <p>To confirm some more, I had a look at the app with the UI debugger. In iOS 9 and 10, UIBarButtonItems are built differently. This weirdly-disabled modern button is a new thing in iOS 11. That explains why it worked fine up until now.</p> <p>So what did we learn from this?</p> <ol> <li>Extensions on system-provided classes can be tricky, especially if you're using override methods.</li> <li>For this specific case, it might have been easier to just make sure buttons were not tiny rather than faking a larger touch area.</li> <li>Even experienced devs like me can take a while to find things that might look fairly simple in the end.</li> <li>Stuff like this is why it's nice to have a senior developer around.</li> <li>Reading the documentation is good for everyone at all levels.</li> </ol> How To Write IRC: Part 6 Sat, 26 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-6/ https://roundwallsoftware.com/blog/how-to-write-irc-part-6/ <p>This is a continuation of the article published yesterday: <a href="http://roundwallsoftware.com/how-to-write-irc-part-5/">How To Write IRC: Part 5</a></p> <h1 id="framework-ification">Framework-ification</h1> <p>After all this work, it was time to make the actual framework. I started by making a new project with the Cocoa Framework template and copying over the important files from my demo project.</p> <p><img src="/blog/Screen-Shot-2017-08-19-at-09.56.32.png" alt="Screenshot of the new project&#39;s file structure" /></p> <p>Next, I setup support for <a href="https://github.com/Carthage/Carthage">Carthage</a> for people to bring this into their own apps. I could support Cocoapods as well, but I don't especially care, so I left that for someone to submit a pull request to add. Supporting Carthage was fairly straightforward, I followed the directions on their front page without much of an issue. One minor thing I ran into: because I was developing this using the latest Xcode Beta and not normal Xcode, I needed to use <code>xcode-select</code> to tell command-line xcodebuild (which Carthage uses) to use my beta version of Xcode instead of the default. I don't think in the end I used anything special to Swift 4 that would <em>require</em> the beta, but I'm too cool to go back now and support older versions, Swift 4 will be finalized soon enough.</p> <p>The next step in making a framework is important, but often skipped: documentation. You should provide at least a basic usage guide so that people have any clue how to use your library. Nobody wants to go fishing around in your code to figure it out unless they absolutely have to. Initially, they just want to be able to pull in your library and try it out. After writing documentation, I pushed my code to Github in a new repo. You can find it <a href="https://github.com/sgoodwin/IRC">here</a>. I also needed to create a release tag for versioning purposes so I start at v1.0. Sure this wasn't a super-complete implementation of everything possible with IRC, but it <em>was</em> enough to use for demos which was my intent. I don't like the idea of starting with version "0.1" or some such, that implies it isn't usable yet. The last step to building a framework was to update the demo app to use the framework. So I go in, delete all the IRC code that now exists in a framework, including tests, and instead import my new framework.</p> <p>It was a good thing I decided to do this because it uncovered a massive flaw in my plans! I forgot that, since my IRC code is bundled into a framework, I must mark all my classes public if I want them to be visible outside the framework. Whoops! I decided to only make the user, channel, and server objects (along with their delegate protocols) public. A developer using this framework shouldn't need to use the input parser directly or any of the enums I made to handle the parsed input. With these changes, I commit again and replace the original 1.0 release to this new one. Normally you might want to issue this as a bugfix release or something, but since I published the initial release literally 5 minutes before and I'm not very popular, I know that nobody has downloaded that original release yet. There's a certain safety in not being popular at all.</p> <p>And that's it. I wrote some code with tests and published it as a framework people can play with. Fun times had by all.</p> <p>The final result of the demo project can be found <a href="https://github.com/sgoodwin/IRCDemo">here</a> the final result of the IRC framework can be found <a href="https://github.com/sgoodwin/IRC">here</a></p> How To Write IRC: Part 5 Fri, 25 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-5/ https://roundwallsoftware.com/blog/how-to-write-irc-part-5/ <p>This is a continuation of the article published yesterday: <a href="http://roundwallsoftware.com/how-to-write-irc-part-4/">How To Write IRC: Part 4</a></p> <h1 id="more-tests">More Tests!</h1> <p>Next larger feature I wanted to test was joining a channel. As I planned before, joining a channel should create a new object to give developers a way to interact. For this I modified the first test I wrote which had the basic parts laid out already. It didn't really test anything anyway, so I made it do something useful.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testJoiningAChannel() { </span><span> </span><span style="color:#b48ead;">let</span><span> user = IRCUser(username: </span><span style="color:#a3be8c;">&quot;sgoodwin&quot;</span><span>, realName: </span><span style="color:#a3be8c;">&quot;Samuel Goodwin&quot;</span><span>, nick: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> server = IRCServer.connect(</span><span style="color:#a3be8c;">&quot;127.0.0.1&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>, user: user, session: fakeSession) </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> channel = server.join(</span><span style="color:#a3be8c;">&quot;clearlyafakechannel&quot;</span><span>) </span><span> </span><span> </span><span style="color:#b48ead;">struct</span><span> ChannelDelegate: IRCChannelDelegate { </span><span> </span><span style="color:#b48ead;">let</span><span> expectation = XCTestExpectation(description: </span><span style="color:#a3be8c;">&quot;Any message receieved&quot;</span><span>) </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>didReceieveMessage(_ channel: IRCChannel, message: String) { </span><span> expectation.fulfill() </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> channelDelegate = ChannelDelegate() </span><span> channel.delegate = channelDelegate </span><span> </span><span> wait(</span><span style="color:#b48ead;">for</span><span>: [channelDelegate.expectation], timeout: </span><span style="color:#d08770;">1</span><span>.</span><span style="color:#d08770;">0</span><span>) </span><span>} </span></code></pre> <p>After clearing out everything not directly related, I was left with this. It connects to a server, tries to join a channel, and expects at least one channel message to pass through. This would test that my channel objects are connected to everything else properly. After I had everything connected as planned, I found that my tests for the input parser were insufficient. It turns out the streaming task does not split the chunks of data it receive by newlines as I had assumed. When I thought you would only get messages like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>:[email protected] JOIN #clearlyatestchannel\r\n </span></code></pre> <p>it turns out you can actually get chunks of data more like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>:[email protected] JOIN #clearlyatestchannel\r\n\r\n:development.irc.roundwallsoftware.com 353 mukman = #clearlyafakechannel :mukman @sgoodwin\r\n:development.irc.roundwallsoftware.com 366 mukman #clearlyafakechannel :End of /NAMES list. </span></code></pre> <p>all at once in a streaming task. This told me that I either needed to split input before feeding it to my parser or I needed to make the parser return an array of results instead of just one result. I decided it would involve the least amount of change to split the incoming data before parsing it, so I did. The second chunk of that message also turned out to be a type of message I had not considered. When you first join a channel, the server sends a list of all the users currently already in that channel. I needed to add this "user list" message to my input message enum. To get all my tests to pass, I cheated out a bit on the user list. Later when I have a better example of what a longer list of users looks like, I can improve the implementation to be more accurate. For now it only cares about the first user in the list. With all tests working, I knew that my plans for how things should be connected are pretty much settled. Changes were scattered all around, so rather than show it all here, you can check <a href="https://github.com/sgoodwin/IRCFramework/commit/4ffa0cdf95d86712642805f22d59b33a8e956654">this commit</a> for all the gory details.</p> <p>From here I updated my terrible sample app from my first experiments to use the new code. The view controller became smaller, how convenient. I could connect to a server, join a channel, see what is said in the channel, but I noticed two important things were missing:</p> <ol> <li>I wasn't handling PING messages properly. Turns out they look like <code>PING :development.irc.roundwallsoftware.com</code> and not simply <code>PING</code> as I had expected before.</li> <li>I can't yet send messages to the channel.</li> </ol> <p>The first wasn't too bad to fix, I updated the unit test for my input parser. I could make all my tests pass again by changing the part that looks for the <code>PING</code> message to</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">if</span><span> message.hasPrefix(</span><span style="color:#a3be8c;">&quot;PING&quot;</span><span>) { </span><span> </span><span style="color:#b48ead;">return </span><span>.ping </span><span>} </span></code></pre> <p>Then second I decided to try to fix with a mock object. Essentially what I wanted to verify was: if I use a channel's send method, it should use its related server's send method with the proper input to say "the user said this from this channel". I could test this by mocking the IRCServer that the channel was created with.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testSendingAChannelMessage() { </span><span> </span><span style="color:#b48ead;">class</span><span> MockServer: IRCServer { </span><span> </span><span style="color:#b48ead;">var</span><span> sentMessage: String? </span><span> </span><span> </span><span style="color:#b48ead;">override func </span><span>send(_ message: String) { </span><span> sentMessage = message </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> user = IRCUser(username: </span><span style="color:#a3be8c;">&quot;sgoodwin&quot;</span><span>, realName: </span><span style="color:#a3be8c;">&quot;Samuel Goodwin&quot;</span><span>, nick: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> server = MockServer.connect(</span><span style="color:#a3be8c;">&quot;127.0.0.1&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>, user: user) </span><span> </span><span style="color:#b48ead;">let</span><span> channel = server.join(</span><span style="color:#a3be8c;">&quot;clearlyafakechannel&quot;</span><span>) </span><span> channel.send(</span><span style="color:#a3be8c;">&quot;hey sup&quot;</span><span>) </span><span> </span><span> XCTAssertEqual(server.sentMessage, </span><span style="color:#a3be8c;">&quot;PRIVMSG #clearlyafakechannel :hey sup&quot;</span><span>) </span><span>} </span></code></pre> <p>This test took advantage of Swift's ability to arbitrarily make new classes inside functions to make a special-case IRCServer. We only needed it for this one test. For this to work I needed to modify IRCServer's init to be internal and not private, but that's ok (read about the difference <a href="https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AccessControl.html">here</a> if you're curious). The test does not initially pass because right now IRCChannel's send method does nothing. So I fixed that.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>send(_ text: String) { </span><span> server.send(</span><span style="color:#a3be8c;">&quot;PRIVMSG #</span><span>\(name) </span><span style="color:#a3be8c;">:</span><span>\(text)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span>} </span></code></pre> <p>With this change, the test passed and I went back to my demo app to play with it. It's quite a bit of fun carrying on a conversation with yourself over two irc clients, especially when you wrote one of them. This makes all the basic functionality I wanted to implement complete! With my code you can connect to a server, join a channel, and talk back and forth with your new super-cool friends on that channel. Hooray! Fireworks! The only thing left to do is move all the IRC code into a framework so that other people can use it without needing to touch my awful demo app. I'll show you that in our exciting conclusion tomorrow.</p> <p>The work finishes in <a href="http://roundwallsoftware.com/how-to-write-irc-part-6/">How To Write IRC: Part 6</a></p> How to Write IRC: Part 4 Thu, 24 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-4/ https://roundwallsoftware.com/blog/how-to-write-irc-part-4/ <p>This is a continuation of the article published yesterday: <a href="http://roundwallsoftware.com/how-to-write-irc-part-3/">How To Write IRC: Part 3</a></p> <h1 id="async-integration-testing">Async Integration Testing</h1> <p>Some of you might be reading along and think to yourself, "This guy hasn't tested anything! He doesn't assert <em>anything</em>. What kind of lies is he trying to sell?". To that I say, "Give me a minute, it gets better". One of the nice things about code is that nothing you write really needs to be permanent. Especially when you're starting a thing, it's fine if you have to change it a bunch. You don't need to know exactly what you're going to do right when you start. That would be boring.</p> <p>The very next thing I wrote was an actual test with actual assertions.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testServerDelegateGetsServerMessages() { </span><span> </span><span style="color:#b48ead;">let</span><span> user = IRCUser(username: </span><span style="color:#a3be8c;">&quot;sgoodwin&quot;</span><span>, realName: </span><span style="color:#a3be8c;">&quot;Samuel Goodwin&quot;</span><span>, nick: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> server = IRCServer.connect(</span><span style="color:#a3be8c;">&quot;irc.freenode.org&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>, user: user) </span><span> </span><span> </span><span style="color:#b48ead;">struct</span><span> ServerDelegate: IRCServerDelegate { </span><span> </span><span style="color:#b48ead;">var</span><span> buffer = [String]() </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> serverDelegate = ServerDelegate() </span><span> server.delegate = serverDelegate </span><span> </span><span> XCTAssert(serverDelegate.buffer.count &gt; </span><span style="color:#d08770;">0</span><span>) </span><span>} </span></code></pre> <p>The test says that if I connect to a server, I can expect some data to be sent from the server and it should collect in my server delegate. The API I decided on required a server object to have a delegate to signal with messages from the server (such as the message-of-the-day or MOTD). This test fails as we expect because nothing is actually connecting to anything and there aren't any delegate methods. Then I set to work making the test pass.</p> <p>I added a method to the <code>IRCServerDelegate</code> protocol:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">protocol</span><span> IRCServerDelegate { </span><span> </span><span style="color:#b48ead;">func </span><span>didRecieveMessage(_ server: IRCServer, message: String) </span><span>} </span></code></pre> <p>implemented the method in my delegate object:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>didRecieveMessage(_ server: IRCServer, message: String) { </span><span> buffer.append(message) </span><span style="color:#65737e;">// In real life, the developer would maybe update the UI or something here. They&#39;re free to do what they want. </span><span>} </span></code></pre> <p>and finally updated the <code>IRCServer</code> class to make the test pass:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> IRCServer { </span><span> </span><span style="color:#b48ead;">var</span><span> delegate: IRCServerDelegate? { </span><span> </span><span style="color:#b48ead;">didSet</span><span> { </span><span> delegate?.didRecieveMessage(</span><span style="color:#b48ead;">self</span><span>, message: </span><span style="color:#a3be8c;">&quot;This is a message from the server!&quot;</span><span>) </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">static func </span><span>connect(_ hostname: String, port: Int, user: IRCUser) -&gt; IRCServer { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCServer() </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>join(_ channelName: String) -&gt; IRCChannel { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCChannel() </span><span> } </span><span>} </span></code></pre> <p>With this I knew that the server object would probably need some sort of buffer inside it. It would begin to receive server messages as soon as it connected, but wouldn't be able to send them to the delegate until one has been assigned. I either needed this or I needed to require a delegate upon creation. I decided I liked giving the developer control over when they got messages, so I made a buffer.</p> <p>Next I wanted to make this test work using a <code>URLSession</code> instead of hard-coding what data to send back. For this I didn't want the test to depend on the internet so I planned to use <code>URLProtocol</code> to make a fake version of an IRC connection. This method was explained in <a href="http://nshipster.com/nsurlprotocol/">NSHipster</a> (one of my favorite blogs) back in the day. With this I could move my test code that actually uses <code>URLSession</code> into my new <code>IRCServer</code> class and try to get things working.</p> <p>First I needed to modify the test because server messages don't come instantaneously.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testServerDelegateGetsServerMessages() { </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> user = IRCUser(username: </span><span style="color:#a3be8c;">&quot;sgoodwin&quot;</span><span>, realName: </span><span style="color:#a3be8c;">&quot;Samuel Goodwin&quot;</span><span>, nick: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> server = IRCServer.connect(</span><span style="color:#a3be8c;">&quot;irc.freenode.org&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>, user: user) </span><span> </span><span> </span><span style="color:#b48ead;">class</span><span> ServerDelegate: IRCServerDelegate { </span><span> </span><span style="color:#b48ead;">var</span><span> buffer = [String]() </span><span> </span><span style="color:#b48ead;">let</span><span> expectation = XCTestExpectation(description: </span><span style="color:#a3be8c;">&quot;Any message receieved&quot;</span><span>) </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>didRecieveMessage(_ server: IRCServer, message: String) { </span><span> expectation.fulfill() </span><span> </span><span style="color:#65737e;">// In real life, the developer would maybe update the UI or something here. They&#39;re free to do whatever they want. </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> serverDelegate = ServerDelegate() </span><span> server.delegate = serverDelegate </span><span> </span><span> wait(</span><span style="color:#b48ead;">for</span><span>: [serverDelegate.expectation], timeout: </span><span style="color:#d08770;">1</span><span>.</span><span style="color:#d08770;">0</span><span>) </span><span>} </span></code></pre> <p>and I modified the server to actually use <code>URLSession</code> based on my test code I had in my view controller.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> IRCServer { </span><span> </span><span style="color:#b48ead;">var</span><span> delegate: IRCServerDelegate? { </span><span> </span><span style="color:#b48ead;">didSet</span><span> { </span><span> </span><span style="color:#b48ead;">guard let</span><span> delegate = delegate </span><span style="color:#b48ead;">else</span><span> { </span><span> </span><span style="color:#b48ead;">return </span><span> } </span><span> </span><span> buffer.forEach { (line) </span><span style="color:#b48ead;">in </span><span> delegate.didRecieveMessage(</span><span style="color:#b48ead;">self</span><span>, message: line) </span><span> } </span><span> buffer = [] </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">private var</span><span> buffer = [String]() </span><span> </span><span style="color:#b48ead;">private lazy var</span><span> session: URLSession = { </span><span> URLSession(configuration: URLSessionConfiguration.</span><span style="color:#b48ead;">default</span><span>, delegate: </span><span style="color:#d08770;">nil</span><span>, delegateQueue: </span><span style="color:#d08770;">nil</span><span>) </span><span> }() </span><span> </span><span style="color:#b48ead;">private var</span><span> task: URLSessionStreamTask! </span><span> </span><span> </span><span style="color:#b48ead;">private init</span><span>(hostname: String, port: Int, user: IRCUser) { </span><span> task = session.streamTask(withHostName: hostname, port: port) </span><span> task.resume() </span><span> read() </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">private func </span><span>read() { </span><span> task.readData(ofMinLength: </span><span style="color:#d08770;">0</span><span>, maxLength: </span><span style="color:#d08770;">9999</span><span>, timeout: </span><span style="color:#d08770;">0</span><span>) { (data, atEOF, error) </span><span style="color:#b48ead;">in </span><span> </span><span style="color:#b48ead;">guard let</span><span> data = data, </span><span style="color:#b48ead;">let</span><span> message = String(data: data, encoding: .utf8) </span><span style="color:#b48ead;">else</span><span> { </span><span> </span><span style="color:#b48ead;">return </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> input = IRCServerInputParser.parseServerMessage(message) </span><span> </span><span style="color:#b48ead;">switch</span><span> input { </span><span> </span><span style="color:#b48ead;">case </span><span>.serverMessage(_, </span><span style="color:#b48ead;">let</span><span> message): </span><span> </span><span style="color:#b48ead;">if let</span><span> delegate = </span><span style="color:#b48ead;">self</span><span>.delegate { </span><span> delegate.didRecieveMessage(</span><span style="color:#b48ead;">self</span><span>, message: message) </span><span> } </span><span style="color:#b48ead;">else</span><span> { </span><span> </span><span style="color:#b48ead;">self</span><span>.buffer.append(message) </span><span> } </span><span> </span><span style="color:#b48ead;">default</span><span>: </span><span> print(</span><span style="color:#a3be8c;">&quot;Some other stuff&quot;</span><span>) </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">self</span><span>.read() </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">static func </span><span>connect(_ hostname: String, port: Int, user: IRCUser) -&gt; IRCServer { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCServer(hostname: hostname, port: port, user: user) </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>join(_ channelName: String) -&gt; IRCChannel { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCChannel() </span><span> } </span><span>} </span></code></pre> <p>I took out every bit of code that wasn't needed to make the test pass. I wasn't worried about sending data yet and also not worried about handling error cases. I also didn't care about any of the other types of messages that might come in, for now just the server ones. Beyond the test code, I added the logic for handling a buffer of server messages. If there's no delegate, I made it save them up and when there was a delegate it could unload them.</p> <p>The test passed, but it depended on the internet. Gross! This would also flood poor freenode with a bunch of connection attempts while I was testing too, no server wants that.</p> <p>Normally this is where I would reach for <code>URLProtocol</code> to fake out the internet reactions. It's a super handy way to supplant HTTP interaction in your tests without having to do anything special in your actual application/framework code. Unfortunately though, since I was using a streaming task and not the more-commonly-used data task, this wouldn't work. (And I spent a day or so finding that out.) Instead I opted to setup my own local IRC server which I could connect to. I thought this could be even more useful down the line when I wanted to implement more elaborate interactions like operator status and such later. After flipping through some setup tutorials online, I thought I'd try <code>ircd-hybrid</code>. It doesn't matter really which server you use, there <em>are</em> however a few things you should make sure to configure</p> <ul> <li>Don't actually set it up to be publicly accessible. This is something you should run on your development machine while you're working. Arbitrary IRC servers out in the wild are subject to attack by hackers and other people who don't want the best for you. Make life easier by not letting them in.</li> <li>Disable the connection throttle rate. In testing code, you'll be reconnecting quite a bit, a throttling rate would create random failures in your tests which are no fun to track down (this happened to me initially)</li> </ul> <p>Once I got the local server running, I replaced the address in all my tests with <code>127.0.0.1</code> and re-ran to verify everything works with my server. Success! From here I could get on to implementing more stuff. One other thing to note though is that this test is an integration-level test. Ideally there wouldn't be tons of them, because each one has to wait for its timeout or a response from the server and that time adds up if you have 1000 tests. Verifying that my code handles specific responses from the server correctly was taken care of by testing the parser. The test to connect to the server was largely making sure all the different parts are connected as expected (one might even say <em>integrated</em>).</p> <p>The work continues in <a href="http://roundwallsoftware.com/how-to-write-irc-part-5/">How To Write IRC: Part 5</a></p> How To Write IRC: Part 3 Wed, 23 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-3/ https://roundwallsoftware.com/blog/how-to-write-irc-part-3/ <p>This is a continuation of the article published yesterday: <a href="http://roundwallsoftware.com/how-to-write-irc-part-2/">How To Write IRC: Part 2</a></p> <h1 id="defining-the-api">Defining the API</h1> <p>Before you run off and make a framework, it can be helpful to do a bit of brainstorming and plotting or scheming first. For this part I pulled out a sweet notebook and my fancy pencil. After some doodling and discussion with a friend of mine, I came up with this:</p> <p><img src="/blog/IMG_2047.jpg" alt="Image of my notebook drawing" /></p> <p>I wanted the idea of a channel to be a first-class object rather than an interface where you send along the channel name as a string and say "hey send this message to this channel". Directly having a channel object to send messages to sounded nicer to me.</p> <p>With this vague idea in mind, I started writing tests. This way I could work out exactly what the API would look like by attempting to actually use the API.</p> <p>After some debate with myself and discussion with a friend, I wrote a test to flush out the API I planned:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testConnectingToServer() { </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> user = IRCUser(username: </span><span style="color:#a3be8c;">&quot;sgoodwin&quot;</span><span>, realName: </span><span style="color:#a3be8c;">&quot;Samuel Goodwin&quot;</span><span>, nick: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> server = IRCServer.connect(</span><span style="color:#a3be8c;">&quot;irc.freenode.org&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>, user: user) </span><span> </span><span> </span><span style="color:#b48ead;">struct</span><span> ServerDelegate: IRCServerDelegate { </span><span> </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> serverDelegate = ServerDelegate() </span><span> </span><span> server.delegate = serverDelegate </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> channel = server.join(</span><span style="color:#a3be8c;">&quot;clearlyafakechannel&quot;</span><span>) </span><span> </span><span> </span><span style="color:#b48ead;">struct</span><span> ChannelDelegate: IRCChannelDelegate { </span><span> </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> channelDelegate = ChannelDelegate() </span><span> channel.delegate = channelDelegate </span><span> channel.send(</span><span style="color:#a3be8c;">&quot;Hey sup everybody&quot;</span><span>) </span><span>} </span></code></pre> <p>A user needs a few different pieces of identifying information, so I grouped them into an <code>IRCUser</code> struct. Next I identified the two important conceptual pieces: a channel and a server. In most IRC apps, you're able to connect to multiple servers and join multiple channels. I thought treating each one as a separate object would be easier to work with than having a single global object and needing to use functions that say things like, "Send this text (a string) to this channel (also a string)" and to have delegate methods that say things like, "You got this message (a string) from this channel (also a string)". Whoever uses this library later would have to do more work to keep up with which channels and servers a user is connected to and I didn't like that so much. With separate objects, a developer could hand each channel and server object to a different controller responsible for displaying the information from that one object. It was also reassuring to know that this doesn't need to be permanent. I can change anything later and I'll have tests to be confident it still works.</p> <p>Of course this test won't even compile right away. I needed to make the classes and protocols mentioned in the test so the compiler wasn't sad they don't exist.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">struct</span><span> IRCUser { </span><span> </span><span style="color:#b48ead;">let</span><span> username: String </span><span> </span><span style="color:#b48ead;">let</span><span> realName: String </span><span> </span><span style="color:#b48ead;">let</span><span> nick: String </span><span>} </span><span> </span><span style="color:#b48ead;">class</span><span> IRCChannel { </span><span> </span><span style="color:#b48ead;">var</span><span> delegate: IRCChannelDelegate? </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>send(_ text: String) { </span><span> </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">class</span><span> IRCServer { </span><span> </span><span style="color:#b48ead;">var</span><span> delegate: IRCServerDelegate? </span><span> </span><span> </span><span style="color:#b48ead;">static func </span><span>connect(_ hostname: String, port: Int, user: IRCUser) -&gt; IRCServer { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCServer() </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">func </span><span>join(_ channelName: String) -&gt; IRCChannel { </span><span> </span><span style="color:#b48ead;">return</span><span> IRCChannel() </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">protocol</span><span> IRCServerDelegate { </span><span> </span><span>} </span><span> </span><span style="color:#b48ead;">protocol</span><span> IRCChannelDelegate { </span><span> </span><span>} </span></code></pre> <p>At this point I only filled in the minimum required bits. This wouldn't actually do anything, but the shell of a working thing was there. Now I just needed to cram some innards in there and make it walk.</p> <p>The work continues in <a href="http://roundwallsoftware.com/how-to-write-irc-part-4/">How To Write IRC: Part 4</a></p> How To Write IRC: Part 2 Mon, 21 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-2/ https://roundwallsoftware.com/blog/how-to-write-irc-part-2/ <p>This is a continuation of the article published yesterday: <a href="http://roundwallsoftware.com/how-to-write-irc-part-1/">How To Write IRC: Part 1</a></p> <h1 id="spike">Spike!</h1> <p>When I write a framework, I don't start by making a framework. First I mess around and build what you might call a "spike" to get an vague idea how things work. I was fairly familiar with the IRC protocol when I started, but not this new API from Apple. Writing something right off the bat with tests is quite difficult if you're not even very sure what you need to make your code do. After some time to play around, I had a better sense of direction and could be confident writing tests.</p> <h1 id="walking-skeleton">Walking Skeleton</h1> <p>Last thing I needed before I could write any tests was the skeleton of an app. You can see what I wrote in <a href="https://github.com/sgoodwin/IRCFramework/commit/ce6936e7e3827b96c55b145aac09a9ae279d3e19">this initial commit</a> on Github. Mostly it is Xcode's new-project template for a Mac app, with this I added two methods based on my experience with the spike. One to send data:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">private func </span><span>send(_ message: String) { </span><span> print(</span><span style="color:#a3be8c;">&quot;queing message </span><span>\(message)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> task.write((message + </span><span style="color:#a3be8c;">&quot;</span><span style="color:#96b5b4;">\r\n</span><span style="color:#a3be8c;">&quot;</span><span>).data(using: .utf8)!, timeout: </span><span style="color:#d08770;">0</span><span>) { (error) </span><span style="color:#b48ead;">in </span><span> </span><span style="color:#b48ead;">if let</span><span> error = error { </span><span> print(</span><span style="color:#a3be8c;">&quot;Failed to send: </span><span>\(String(describing: error)</span><span style="color:#a3be8c;">)&quot;</span><span>) </span><span> } </span><span style="color:#b48ead;">else</span><span> { </span><span> print(</span><span style="color:#a3be8c;">&quot;Sent!&quot;</span><span>) </span><span> } </span><span> } </span><span> } </span></code></pre> <p>and one to receive data:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">private func </span><span>read() { </span><span> task.readData(ofMinLength: </span><span style="color:#d08770;">0</span><span>, maxLength: </span><span style="color:#d08770;">9999</span><span>, timeout: </span><span style="color:#d08770;">0</span><span>) { (data, atEOF, error) </span><span style="color:#b48ead;">in </span><span> </span><span style="color:#b48ead;">if</span><span> atEOF { </span><span> print(</span><span style="color:#a3be8c;">&quot;Connection&#39;s done!&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">return </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">guard let</span><span> data = data, </span><span style="color:#b48ead;">let</span><span> message = String(data: data, encoding: .utf8) </span><span style="color:#b48ead;">else</span><span> { </span><span> print(</span><span style="color:#a3be8c;">&quot;No data!&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">return </span><span> } </span><span> </span><span> print(message) </span><span> </span><span style="color:#b48ead;">self</span><span>.read() </span><span> } </span><span>} </span></code></pre> <p>I knew these would change and would definitely be moved out of the view controller, but that wasn't super important right from the start. In a game of test-driven development, <strong>nothing</strong> needs to be done perfectly the first time. Everything is meant to change, and when it's time to change there will be tests to make sure the changes don't ruin expected behavior. I did include two assumptions in here based on my experience with the spike:</p> <ol> <li>When sending data, I knew that IRC servers expect you to mark the end of your message with a return and newline ( \r\n ), so I added it in the send method. This way I wouldn't need to remember to always add it to the end of messages I want to send.</li> <li>When reading the data, I know data needs to be handled as UTF8 encoded strings. Once reading is done (and I just print the message for now), I schedule another read.</li> </ol> <h1 id="now-tests">Now tests!</h1> <p>Everything was all set to try to write some tests and make some stuff work. I decided a good place to start would be handling the incoming messages from the server. In the IRC protocol, messages from the server can mean different kinds of things: someone joined a channel, a message came across a channel, or the server itself needed to comunicate some status, etc. Some of these messages will need to be handled by our framework; for example, if the server sends the message "PING", we must immediately respond with "PONG" to prevent the server from disconnecting us. This isn't something I'd expect users to do, so I'll make the framework do it automatically.</p> <p>First test: I figured an easy first test would be detecting the "PING" message from the server. There's no variation like in other possible kinds of messages, so the test is fairly simple:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testPingMessage() { </span><span> </span><span style="color:#b48ead;">let</span><span> input = parseServerMessage(</span><span style="color:#a3be8c;">&quot;PING&quot;</span><span>) </span><span> </span><span> XCTAssertEqual(input, .ping) </span><span>} </span></code></pre> <p>To make this compile, I added a <code>parseServerMessage</code> function, it took a string and generated an enumerable. I thought enums with possible associated values would be a nice way to represent messages from the server and make them easy to act on, so I also added an enum with a single case, <code>ping</code>. Once this compiled, making the test pass wasn't too bad:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>parseServerMessage(_ message: String) -&gt; IRCServerInput { </span><span> </span><span style="color:#b48ead;">if</span><span> message == </span><span style="color:#a3be8c;">&quot;PING&quot;</span><span> { </span><span> </span><span style="color:#b48ead;">return </span><span>.ping </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span>.channelMessage(channel: </span><span style="color:#a3be8c;">&quot;sup&quot;</span><span>, message: </span><span style="color:#a3be8c;">&quot;bro&quot;</span><span>) </span><span>} </span></code></pre> <p>If we detect a ping, return <code>.ping</code>, if we don't, return some garbage for now. This made the test pass and made me smile. From here I made the next test:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testChannelMessage() { </span><span> </span><span style="color:#b48ead;">let</span><span> input = parseServerMessage(</span><span style="color:#a3be8c;">&quot;PRIVMSG #clearlynotarealchannel :this is so cool&quot;</span><span>) </span><span> </span><span> XCTAssertEqual(input, IRCServerInput.channelMessage(channel: </span><span style="color:#a3be8c;">&quot;clearlynotarealchannel&quot;</span><span>, message: </span><span style="color:#a3be8c;">&quot;this is so cool&quot;</span><span>)) </span><span>} </span></code></pre> <p>Conveniently, I had the log from my spike where I connected to a server and got messages, so I had plenty of examples of what the server would send. It wasn't until typing this article after writing the test that I realized the test was invalid. A <code>PRIVMSG</code> from the server actually looks like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>:[email protected] PRIVMSG #clearlynotarealchannel :this is so cool </span></code></pre> <p>There's a bit before the keyword <code>PRIVMSG</code> because otherwise, how would you know <em>who</em> sent the message? Silly me. The process to making the test pass with the correct string was similar, however. I added a case to my enum to reflect this new type of message so I could get to making the test pass by modifying my parse function.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#65737e;">// The new parse function looks like this: </span><span style="color:#b48ead;">static func </span><span>parseServerMessage(_ message: String) -&gt; IRCServerInput { </span><span> </span><span style="color:#b48ead;">if</span><span> message == </span><span style="color:#a3be8c;">&quot;PING&quot;</span><span> { </span><span> </span><span style="color:#b48ead;">return </span><span>.ping </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">if</span><span> message.hasPrefix(</span><span style="color:#a3be8c;">&quot;:&quot;</span><span>) { </span><span> </span><span style="color:#b48ead;">let</span><span> firstSpaceIndex = message.index(of: </span><span style="color:#a3be8c;">&quot; &quot;</span><span>)! </span><span> </span><span style="color:#b48ead;">let</span><span> source = message.substring(to: firstSpaceIndex) </span><span> </span><span style="color:#b48ead;">let</span><span> rest = message.substring(from: firstSpaceIndex).trimmingCharacters(</span><span style="color:#b48ead;">in</span><span>: .whitespacesAndNewlines) </span><span> print(source) </span><span> </span><span> </span><span style="color:#b48ead;">if</span><span> rest.hasPrefix(</span><span style="color:#a3be8c;">&quot;PRIVMSG&quot;</span><span>) { </span><span> </span><span style="color:#b48ead;">let</span><span> remaining = rest.substring(from: rest.index(message.startIndex, offsetBy: </span><span style="color:#d08770;">8</span><span>)) </span><span> </span><span> </span><span style="color:#b48ead;">if</span><span> remaining.hasPrefix(</span><span style="color:#a3be8c;">&quot;#&quot;</span><span>) { </span><span> </span><span style="color:#b48ead;">let</span><span> split = remaining.components(separatedBy: </span><span style="color:#a3be8c;">&quot;:&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">let</span><span> channel = split[</span><span style="color:#d08770;">0</span><span>].trimmingCharacters(</span><span style="color:#b48ead;">in</span><span>: CharacterSet(charactersIn: </span><span style="color:#a3be8c;">&quot; #&quot;</span><span>)) </span><span> </span><span style="color:#b48ead;">let</span><span> user = source.components(separatedBy: </span><span style="color:#a3be8c;">&quot;!&quot;</span><span>)[</span><span style="color:#d08770;">0</span><span>].trimmingCharacters(</span><span style="color:#b48ead;">in</span><span>: CharacterSet(charactersIn: </span><span style="color:#a3be8c;">&quot;:&quot;</span><span>)) </span><span> </span><span style="color:#b48ead;">let</span><span> message = split[</span><span style="color:#d08770;">1</span><span>] </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span>.channelMessage(channel: channel, user: user, message: message) </span><span> } </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span>.unknown(raw: message) </span><span>} </span></code></pre> <p>Sure it was a little messy looking and normally I'd cringe at so many <code>if</code> statements inside <code>if</code> statements, but that's ok! I could always decide later there's a neater way to solve this, and I would be able to make that change knowing my tests still pass and I hadn't ruined everything. I knew I would need to modify this function even further as there were a few other types of message I would need to handle. Typically, as I try to make code like this handle all the possible outcomes, I find a nicer way to solve the issue once I have more information. You'll notice I added another little case <code>unknown</code> because there's plenty of messages that I don't handle properly yet and I don't want to incorrectly mark them as something they're not.</p> <p>Next I tried to handle server messages properly. These are direct communications from the server such as welcome messages when you first connect. The test used a line from the logs I kept while playing around before:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testServerMessage() { </span><span> </span><span style="color:#b48ead;">let</span><span> input = IRCServerInputParser.parseServerMessage(</span><span style="color:#a3be8c;">&quot;:tolkien.freenode.net 001 mukman :Welcome to the freenode Internet Relay Chat Network mukman&quot;</span><span>) </span><span> </span><span> XCTAssertEqual(input, IRCServerInput.serverMessage(server: </span><span style="color:#a3be8c;">&quot;tolkien.freenode.net&quot;</span><span>, message: </span><span style="color:#a3be8c;">&quot;Welcome to the freenode Internet Relay Chat Network mukman&quot;</span><span>)) </span><span>} </span></code></pre> <p>First run of the test failed, as expected (if it didn't, then I'd be very confused and concerned). The parser incorrectly flagged the message as unknown when it should be one it knows. This required a little modification to the if statements I had previously:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">static func </span><span>parseServerMessage(_ message: String) -&gt; IRCServerInput { </span><span style="color:#65737e;">/* Old stuff was here */ </span><span> } </span><span style="color:#b48ead;">else</span><span>{ </span><span> </span><span style="color:#65737e;">// This is where the new stuff is, server messages start with a &quot;:&quot; but don&#39;t have PRIVMSG </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> server = source.trimmingCharacters(</span><span style="color:#b48ead;">in</span><span>: CharacterSet(charactersIn: </span><span style="color:#a3be8c;">&quot;: &quot;</span><span>)) </span><span> </span><span style="color:#b48ead;">let</span><span> message = rest.components(separatedBy: </span><span style="color:#a3be8c;">&quot;:&quot;</span><span>)[</span><span style="color:#d08770;">1</span><span>] </span><span> </span><span style="color:#b48ead;">return </span><span>.serverMessage(server: server, message: message) </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">return </span><span>.unknown(raw: message) </span><span>} </span></code></pre> <p>A small addition to the parsing function made the test pass. A fun part about test-driven development like this is: once you get the hang of it and start getting some tests to pass, it feels like you're on an awesome train to feature-delivery-town and it's picking up speed.</p> <p>My initial plan for this IRC library was to support a very minimal set of functionality initially so that I could test building an app with the library with a friend who had some UI ideas. So far the library understood server messages, channel messages, and handled the important PING message. In order to connect to a server, join the a channel, and speak, I also needed to handle the <code>JOIN</code> message. This informs the client that either they or someone else has joined a specific channel. As always, the test includes a line from my spike's logs:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>testJoinMessage() { </span><span> </span><span style="color:#b48ead;">let</span><span> input = IRCServerInputParser.parseServerMessage(</span><span style="color:#a3be8c;">&quot;:[email protected] JOIN #clearlyatestchannel</span><span style="color:#96b5b4;">\r\n</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> </span><span> XCTAssertEqual(input, IRCServerInput.joinMessage(user: </span><span style="color:#a3be8c;">&quot;mukman&quot;</span><span>, channel: </span><span style="color:#a3be8c;">&quot;clearlyatestchannel&quot;</span><span>)) </span><span>} </span></code></pre> <p>With this, a client could know someone joined a channel. I handled this the same as basically every other message, first I added the case to the <code>IRCServerInput</code> enum.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">case</span><span> joinMessage(user: String, channel: String) </span></code></pre> <p>Something different happens here though, the test doesn't fail, but causes the whole thing to crash.</p> <p><img src="/blog/Screen-Shot-2017-07-30-at-11.54.46.png" alt="IMAGE OF THE CRASH HERE" /></p> <p>It looked like the parsing function wrongfully thought the <code>JOIN</code> message was a server message. I needed to disambiguate them. I tried adding another <code>if</code> statement into my mess and it makes the test fail instead of crash:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>} </span><span style="color:#b48ead;">else if</span><span> rest.hasPrefix(</span><span style="color:#a3be8c;">&quot;JOIN&quot;</span><span>) { </span><span> print(</span><span style="color:#a3be8c;">&quot;This is a join message&quot;</span><span>) </span><span>} </span></code></pre> <p>This was a good enough sign to me that we were in the right place and I could try to handle the rest of the string and assume it's definitely the <code>JOIN</code> message. Adding this bit to the new if statement:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">let</span><span> channel = rest.substring(from: rest.index(message.startIndex, offsetBy: </span><span style="color:#d08770;">5</span><span>)) </span><span style="color:#b48ead;">return </span><span>.joinMessage(user: source, channel: channel) </span></code></pre> <p>almost got the test to pass. The error message showed the issue remaining:</p> <pre data-lang="log" style="background-color:#2b303b;color:#c0c5ce;" class="language-log "><code class="language-log" data-lang="log"><span>XCTAssertEqual failed: (&quot;joinMessage(user: &quot;:[email protected]&quot;, channel: &quot;#clearlyatestchannel&quot;)&quot;) is not equal to (&quot;joinMessage(user: &quot;mukman&quot;, channel: &quot;clearlyatestchannel&quot;)&quot;) </span></code></pre> <p>So was correctly identifying the <code>JOIN</code> message, except my test expected the joining user to be identified as <code>mukman</code> but the parse function thought the username was <code>:mukman [email protected]</code>. I wasn't sure which is better to use to identify the user. The parsing function was returning a much more specific way to identify the user which could be useful if there were issues on an IRC server somewhere with multiple users having the same nickname. I decided to make it convenient and chop everything off to use only the simple bit. (It was also consistent with the way <code>PRIVMSG</code> is handled). Tests passed! You can see all the code up to this point at <a href="https://github.com/sgoodwin/IRCFramework/commit/062b9fbe801552af859f64fb934c2e2aaa881f3e">this commit</a>.</p> <p>Hooray! My minimum set of messages from the server were now identified and processed. Running my ugly demo program also showed that no messages from the server were causing crashes so far, which was good news as well. From here I could start to trying to organize things into a useful, usable thing instead of random functions scattered around a view controller and such.</p> <p>The work continues in <a href="http://roundwallsoftware.com/how-to-write-irc-part-3/">How To Write IRC: Part 3</a></p> How To Write IRC: Part 1 Sun, 20 Aug 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-write-irc-part-1/ https://roundwallsoftware.com/blog/how-to-write-irc-part-1/ <p>IRC is a fairly old protocol for chatting with people on the internet. It's not super popular these days because even more technical people have migrated to things like Slack or Hipchat which may or may not be more friendly to use. Still, I decided to try to write a library for interacting with IRC for a few reasons:</p> <ol> <li>I think a big reason for Slack/Hipchat popularity over IRC is the clients. Every IRC client I've ever tried seems fairly technical and does not try to simplify the experience very much.</li> <li>The library and my writing explaining it can serve as nice example of how to make a framework and how to write Swift in general using tests. Even if it never gets used in an exciting IRC client.</li> <li>The library can also serve as an example for implementing support for an internet protocol that isn't already supported for the system (like HTTP(S) is already supported).</li> </ol> <p>Once I was able to rationalize spending time on this myself, I decided on the features for the initial milestone:</p> <ol> <li>It needs to connect to IRC servers.</li> <li>It needs to allow joining channels.</li> <li>It needs to allow sending and receiving messages on those channels.</li> </ol> <p>There are different strategies for making frameworks, but this is how I do it:</p> <ol> <li>First, make IRC code work in a demo app.</li> <li>Second, extract out the IRC bits into a framework that's usable outside the demo app</li> <li>Third, forget about the demo app because it's probably ugly.</li> </ol> <h1 id="api-options">API options</h1> <p>For my implementation of an IRC client library, I decided to try to use the new <code>URLSessionSteamTask</code> API which they seem to want us to use instead of older <code>NSSTream</code> APIs or directly through Grand Central Dispatch which I've tried to use <a href="https://github.com/sgoodwin/Turbo-Mud">before</a>. I previously thought the GCD way wasn't too bad, but this new stuff was even easier.</p> <p>There's not much to the API, you can ask a <code>URLSession</code> to make a streaming task with</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>streamTask(withHostName: </span><span style="color:#a3be8c;">&quot;irc.freenode.org&quot;</span><span>, port: </span><span style="color:#d08770;">6667</span><span>) </span></code></pre> <p>only you'd need to put in your desired hostname and port, not mine. (If you're not sure what a hostname or port is, no worries, maybe read <a href="https://en.wikipedia.org/wiki/Network_socket">this article</a> first.) From there you have a task to resume like any other <code>URLSessionTask</code> except this one lets you also schedule reading data or writing data on the <code>URLSession</code>'s queue. To read from your connection, you say</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>readData(ofMinLength minBytes: Int, maxLength maxBytes: Int, timeout: TimeInterval, completionHandler: </span><span style="color:#b48ead;">@escaping</span><span> (Data?, Bool, Error?) -&gt; Swift.Void) </span></code></pre> <p>which tells the task how much data to read, how long to wait, and lets you provide a closure to do whatever you like with the data once it's ready. Writing is very similar,</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>write(_ data: Data, timeout: TimeInterval, completionHandler: </span><span style="color:#b48ead;">@escaping</span><span> (Error?) -&gt; Swift.Void) </span></code></pre> <p>which tells the task what data to send, how long to wait, and a closure to do something if there's a problem. With this, some explanations of the IRC protocol, and a bunch of unit tests, I set out to build an IRC framework.</p> <p>The work continues in <a href="http://roundwallsoftware.com/how-to-write-irc-part-2/">How To Write IRC: Part 2</a></p> Making SwiftKilo Part 2: Raw mode Sat, 13 May 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/making-swiftkilo-part-2-raw-mode/ https://roundwallsoftware.com/blog/making-swiftkilo-part-2-raw-mode/ <p>In the next chapter of <a href="http://viewsourcecode.org/snaptoken/kilo/02.enteringRawMode.html">the tutorial</a>, I went through the steps to enable raw mode. In a typical command line app, you print some things on the screen and maybe wait for input for the user on the next line. The terminal is setup by default to send your program this input one line at a time which means waiting until the user hits return/enter. For a text editor where we want to handle things like keyboard shortcuts and special characters, this isn't good enough. Fortunately, the terminal can be told to change its behavior and send each individual keypress to your program one at a time. Unfortunately, it takes several steps to do this. Also, since you're changing the behavior of your terminal for your program, you need to make sure you turn the settings back to normal when your program is done.</p> <p>Many of the functions used to read and write input from the terminal operate on CChar's and arrays of CChars. These are ascii characters and not the same as Swift's Character which understands unicode. Technically CChar's are just 8-bit integers that happens to correlate with ascii values. C has many "types" that are basically just some kind of integer and a decent amount of the code in this app relies on not really caring about the difference. For example: in later chapters we compare a CChar (which is an Int8) to numbers like 10000 which can't be stored in an Int8 because it's entirely too large. In C this is fine, but in Swift you'll have to do a bunch of converting between Int types. I wouldn't be suprised if this fast-and-loose handling of number types in C code is the root cause of many of the exploits and crashes you hear about in the news.</p> <p>In Swift, when you work with Characters, you can make new ones with the string literal like so:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">let</span><span> someChar: Character = </span><span style="color:#a3be8c;">&quot;s&quot; </span><span> </span></code></pre> <p>There is no support by default for doing the same thing with CChars which made things a bit trickier to write in Swift. Fortunately, Swift allows you to enable creating literally anything from string literals (or other literals like <code>nil</code> or <code>134</code>). I added my own fairly lazy implementation which seems to work fine for CChar:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">extension</span><span> CChar: ExpressibleByStringLiteral { </span><span> </span><span style="color:#b48ead;">public typealias</span><span> StringLiteralType = String </span><span> </span><span style="color:#b48ead;">public typealias</span><span> ExtendedGraphemeClusterLiteralType = Character </span><span> </span><span style="color:#b48ead;">public typealias</span><span> UnicodeScalarLiteralType = UnicodeScalar </span><span> </span><span> </span><span style="color:#b48ead;">public init</span><span>(stringLiteral value: String) { </span><span> </span><span style="color:#b48ead;">self</span><span>.</span><span style="color:#b48ead;">init</span><span>(value.utf8CString[</span><span style="color:#d08770;">0</span><span>]) </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">public init</span><span>(extendedGraphemeClusterLiteral value: Character) { </span><span> </span><span style="color:#b48ead;">self</span><span>.</span><span style="color:#b48ead;">init</span><span>(stringLiteral: String(value)) </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">public init</span><span>(unicodeScalarLiteral value: UnicodeScalar) { </span><span> </span><span style="color:#b48ead;">self</span><span>.</span><span style="color:#b48ead;">init</span><span>(stringLiteral: String(value)) </span><span> } </span><span> </span><span>} </span><span> </span></code></pre> <p>Creating something from a string literal in Swift means handling unicode characters because Swift lets us make strings with values like "🕺🏾". CChar's can't represent those anyway, so I figure attempting to make a string from the literal and then grabbing the very first character is good enough. There are probably much better ways to do this.</p> <p>Even with this added convenience, there are still oddities to deal with. A handy function <code>iscntrl</code> can be used to determine if a character is a regular keypress or the combination of control and another key (which would be represented by a single ascii character, not by a seqence or characters). You would think a C funcion like this was made to accept CChars as input since other C functions like <code>read</code> work with CChars, but you'd be wrong. <code>iscntrl</code> expects an Int32 which is not a CChar. I'm not sure why this is, but fortunately you can make Int32's out of Int8's easily (though it does look a bit disappointing when you read the code later).</p> <p>At this stage, the app doesn't do much but print in the console whether a key is a control character or a printable letter each time a user presses a button. To get this far though, you'll need to learn about terminal configuration with <code>termios</code> and gain at least a general understanding of string encoding. I knew about strings personally, but had no idea about the rest, so it was fun for me to learn. At this point in my project, I made my first commit, you can see it on <a href="https://github.com/sgoodwin/SwiftKilo/commit/d546e2b18dca463149b7368c5fe0a66891c5186c#diff-907fb0ef7fdf3d97b4e235dfc7abb254">github</a>. Tune in next time for more fun with low level Swift!</p> Core Data unique constraints Fri, 05 May 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/core-data-unique-constraints/ https://roundwallsoftware.com/blog/core-data-unique-constraints/ <p>There's a bug with Core Data such that if you have two entities, <code>A</code> and <code>B</code>, with a one-to-one relationship between them, that relationship will not be setup properly. This bug doesn't care wether you do your work in Objective-C or Swift or both.</p> <p>The steps to reproduce are as follows:</p> <ul> <li>Create two entities, one <code>A</code> and one <code>B</code>.</li> <li>Give each a property, such as <code>name</code>.</li> </ul> <ol start="3"> <li>Add a one-to-one relationship between the two.</li> <li>Generate the class file for each by any method you wish. Doesn't matter which.</li> <li>In your app, create an instance of each and link them by their relationship property.</li> </ol> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">let</span><span> context = persistentContainer.viewContext </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> a = A(context: context) </span><span> a.name = </span><span style="color:#a3be8c;">&quot;I&#39;m alive&quot; </span><span> </span><span style="color:#b48ead;">let</span><span> b = B(context: context) </span><span> b.name = </span><span style="color:#a3be8c;">&quot;I&#39;m also alive&quot; </span><span> a.thing = b </span><span> </span><span> </span><span style="color:#b48ead;">try</span><span>! context.save() </span></code></pre> <ul> <li>Fetch all <code>A</code> entities and print out their <code>B</code>'s. Do the same the other away around too.</li> </ul> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span> </span><span style="color:#b48ead;">let</span><span> context = container.viewContext </span><span> </span><span> </span><span style="color:#b48ead;">let</span><span> request = A.fetchRequest() as NSFetchRequest&lt;A&gt; </span><span> </span><span style="color:#b48ead;">let</span><span> allA = </span><span style="color:#b48ead;">try</span><span>! context.fetch(request) </span><span> print(allA.map({ $</span><span style="color:#d08770;">0</span><span>.thing?.name })) </span><span> </span><span style="color:#b48ead;">let</span><span> bRequest = B.fetchRequest() as NSFetchRequest&lt;B&gt; </span><span> </span><span style="color:#b48ead;">let</span><span> allB = </span><span style="color:#b48ead;">try</span><span>! context.fetch(bRequest) </span><span> print(allB.map({ $</span><span style="color:#d08770;">0</span><span>.otherThing?.name })) </span></code></pre> <ul> <li>Notice how nothing is nil, as you'd expect, in the console output. This is what we expect.</li> </ul> <ol start="8"> <li>Every time you run this code, you'll see more items in the output.</li> <li>To prevent all these extra duplicates, let's give them a unique constraint, to both the <code>A</code> and <code>B</code> entities. For this to work, you'll also need to change the merge policy of the context.</li> </ol> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>context.mergePolicy = NSMergePolicy.mergeByPropertyObjectTrump </span></code></pre> <ul> <li>Now, notice how it's broken. Your entity properties will show as nil even though we know it should work just fine. This is the bug!</li> </ul> <ol start="11"> <li>To make things work again, remove the unique constraint on <code>B</code>. Now everything works just fine. Tada!</li> </ol> <p>There's an example project displaying this bug on <a href="https://github.com/sgoodwin/CoreDataBug">github</a>.</p> Making SwiftKilo Part 1: Setup Sat, 29 Apr 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/making-swiftkilo-part-1-setup/ https://roundwallsoftware.com/blog/making-swiftkilo-part-1-setup/ <p>Some time ago, I read about AntiRez writing a <a href="http://antirez.com/news/108">text editor in C</a>. This dude is pretty hardcore in C and does things like maintain Redis, an in-memory database nearly every startup you ever heard of is using or will soon use. It was super cool to flip through and read about the editor. I was especially impressed by his ability to add search and syntax highlighting even. Some time later, <a href="https://github.com/snaptoken">Snaptoken</a> wrote a very thorough tutorial explaining how to make the editor step by step instead of browsing through Antirez's final product. At this point I thought it might be interesting to follow along with the tutorial, but to do it in Swift and write about the differences. Maybe the Swift version will take less lines of code? Maybe more?</p> <p>The differences between the Swift and C versions of this editor start right at the beginning of <a href="http://viewsourcecode.org/snaptoken/kilo/01.setup.html">this</a> tutorial. Setup for Swift projects is quite different, you can read about it on <a href="https://swift.org/getting-started/#using-the-package-manager">Swift.org</a>. With this, we can skip past writing Makefiles and stuff because we don't care! Another major difference is that Swift programs don't get a main function to execute. There is <code>main.swift</code> which gets executed a bit like a script. For my own sanity, I made a main function myself and just executed that function right after. This somehow felt useful to me.</p> <p>For those of you who are iOS or Mac developers, there are a few differences in a project like this you'll need to get your head around:</p> <ol> <li>You'll probably be writing Swift in an editor that isn't Xcode. To help make up for other-editor's lack of code completion (which helps a ton when you're not sure how C API's were translated for Swift), I kept open a Playground file like this to test thing:</li> </ol> <p><img src="/blog/Screen-Shot-2017-04-29-at-21.57.19.png" alt="Screenshot of Xcode Playgrounds file with random gibberish" /></p> <ol start="2"> <li>Since you aren't using a regular Xcode project (turns out the Swift Package Manager has a way to generate an Xcode project you can use, but I couldn't figure out how to make it work since I'm making a CLI app here and not a Swift library), you'll also get to become familiar with using lldb, the debugger we all know and love since back when gdb was the cool thing, directly. <a href="https://medium.com/engineering-housing/developing-and-debugging-swift-packages-using-swift-package-manager-a7e4a1c65528">This article by Ankit Agarwal</a> was very helpful for getting my bearings. The output of lldb will often get a little squirrely since you'll be messing with how the terminal behaves and you're debugging your CLI app in the same terminal as you're using lldb to fix it. Hilarity will ensue and you'll find all kinds of off-by-one errors.</li> </ol> <p>Yay, now you're setup for super cool potentially-cross-platform Swift projects. Good luck! If you want to see how I did things in this project, you can find it on <a href="https://github.com/sgoodwin/SwiftKilo">github</a></p> You Belong Here Sat, 08 Apr 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/you-belong-here/ https://roundwallsoftware.com/blog/you-belong-here/ <p>Everyone around you right now, all those people who intimidate you or make you feel like you're a fraud have been where you are now. Nobody leaped from a test-tube as a fully-formed senior developer. They all had to start with no clue and find out how to move forward. Many of those people remember what it's like to be where you are and will be happy to help get you moving. The others you can ignore because they're either heartless monsters or drowning in their own issues.</p> <p>Even those of us who have been at it for a while experience what we call "Imposter Syndrome". That's where you're an experienced developer and still feel like maybe you have no clue what you're doing and everyone around you might soon figure that out. A career in software development can mostly be a career spent starting things you don't understand (mabye at all) and figuring it out as you go. If you knew exactly what needed to be done up front, not only would this be fairly boring for you, there wouldn't be much value in it for your employer/customer. If something already does exactly what they need, why would they be paying you to work on it?</p> <p>It's not exactly confidence-building to spend your days working on things you barely understand. Even worse when people like your boss understand it even less than you do. It doesn't have to be miserable though. Did you ever have that feeling as a kid of excitement as you discovered bits of information about some unknown topic? Learning about animals that don't exist near you, finding out about planets and solar systems beyond yours, or when you first found out how to make a computer do literally anything? This is the feeling we get to have all the time as software developers.</p> <p>Here's the best ways I know to make sure you survive and do well in this field:</p> <ol> <li> <p>Keep making progess. Think like a shark and keep moving, do not stand still. If you get stuck, reach out to people/organizations you can find and get help. For example: if you are anywhere near cities like Amsterdam here in the Netherlands, every Saturday morning there is free help available to you at a pleasant cafe in the Oud West. My friend maintains a list of other cities with helpful events like this here: <a href="http://peerlab.community/find/">http://peerlab.community/find/</a>. Go and ask for help. The "dumbest" question you can think of is not actually that dumb. When I was learning Objective-C years ago, I asked questions where the answer was, "add a semicolon at the end of this line", for <em>weeks</em>. Fortunately I found people nice enough to give that answer without calling me an idiot (thanks guys, I appreciate it alot).</p> </li> <li> <p>No really, keep making progress. Lay out 30 minutes every day to make some progress. A little progress every day is way more valuable than 4 hours on Sunday. Life often gets in the way anyway and the 4 hours you planned on Sunday often turns into 15 minutes and now you've gone another week making only a little progress.</p> </li> <li> <p>Be nice to yourself. What you are doing is really hard. Don't expect it to be easy. Don't beat yourself up when you don't instantly understand or when you make the same mistake today that you made last week. The world is plenty mean and terrible enough without you piling on yourself. Be nice, take care of yourself, and give yourself a chance to do this.</p> </li> </ol> Formatter Fri, 06 Jan 2017 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/formatter/ https://roundwallsoftware.com/blog/formatter/ <p>If you work with JSON files in your job on a regular basis, you will likely benefit from my new app, <a href="https://t.co/HOJPyOTLVS">Formatter</a>.</p> <p>It provides a drop window to reformat JSON files quickly, an Xcode extension to do it from inside Xcode (to which you can assign a keyboard shortcut), and a Quick Look plugin to see formatted previews of files on your computer.</p> <p>It has an adorable icon created by the talented <a href="https://bloomingbridges.gitlab.io">Florian</a>. I arbitrarily picked purple as a color, he took care of the rest. He does good work, you should hire him.</p> <h2 id="drop-window">Drop Window</h2> <img src="/content/images/2017/01/drag.png" width="400" alt="Formatter app screenshot with file hovering over the window." /> <p>Formatter provides a convenient window to drop your JSON files and instantly reformat them. You can also drop your files on the Dock icon as well. It will be done before you have a chance to open up Facebook or get up for coffee!</p> <h2 id="xcode-extension">Xcode Extension</h2> <p>Formatter also includes an Xcode extension which you can enable in System Preferences. This gives a menu item in Xcode you can use to format JSON files you are looking at from inside your project. All those JSON files you included for your unit tests will be nice and readable with minimal effort.</p> <p>It will turn this:</p> <p><img src="/blog/xcode-before.png" alt="Formatter screenshot showing unformatted JSON file in Xcode." /></p> <p>Into this:</p> <p><img src="/blog/xcode-after.png" alt="Formatter screenshot showing formatted JSON file in Xcode." /></p> <p>All with a menu item. You can assign any hotkey you want to this and quickly reformat your JSON without reaching for the mouse.</p> <h2 id="quick-look">Quick Look</h2> <p>If you're browsing through your files looking for a specific JSON file, Formatter provides a Quick Look plugin to make life a bit easier for you. Tap space bar and you'll see a nicely-formatter preview of your JSON file so you can see if that's the file you're looking for.</p> <p><img src="/blog/Screen-Shot-2017-01-02-at-10.17.05.png" alt="Screenshot showing Quick Look plugin&#39;s preview of example JSON file." /></p> <p>Much easier to see if this is the file you were looking for.</p> <center>It's available now! <br/><a href="https://itunes.apple.com/us/app/formatter/id1190228172?mt=12"> <img src=/content/images/2017/01/Download_on_the_App_Store_Badge_US-UK_135x40.svg width="300" height="100" alt="Available on the app store"/></a></center> Turning 30 Tue, 27 Sep 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/turning-30/ https://roundwallsoftware.com/blog/turning-30/ <p>The people I looked up to when I learned to program are now getting to about 50 and I've seen many of them suffer from stress and endless sitting. I've seen some try to do something about it and several succeed even. This has motivated me to make sure I take care of myself. In high school I was forced to play sports (which turned out to be a good thing). In college I discovered skateboarding and loved doing it so much that I never had to think about keeping active. Lately I've taken specific action to make sure health and strength don't just accidentally happen. I've tried working with a personal trainer, capoeira training, and velodrome training, and have arrived at my own calisthenics plan which continues to make me stronger. This time last year I could not do one pull up, for example, to save my life on a good day. Now I can do more than 10. This has helped to improve everything else I do, even just sitting at a desk to work.</p> <p>I saw what happens when you do nothing. I saw how hard it is to get back to healthy once you're past the line. I never want to get to that point.</p> <p>My dad and my mom worked very hard to give my siblings and me a decent shot at life. Because of this, I've done what I could to take advantage of that situation. I have learned and grown, moved around, and been part of things like Appsterdam.</p> <p>Appsterdam is something I am very happy to be a part of and proud of my contributions to it. It is a volunteer organization: nothing happens if someone isn't willing to put the time and thought into making it happen. My event, Peer Lab, has been running for just over three years now. Every Saturday since then there has been a place in Amsterdam where anyone can come and talk to or get help from fellow software engineers. I've seen a number of people come through when they were first learning to program who now have jobs and/or their own apps in the store. Some people, like my friend <a href="https://ashfurrow.com">Ash Furrow</a>, have even taken the idea with them when they left Amsterdam to spread Peer Lab to other cities. Now when you go to New York, you can enjoy a Saturday morning (although a bit later-starting) with local developers at the Artsy office in Manhattan.</p> <p>Two things have changed the world for me as an adult: skateboarding and programming. Learning to program conveniently got me into a job I enjoy with control I'm lucky to have and a community that's great to be a part of. Learning to skateboard gave me the confidence and courage to take advantage of my position. Power is useless if you can't use it, skateboarding gave that to me. I stopped skateboarding after moving to Amsterdam because the weather and lack of nice bowls to skate (roundwall, if you will), but years of not skating seems to be not good for me. I just picked up a new board to fix this problem.</p> <p>Here's to turning 30. I'm in a good position in my career and health, surrounded by a global web of friends I enjoy, and I'm an uncle to a cool nephew and niece. In today's fairly awful world, I'm an extremely lucky guy and I'm glad you're a part of that.</p> Adventures in Vimscript Mon, 19 Sep 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/adventures-in-vimscript/ https://roundwallsoftware.com/blog/adventures-in-vimscript/ <p>Last week Vim 8.0 came out. I was pretty excited because, <a href="https://medium.com/@haya14busa/vim-8-0-released-and-now-im-a-contributor-of-vim-36d93bbfc7b6#.an9hyw2le">among other things</a>, Vim now supports asynchronously running code in plugins. Until now, if your plugin code took time (like compiling your project or some auto-completion code) it would cause Vim to freeze until it was finished. No typing, no moving the cursor, you could do nothing. This has been one of the things that sucked about Vim compared to the <a href="http://macromates.com">many</a> <a href="https://www.sublimetext.com">other</a> <a href="https://atom.io">good </a> <a href="http://www.barebones.com/products/BBedit/">editors</a> that exist. There are projects like <a href="https://neovim.io">neovim</a> which are attempts to make a version of Vim which could handle asynchronous plugins, but the last time I tried this it caused a kernel panic (I'm sure that's fixed by now). Now that Vim supports it out of the box, I was excited to dig in and see if I could live my dream of doing iOS development in Vim.</p> <p>There are a few things that are necessary for me to enjoy development with a compiled language like Swift.</p> <ul> <li>Syntax highlighting</li> <li>Autocomplete (the Cocoa frameworks are full of long method names)</li> <li>Buttons for compiling, testing, and running</li> </ul> <p>The vim-swift plugin from <a href="https://github.com/kballard/vim-swift">kballard</a> takes care of syntax highlighting, so that was easy. The next I thought would be easy was compiling, testing, and running. I had heard about <a href="https://github.com/gfontenot/vim-xcode">gfontenot's plugin</a> which reduces compiling, testing, and running your normally-Xcode-based-projects to simple commands like <code>:XBuild</code> that you could map to a key. A quick browse through the documentation showed that he even added support for supplying your own runner for the command it generated. The announcement for Vim 8.0 even mentioned a <a href="https://github.com/skywind3000/asyncrun.vim">plugin</a> that used Vim's new asynchronous abilities to run whatever command you wanted. It seemed like a convenient combination, use AsyncRun as the runner for Xbuild and go, go productivity.</p> <p>This didn't work and suddenly it was time to go into detective mode.</p> <DRAMATIC GIF> <p>I have a limited familiarity with vimscript (the language of plugins), so I spent a day breezing through <a href="http://learnvimscriptthehardway.stevelosh.com">Learn Vim The Hard Way</a> which was well written and wonderfully informative. I didn't follow the instructions, of course, but I got the idea. After browsing through the XBuild code (which was easier than normal because Gordon is cool and <a href="https://github.com/gfontenot/vim-xcode/issues/80">pointed out</a> where to start), I started by plugging the <code>echom</code> command everywhere to print out the value of variables and get a feel for which functions executed in which order. The plugin turned out to be pretty clear once you get into it.</p> <p>First thing I found is that the plugin relies on some shell scripts to determine some of the parameters needed for the actual compile command. These use the runner also, which doesn't work out so great because the plugin doesn't know to wait for the asynchronous runner to finish. Fortunately the plugin provides commands which allow you to manually supply those values and eliminate the need for the scripts.</p> <p>Once you skip the shell scripts, the plugin no longer hangs Vim, but you get a new problem: the final build command fails because of "command not found" errors in the shell. I tried asking <a href="https://github.com/skywind3000/asyncrun.vim">skywind3000</a> who made the AsyncRun plugin to make sure commands executed with AsyncRun run as the same user and in the same environment as me, the user. He confirmed that was true, so I knew there had to be some other explanation for why I was getting a "command not found" error. I even tried copying the full compiler command, which looks like this:</p> <pre data-lang="bash" style="background-color:#2b303b;color:#c0c5ce;" class="language-bash "><code class="language-bash" data-lang="bash"><span style="color:#96b5b4;">set </span><span style="color:#bf616a;">-o</span><span> pipefail; </span><span style="color:#bf616a;">NSUnbufferedIO</span><span>=</span><span style="color:#a3be8c;">YES </span><span style="color:#bf616a;">xcrun</span><span> xcodebuild build</span><span style="color:#bf616a;"> -project </span><span>&#39;</span><span style="color:#a3be8c;">./TheoryDrills.xcodeproj</span><span>&#39;</span><span style="color:#bf616a;"> -scheme </span><span>&#39;</span><span style="color:#a3be8c;">TheoryDrills</span><span>&#39;</span><span style="color:#bf616a;"> -destination </span><span>&quot;</span><span style="color:#a3be8c;">platform=iOS Simulator,name=iPhone SE</span><span>&quot; | </span><span style="color:#bf616a;">xcpretty --color </span></code></pre> <p>(you can see now whey you'd want a plugin to generate it for you) and running it in the terminal myself. "How could it work fine in the terminal, but not in the runner?" I asked myself. Finally it dawned on me. String escaping! When escaped "properly" the command works just fine when fed to AsyncRun.</p> <p>In the end, the solution to the problem was a bit of a bummer. AsyncRun couldn't be a runner for Xbuild without modifying Xbuild in ways that would break it for other runners. My solution, for now, was to add a mapping in my vimrc to compile and test my project with AsyncRun directly like so:</p> <pre data-lang="vim" style="background-color:#2b303b;color:#c0c5ce;" class="language-vim "><code class="language-vim" data-lang="vim"><span style="color:#96b5b4;">nnoremap </span><span>&lt;leader&gt;b :AsyncRun xcodebuild -scheme TheoryDrils -destination name=iPhone</span><span style="color:#b48ead;">\</span><span> SE,platform=iOS</span><span style="color:#b48ead;">\</span><span> Simulator </span><span style="color:#b48ead;">\</span><span>| xcpretty&lt;cr&gt; </span><span style="color:#96b5b4;">nnoremap </span><span>&lt;leader&gt;u :AsyncRun xcodebuild test -scheme TheoryDrills -destination name=iPhone</span><span style="color:#b48ead;">\</span><span> SE,platform=iOS</span><span style="color:#b48ead;">\</span><span> Simulator </span><span style="color:#b48ead;">\</span><span>| xcpretty&lt;cr&gt; </span></code></pre> <p>It's not as fancy, but it'll keep me working for now. In theory this could evolve a little bit to work with my client work too. Since the projects I work on are so simple, turning the now-hard-coded "TheoryDrills" part into a variable would work just fine. If I manage to get a working solution for autocomplete, Vim on iOS client work could be a dream achieved. My hope is that plugins will catch up to this fancy asynchronous behavior Vim has now and this will be easier some day.</p> Modifier Keys Sat, 10 Sep 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/modifier-keys/ https://roundwallsoftware.com/blog/modifier-keys/ <p>A keyboard big enough to have all the possible letters and symbols you would need to type when coding or even writing an email would need to be huge. You'd need a key for every letter, lower case and upper case, including the letters less common in English like é or æ, every symbol like , and ; and "", and other useful keys like return and backspace. Back in the day we figured out that this was going to be a bit absurd and different systems developed different ways to allow you to type all these things without a board with 200+ keys. While these systems differed, they converged on some similar concepts.</p> <p>In order to type more symbols with less keys, we started making use of modifier keys. Using one or more modifier keys in concert with other existing keys, you get many more letters and symbols than you get on your keyboard directly. You can also use them to send other useful commands to your computer like "undo" and "jump to the end of this line".</p> <h2 id="shift">Shift</h2> <p>Probably the most commonly used modifier key is shift. For letters, this allows you to type an "a" and a "A" with the same key. Pressing shift at the same time as the "a" key modifies the command and the computer displays an "A". This is also useful for symbols. Most labeled keyboards show two symbols, one (like ";") is the key you produce when you press that key by itself, the other (like ":") you get when you press that key using shift as a modifier key. You can also use shift to modify other modifier keys to allow for 3 and 4-key combinations; usually reserved for commands.</p> <h2 id="alt">Alt</h2> <p>Alt's (aka Option) exact use depends on your operating system and settings. Generally you use it in combination with letters to produce symbols like æ. In macOS this is less necessary since you can hold down keys like "e" and see a list of choices including ë and è. On Linux and Windows systems, typing these letters will require figuring out which keys in combination with alt you should press. Windows systems also use alt in combination with keys such as F4 to close an app's window. In macOS it also modifies the behavior of menu items and shortcuts in many apps. For example, in Safari, holding alt changes the "Close Tab" menu item to "Close Other Tabs". Alt will also turn the keyboard shortcut for "Close window" into "Close All Windows".</p> <h1 id="control">Control</h1> <p>Control is mostly used for commands and less for typing letters and symbols. In Windows, popular application-specific shortcuts like cut, copy, paste, and undo are all the control key combined with a letter (conveniently across the bottom row of a standard qwerty keyboard). In macOS, you use control to send Emacs (an old text editor) style shortcuts to text areas like "delete everything on this line" or "move to the beginning of this paragraph". It is also used with the macOS command line to send commands like "put this program into the background" and "kill this program". In Linux systems, it depends on your window manager largely, but similar terminal shortcuts work there too.</p> <h1 id="windows-command">Windows/Command</h1> <p>The Windows command key is not very exciting in Windows, used all the time in macOS, and potentially useful in Linux systems. In Windows, this key opens the Windows application menu. This can be so annoying for people playing full-screen games who accidentally press it that some keyboards have a "Windows lock" feature to disable the button while you're in games. It also works with other OS-level shortcuts such as Windows + "d" to show the desktop. In macOS, however, the command key plays the same kind of roll control does in Windows. Print is Command combined with P, undo is command combined with Z, and so on. Command can also work with shift, control, or alt to do even more things. For example, command and "w" will close a window, but add alt and it will close <em>all</em> windows. Command used as the only modifier tends to send commands that act on the active app like "print", but when combined with the other modifier keys, it tends to create global shortcuts like using command, shift, and "q" to logout of the system.</p> <p>Popular productivity apps such as Omnifocus and Fantastical have special input windows which are assignable to global key combinations. In Fantastical, for example, you can set a key to call up their natural-language event input to quickly add an event to your calendar without switching apps and losing where you were in your work. You couldn't map this to something simple like command and "f" because that's already the command for "find" in most apps. Adding shift might not even be enough, so on mine it is a combination of control, shift, command, <em>and</em> alt combined with "f". Use of all four modifier keys at once makes a sort of meta-modifier key known as "hyper". Since I have a fancy programmable keyboard, I can easily make a single key on my keyboard send this Hyper key, so launching Fantastical's input window requires pressing two keys on my keyboard. Hooray efficiency! Hooray productivity! For those of you without a programmable keyboard, you can accomplish something similar in software, the great <a href="http://brettterpstra.com/2012/12/08/a-useful-caps-lock-key/">Brett Terpstra</a> explains how to do that.</p> <p>Learning to make use of these modifier keys can help to make you more productive. You'll spend less time reaching over to the mouse and dragging the pointer to a menu item or button when you can simply pound out the key combination that does the same action from memory. People who have burned these combinations and shortcuts into their muscle memory tend to become slower and frustrated when you change their operating system, keyboard, or even system settings. It's the trade-off you make when you spend time learning how to use a tool and all of it's wonderful features.</p> Transference Sat, 09 Jul 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/transference/ https://roundwallsoftware.com/blog/transference/ <p>One of the signs of an expert programmer is a thing I call transference. I learned about it while reading a <a href="http://garymarcus.com/books/guitarzero.html">book</a> about learning to play music and it applies to programmers as well.</p> <p>A novice musician is hard at work studying exactly what to do with their fingers and exactly the patterns necessary to play a piece of music. With enough practice even, they can play pieces perfectly and beautifully. An expert musician, however, sees past the exact patterns and sees the bigger picture: the keys and the chords. Learning a piece of music becomes much faster because they recognize the similarities between that piece and others they have already learned. Many popular songs from the last 39 years, for example, use the exact same chord progressions and are maybe just played in a different key or speed. When a musician makes it past that initial barrier of simply learning to play and begins to recognize those bigger patterns, the world of music opens up and they become expert musicians.</p> <p>This happens much the same way in software. A novice developer is hard at work learning the basics of syntax and what a variable or a function is. As they progress, they begin to recognize code for the patterns it reflects and not the syntax and exact characters written in the file. Even without being able to articulate these patterns in ways other developers can understand, these patterns help to make the developer more productive and begin to turn them into effective expert developers. With enough experience even, this knowledge can help a developer transfer their skills to other languages and domains. When we learn to articulate these patterns we are able to share information with others and accelerate our learning. Much the way a musician gives names to musical patterns like scales and chords and progressions.</p> <p>When this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>doMath(lhs: Int, rhs: Int) -&gt; Int { </span><span> </span><span style="color:#b48ead;">return</span><span> lhs + rhs </span><span>} </span><span> </span><span>doMath(</span><span style="color:#d08770;">2</span><span>, </span><span style="color:#d08770;">4</span><span>) </span></code></pre> <p>is the same thing as this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#d08770;">2 </span><span>+ </span><span style="color:#d08770;">4 </span></code></pre> <p>or even this:</p> <pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span style="color:#b48ead;">def </span><span style="color:#8fa1b3;">do_math</span><span>(</span><span style="color:#bf616a;">lhs</span><span>, </span><span style="color:#bf616a;">rhs</span><span>) </span><span> lhs + rhs </span><span style="color:#b48ead;">end </span></code></pre> <p>that's transference.</p> <p>For me personally, this has helped me learn about refactoring and unit testing from the Ruby and Javascript communities, business from SaaS communities and teaching from language and music communities. Transference is where we begin to gain from our diverse experiences and backgrounds.</p> Building the KC60 Tue, 24 May 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/building-the-kc60/ https://roundwallsoftware.com/blog/building-the-kc60/ <p>Recently I have been super pumped on mechanical keyboards. I found one small enough to easily use when I'm on the couch or in a café which made it easier to spend most of my time using one and get used to it. At this point it feels weird and uncomfortable when I use my regular laptop keyboard!</p> <p>Keyboards can be a bit of a rabbit hole. Once you get into it, there are opportunities to tweak and change things for quite some time. Something even as simple sounding as changing the caps, maybe for a popular set like this</p> <p><img src="/blog/IMG_1356-1.jpg" alt="Rainbow keycaps" /></p> <p>can bring on many choices. What kind or material do you want your caps made out of? Do you want letters printed on the top? the side? not at all? what color? what shape? so many choices!</p> <p>I went even further down and decided that, instead of choosing a keyboard which I could enjoy and maybe customize, chose to go all in and build one up from base pieces. Building a keyboard means you can get one exactly the way you like. It also, for me, means I have a fun project to work on.</p> <h2 id="the-build">The Build</h2> <p><img src="/blog/IMG_1243-1.jpg" alt="KC60 kit" /></p> <p>I started with a KC60 kit. It saved me the effort of sourcing all the parts I'd need individually. Building a keyboard sounds complicated, but it's fairly straightforward (especially now with <a href="https://www.youtube.com/watch?v=oqV2xU1fee8">YouTube</a> giving access to so many videos explaining how to do things like soldering). You only need a few parts:</p> <ul> <li>KC60 PCB (To make things much easier than hand-wiring each switch together.)</li> <li>Top plate (Gives the switches something solid to push against and looks nice.)</li> <li>Switches (It might take quite some time to choose what kind of switch.)</li> <li>LEDs (Not required, but if you want them, they're fun to play with.)</li> <li>A case that fits (These can be cheap plastic or machined aluminum or wood, it's up to you.)</li> <li>Stabilizers (For big keys like the space bar.)</li> <li>Keycaps (Any kind you like, have fun with it!)</li> <li>A soldering iron (Doesn't need to be a super expensive one, mine was less than 100€ I think.)</li> <li>Solder (The cheap stuff they recommend at the electronics store is plenty good enough.)</li> </ul> <p><img src="/blog/IMG_1254.jpg" alt="Soldering Iron" /></p> <p>First you'll want to install the stabilizers. They come in little pieces and need to be assembled first. This &lt;video: https://www.youtube.com/watch?v=b8wYeGa6HEI&amp;feature=youtu.be&gt; explains how to assemble them and install them. This is the first thing to do because it's not possible to fit them in once the top plate is attached to the pcb. Save yourself the pain of needing to undo all your work if you fail to do this first.</p> <p><img src="/blog/IMG_1245.jpg" alt="Stabilizers installed" /></p> <p>Next line up the top plate with the pcb and you can start placing switches. The switches that come with the kit are plate-mounted, so first you fit the switch into a hole in the plate and then you line up the pins on the bottom of the switch with the holes in the pcb. Be careful with the switch pins, you can break them. It can help to get a few of these switches in the corners to help line up the plate. Once a few of them are in, carefully turn the whole thing over and solder the pins for those switches. This will help to hold the board in place while you place all the others.</p> <p><img src="/blog/IMG_1247.jpg" alt="First switches installed" /></p> <p>Once the switches are all placed and soldered, it's time to test your work. Plug it in and run your favorite keyboard tester. I'm a fan of <a href="http://www.keyboardtester.com">keyboardtester.com</a>. The little processor on the board comes pre-programmed with a standard layout, so at this point you should find all the keys work. If they don't, you'll need to go back and check your soldering work.</p> <p><img src="/blog/IMG_1260-1.jpg" alt="Switches installed" /></p> <p>From here you can install the LEDs if you like. They poke in through each switch so the little legs can fit in the tiny holes in the PCB. The long leg of the led is typically the positive lead, so make sure that one goes into the tiny hole marked "+". Once it is positioned, you can bend the legs a bit to keep it in place when you flip the board over to solder it in.</p> <p><img src="/blog/IMG_1267.jpg" alt="Bend legs holding LEDs in place" /></p> <p>Before soldering, you can plug in the keyboard to make sure the LED is in correctly, it should light up if the legs are touching the edge of the tiny PCB holes.</p> <p><img src="/blog/IMG_1269.jpg" alt="First row of led&#39;s working" /></p> <p>From here you're all set! Secure it in the case, place your chosen keycaps and get to clacking!</p> <h2 id="programming">Programming</h2> <p>If you want to make changes to the default layout, you can use this <a href="http://123.57.250.164:9128/">tool</a> (Yes, that's a direct IP address, and yes that's weird). It is a flash app that allows you to change the layout of up to 10 "layers" on your board and generates the proper hex file to install on your board. I tried other tools for programming other boards such as the GH60, but the KC60 is just different enough that it you might think your switches are broken. This tool was made by the manufacturer I think to generate the proper file.</p> <p>One you have the hex file, you can press the programming button on the bottom of the pcb and use a tool such as <a href="https://dfu-programmer.github.io">dfu-programmer</a> to install it. Dfu-programmer is a command-line tool you can install via Homebrew on OS X and by whatever package manager you use on your Linux distribution. Once it is installed, invoking the tool looks about like this:</p> <p><code>dfu-programmer atmega32u4 flash kc60_colemak.hex &amp;&amp; dfu-programmer atmega32u4 reset</code></p> <p>This says, "Install my hex file onto the keyboard (which has an atmega32u4 processor) and reset the processor to put it back into normal working mode." If this succeeds, you'll be the proud owner of a keyboard that was built from the beginning exactly like you wanted, down to the function of each key. Programming the board means you can make any key or combination of keys do the things you would normally lose from having a smaller board such as arrow keys or the F1-F12 keys.</p> <p>On a typical laptop keyboard, you will find an "fn" key. This is because there just aren't enough keys to have a button for everything unless you're on a giant 19 inch laptop. A typical Apple laptop, for example, uses "fn" to allow you to send "page up" and many other commands. We get this same behavior on the programmed KC60, but we can even take it a step futher. We get up to 10 "layers" and we get whatever we want in each one. We also have the ability to make more complicated fn keys. You could make one that doesn't require you to hold it down to work and simply tapping it once changes the current layer and leaves it there. This would allow you to, for example, have your keyboard with a normal QWERTY layout most of the time, but use a button to toggle to a <a href="https://en.wikipedia.org/wiki/Keyboard_layout#Colemak">Colemak</a> layout when you want to give that a try (or the other way around for when other people want to try your keyboard but don't know how to type on Colemak). The possibilities are nearly endless, so you now have something to play with for quite some time. Enjoy!</p> <p><img src="/blog/IMG_1270.jpg" alt="All LEDs lit up" /></p> Fun With Typing Tue, 23 Feb 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/fun-with-typing/ https://roundwallsoftware.com/blog/fun-with-typing/ <p>As a developer, I spend quite a bit of time typing. Typing code, typing email responses to clients and recruiters, typing jokes in Slack.</p> <p>As a child, I was fortunate enough to have access to a computer at home and the internet. Our computer was not fancy enough though to play any of the games the other kids were playing, like Starcraft or Counter Strike, so I spent quite a bit of time playing text-based games instead. No need for graphics cards when all your computer needs to do is show text scrolling by. No need for a fast internet connection even. Playing these games involved so much typing I could not help but get faster. I had no formal typing training, so my methods were far from conventional, but they worked.</p> <p>Years later I read articles like <a href="https://robots.thoughtbot.com/why-you-should-use-a-mechanical-keyboard">Why You Should Use a Mechanical Keyboard</a> and bought a fancy new mechanical keyboard, one small enough to carry around even.</p> <blockquote class="instagram-media" data-instgrm-captioned data-instgrm-version="6" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:658px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:8px;"> <div style=" background:#F8F8F8; line-height:0; margin-top:40px; padding:50.0% 0; text-align:center; width:100%;"> <div style=" background:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAGFBMVEUiIiI9PT0eHh4gIB4hIBkcHBwcHBwcHBydr+JQAAAACHRSTlMABA4YHyQsM5jtaMwAAADfSURBVDjL7ZVBEgMhCAQBAf//42xcNbpAqakcM0ftUmFAAIBE81IqBJdS3lS6zs3bIpB9WED3YYXFPmHRfT8sgyrCP1x8uEUxLMzNWElFOYCV6mHWWwMzdPEKHlhLw7NWJqkHc4uIZphavDzA2JPzUDsBZziNae2S6owH8xPmX8G7zzgKEOPUoYHvGz1TBCxMkd3kwNVbU0gKHkx+iZILf77IofhrY1nYFnB/lQPb79drWOyJVa/DAvg9B/rLB4cC+Nqgdz/TvBbBnr6GBReqn/nRmDgaQEej7WhonozjF+Y2I/fZou/qAAAAAElFTkSuQmCC); display:block; height:44px; margin:0 auto -44px; position:relative; top:-22px; width:44px;"></div></div> <p style=" margin:8px 0 0 0; padding:0 4px;"> <a href="https://www.instagram.com/p/BBvOIpqnWNA/" style=" color:#000; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none; word-wrap:break-word;" target="_blank">New keycaps! #mechanicalkeyboard</a></p> <p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;">A photo posted by Samuel Goodwin (@mukmantheorigina) on <time style=" font-family:Arial,sans-serif; font-size:14px; line-height:17px;" datetime="2016-02-13T19:15:28+00:00">Feb 13, 2016 at 11:15am PST</time></p></div></blockquote> <script async defer src="//platform.instagram.com/en_US/embeds.js"></script> <p>I thought it might finally be time to learn to type properly, so I looked around and found <a href="https://www.typing.com">typing.com</a> and started working to correct years of habit. It took about 4 weeks of fairly regular practice to finally train myself again. I still sometimes forget and revert back to my old ways, especially when I want to quickly whip up some code to show something to a student.</p> <p>For record-keeping purposes, I even recorded a little video to compare before and after my training. You can see before I mostly only used my middle finger on my left hand and didn't use my pinky-fingers on either hand.</p> <iframe width="640" height="360" src="https://www.youtube.com/embed/NIvkX5zz4SQ" frameborder="0" allowfullscreen></iframe> <p>Another convenient side-effect of typing properly is that I put less force into each keystroke which helps to make my keyboard quieter also. Hooray!</p> The difference between classes and structs in Swift Tue, 23 Feb 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/the-difference-between-classes-and-structs-in-swift/ https://roundwallsoftware.com/blog/the-difference-between-classes-and-structs-in-swift/ <p>Today I assigned my students (I teach a <a href="http://www.theappacademy.nl">bootcamp</a> now) to write 400-500 words about one of the topics we have covered in class, so I thought I would do the same. Here goes!</p> <p>Classes and structs are similar. Both can have properties, both can have methods. This can make it a bit confusing for developers who are new to Swift. Understanding the difference between the two can help to make choosing between the two easier in your code.</p> <h1 id="inheritance">Inheritance</h1> <p>Classes give you the ability to make use of inheritance, much like you would expect from other languages. While most developers will discourage you from using inheritance to solve your problems, they're still useful sometimes. Structs cannot have "substructs" or whatever you might call them. They are effectively final.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> Subclass: SomeOtherClass { </span><span> </span><span style="color:#65737e;">// This is just fine </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> SubStruct: SomeOtherStruct { </span><span> </span><span style="color:#65737e;">// This will get you compiler errors, not possible! </span><span>} </span></code></pre> <br/> # References vs values <p>This one is a bit tricky and was the cause of most of my students having confusion moments in the first week of our bootcamp. When you pass around instances of a class, you are actually just passing around a pointer to that class. When you pass around structs, you are passing the actual value of the stuct (a copy even).</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> Town { </span><span> </span><span style="color:#b48ead;">var</span><span> population = </span><span style="color:#d08770;">10 </span><span>} </span><span> </span><span style="color:#b48ead;">class</span><span> Zombie { </span><span> </span><span style="color:#b48ead;">var</span><span> town: Town </span><span> </span><span style="color:#b48ead;">func </span><span>terrorize() { </span><span> town.population -= </span><span style="color:#d08770;">2 </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">class</span><span> Vampire { </span><span> </span><span style="color:#b48ead;">var</span><span> town: Town </span><span> </span><span style="color:#b48ead;">func </span><span>terrorize() { </span><span> town.population -= </span><span style="color:#d08770;">1 </span><span> } </span><span>} </span></code></pre> <p>Let's say you have 3 classes, a town, a zombie, and a vampire. The zombie and vampire expect a town that they can terrorize. If we create one town and assign the same town to both monsters, they will be sharing a reference to the same instance of that town.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">let</span><span> town = Town() </span><span> </span><span style="color:#b48ead;">let</span><span> zombie = Zombie(town: town) </span><span style="color:#b48ead;">let</span><span> vampire = Vampire(town: town) </span></code></pre> <p>If both monsters terrorize that town, your town will have a population of 7 in the end, because both monsters terrorize the same town.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span>zombie.terrorize() </span><span>vampire.terrorize() </span><span>print(town.population) </span><span style="color:#65737e;">// &lt;-- This will print a 7 </span></code></pre> <p>This is what we'd expect. Our poor town suffers from two different monsters killing their people. Poor people.</p> <p>With a struct this happens differently. If we implemented the same objects as structs instead of instances of classes, we would get an entirely different answer.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">struct</span><span> Town { </span><span> </span><span style="color:#b48ead;">var</span><span> population = </span><span style="color:#d08770;">10 </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> Zombie { </span><span> </span><span style="color:#b48ead;">var</span><span> town: Town </span><span> </span><span style="color:#b48ead;">mutating func </span><span>terrorize() { </span><span> town.population -= </span><span style="color:#d08770;">2 </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">struct</span><span> Vampire { </span><span> </span><span style="color:#b48ead;">var</span><span> town: Town </span><span> </span><span style="color:#b48ead;">mutating func </span><span>terrorize() { </span><span> town.population -= </span><span style="color:#d08770;">1 </span></code></pre> <p><em>Now</em> if we make a zombie and a vampire and give it our town we can see a big difference:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">var</span><span> town = Town() </span><span style="color:#b48ead;">var</span><span> zombie = Zombie(town: town) </span><span style="color:#b48ead;">var</span><span> vampire = Vampire(town: town) </span><span> </span><span>zombie.terrorize() </span><span>vampire.terrorize() </span><span>print(town.population) </span><span style="color:#65737e;">// What?!? It says 10 even though our monsters attacked. </span></code></pre> <p>So first you might look at this and think, "oh hey, I found a bug in Swift" or even, "hey maybe I wrote this incorrectly". Neither one is true! Your code is totally valid, but structs are passed around by value. The zombie and vampire both have a town, but it's not the same town that our original town variable is holding onto. They're working with an entirely different object that just happens to have the exact same values as our original town variable had. To use structs to model our monster attacks, we'd need to do things differently.</p> <p>So now you know two of the big ways that structs are different than classes. And knowing is half the battle.</p> Swift Enums for Error Reporting Sat, 09 Jan 2016 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/swift-enums-for-error-reporting/ https://roundwallsoftware.com/blog/swift-enums-for-error-reporting/ <p>In Apple's block-based API's, you'll often see a pattern like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>dataTaskWithRequest(request: NSURLRequest, completionHandler: (NSData?, NSURLResponse?, NSError?) -&gt; Void) -&gt; NSURLSessionDataTask </span></code></pre> <p>The function expects you to provide an <code>NSURLRequest</code> and a block and promises to execute your block later with some info. Inside your block you <em>might</em> get some data, you <em>might</em> get a response, and you also <em>might</em> get an error. Optionals help to make this more clear than it was in Objective-C by forcing you to consider that each parameter might be nil. To handle all the possible combinations, you might have to do something like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">if</span><span> data == </span><span style="color:#d08770;">nil</span><span> { </span><span> </span><span style="color:#b48ead;">if</span><span> error != </span><span style="color:#d08770;">nil</span><span> { </span><span> print(</span><span style="color:#a3be8c;">&quot;Didn&#39;t get any data, but instead got this error that might explain why: </span><span>\(error)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> } </span><span>} </span><span> </span><span style="color:#b48ead;">if let</span><span> data = data, </span><span style="color:#b48ead;">let</span><span> response = response { </span><span> print(</span><span style="color:#a3be8c;">&quot;The server responded with these headers and code </span><span>\(response) </span><span style="color:#a3be8c;">and this data: </span><span>\(data)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span>} </span></code></pre> <p>This involves learning which combination of things you can expect and handling each case. What if we could make our API's so that they're more clear and gave us less opportunity to mess up when we're tired or distracted?</p> <p>What if we built an enum with cases like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">enum</span><span> RequestResult { </span><span> </span><span style="color:#b48ead;">case</span><span> NoConnection, </span><span> </span><span style="color:#b48ead;">case</span><span> Success(data: NSData) </span><span> </span><span style="color:#b48ead;">case</span><span> HTTPError(error: ErrorType) </span><span>} </span></code></pre> <p>We could then wrap <code>NSURLSession</code>'s method into one more like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>dataTaskWithRequest(request: NSURLRequest, completionHandler: (RequestResult) -&gt; Void) -&gt; NSURLSessionDataTask </span></code></pre> <p>Then when we use it later, we're forced to handle all the possible cases without needing to remember what they are.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">switch</span><span> result { </span><span> </span><span style="color:#b48ead;">case </span><span>.NoConnection: </span><span> print(</span><span style="color:#a3be8c;">&quot;No connectivity, can&#39;t try to connect to server&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">case </span><span>.Success(</span><span style="color:#b48ead;">let</span><span> data) </span><span> print(</span><span style="color:#a3be8c;">&quot;Request worked just fine, here&#39;s the headers and such </span><span>\(response) </span><span style="color:#a3be8c;">and the data: </span><span>\(data)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span> </span><span style="color:#b48ead;">case </span><span>.HTTPError(</span><span style="color:#b48ead;">let</span><span> error) </span><span> print(</span><span style="color:#a3be8c;">&quot;Did connect, but server responded with a not-happy HTTP status code: </span><span>\(code)</span><span style="color:#a3be8c;">, error: </span><span>\(error) </span><span style="color:#a3be8c;">} </span></code></pre> <p>Then if we forgot a possibility, the compiler could say "Hey woah, you have a switch and you didn't consider one of the possible cases!" so we wouldn't need to rely on our mental state to keep up. For each possible outcome, we also made it clear which objects we can expect. No optionals, no matrix of possibilities, just a clear set of variables we can expect based on the outcome. If it's considered a successful request, we can expect the data we asked for. Future developers (including yourself 6 months from now) will have a much easier time handling the response of these HTTP requests without forgetting a step or missing an important variable. Hooray!</p> <p>Once this is working and you're feeling especially fancy, you could even take this further. Maybe you make <code>ConnectionResult</code> into some kind of generic thing that doesn't just hold data on a successful request, but instead could also hold model objects constructed from the JSON your server gave you. Maybe something like <code>Result&lt;T, ErrorType&gt;</code> which is an enum that includes either a success case with an object of type T or a failure case with error information. Maybe then you could add some function to your <code>Result</code> type that lets you feed your result a function that should only be executed if your result is a success case. Something like:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>map&lt;T&gt;(transform: (T -&gt; U)) -&gt; Result&lt;U, ErrorType&gt;) </span></code></pre> <p>I'm just calling it map because I though that might be a good name. This would allow you to turn your result of type <code>Result&lt;T, NSError&gt;</code> to one of type <code>Result&lt;U, ErrorType&gt;</code>, like if <code>T</code> were an <code>NSDictionary</code> and the <code>U</code> was your <code>FeedItem</code> model object. If your Result were a success, you'd get back a new success that contained your <code>FeedItem</code>, but if your original result were instead an error, you'd get back a new failure case with the same error. This lets you do nifty things like this:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">func </span><span>dataTaskWithRequest(request: NSURLRequest, completionHandler: (result) -&gt; Void) -&gt; NSURLSessionDataTask { </span><span> </span><span style="color:#b48ead;">let</span><span> dataModel = result.map(processModel) </span><span> print(</span><span style="color:#a3be8c;">&quot;Got a model back from the server: </span><span>\(dataModel)</span><span style="color:#a3be8c;">&quot;</span><span>) </span><span>} </span></code></pre> <p>Where <code>processModel</code> is a function that doesn't know or care about <code>Result</code>. It just knows how to turn NSDictionaries into data models. This convenience function lets us avoid writing switch statements everywhere, and still preserve the context we've built up that our value can either be successful with a useful object or a failure with some error information about why we couldn't do it. Better living through abstractions!</p> <p>Congratulations, you just made a Functor. You've practically created the Voldemort of Swift development (the Monad, cough cough).</p> Semitones 1.0.1 Released Fri, 20 Nov 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/semitones-1-0-1-released/ https://roundwallsoftware.com/blog/semitones-1-0-1-released/ <p>Up to the day I am writing this, Semitones has earned me 122$ in proceeds. That's after Apple's 30% cut from sales through the App Store. That's not much, but it is already more than my last app made in it's entire lifetime. Semitones has a simple business model: an up-front price of 4$. No in-app purchases, no ads, no subscriptions. Just pay up front and get straight to the music practice. The app is intended for people who are serious about studying music: students and teachers. It is not mindless entertainment (which is not bad, by the way, it's good to have a way to let your mind relax and recharge a bit). I assumed the sort of person who was interested in my app would not want to be delayed by in-app purchases for each exercise or have ads attempting to divert their attention from the exercise at hand.</p> <p>So far, things seem off to a good start. Revenue is low, but so is my spending on advertising. I was advised to hold off on pushing money into advertising while I got a better feel for the app and its customers. Without paid advertising, I've needed to rely on social media channels like Twitter. Twitter's analytics information allows me to see, for example, that 2761 people have seen the launch annoucement. From those people, 30 people followed the link to the app store and 9 people retweeted it. Subsequent tweets got less attention.</p> <p>Apple's App Store also provides some analytics. They show that at least 583 people viewed Semitones in the app store and 35 of them actually bought it. The sales numbers reported from Analytics is a bit different from the sales numbers reported in the actual Sales section which is a bit odd to me, but oh well. What this means to me is that a large number of people are finding my app through sources other than my Twitter links. Unfortunately, all of these analytics sources are not perfect and Apple does not provide things such as "how many people found my app from search".</p> <p>One advantage of my app is that searching for "semitones" in the app store only gives two results: my app and another app that hasn't been updated since December of 2014. In the next weeks, I'll be paying more attention to the search keywords I include for the app. Some of the app store views must be coming from search if they're not coming from the links I've posted on places like Twitter.</p> <p>It's worth mentioning that I'm not disappointed by sales so far. I didn't expect to make a million dollars in the first week or anything (though I certainly wouldn't have refused it). I expected this and planned for it. Before starting this project, I made sure I had enough in savings that I could run for about two <em>years</em> without turning a profit before I needed to gave and get a job. Also during this time I'll still be doing contract work occasionally to help stall that out even further.</p> <p>Today version 1.0.1 is live. It's just some bug fixes from things I missed in 1.0. Meanwhile we are working on 1.1 which will add scales and modes to the current list of things to identify. I think this has a real shot at being something useful for people and profitable for me. Either way, there's plenty to learn.</p> <p>If you're interested, you can check out Semitones in the <a href="https://itunes.apple.com/us/app/semitones/id1042212454?mt=8">App Store here</a></p> Introducing Semitones Mon, 09 Nov 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/introducing-semitones/ https://roundwallsoftware.com/blog/introducing-semitones/ <p>In the last few years, I've started taking my bass playing more seriously. I've found a teacher, joined in on jam sessions, and sat in for bands like <a href="http://airplanemo.de">Airplane Mode</a>. In my search for ways to help me improve, I found inspiration for my next app.</p> <p>Becoming a better musician includes some level of music theory study. Learning about different <a href="https://en.wikipedia.org/wiki/Interval_%28music%29">intervals</a>, <a href="https://en.wikipedia.org/wiki/Chord_(music)">chords</a> and <a href="https://en.wikipedia.org/wiki/Scale_(music)">scales</a> make it easier to play music along with other people or recordings. At my first jam session, I had people shouting things like, "F minor!" at me and at first I had almost no clue what they meant. Thanks to my teacher, I not only understand what they mean when they shout "F minor", but I can also start to hear it and play it before they even open their mouth.</p> <p>This is where the new app comes in. It isn't enough just for you to <em>know</em> about different intervals and chords. You also need to be able to recognize them when you hear them. Recognizing minor chords, major seventh intervals, and the like takes regular practice. Semitones is an app created to help with just that. Now whenever you're waiting in line or taking a break on a bench, you have an opportunity to improve your ears.</p> <p>Semitones comes with a number of progressively difficult exercises you can work through. It starts out with only two possible intervals and works up to 16 possible intervals. With less possibilities, it is easier to get the hang of recognizing the difference. Each exercise has a definite ending after 10 questions. It will also remember your progress so you can see how you're doing.</p> <p>I'm really proud of the work we did. The talented <a href="https://twitter.com/bloomingbridges">Florian</a> handled the design. The brilliant <a href="https://twitter.com/macgenie">Jean MacDonald</a> taught me quite a bit about marketing strategy and launching. Awesome people like <a href="https://twitter.com/jcieplinski">Joe</a> encouraged me to build it in the first place. Thank you all for help!</p> <hr> <center> [![Download on the App Store](/blog/1446723583935.png)](https://itunes.apple.com/us/app/semitones/id1042212454?mt=8) <p>For more information, you can check out <a href="http://www.semitonesapp.com">semitonesapp.com</a>.</center></p> What I'm Working On Thu, 24 Sep 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/what-i-m-working-on/ https://roundwallsoftware.com/blog/what-i-m-working-on/ <p>Some of you have been asking what I'm working on lately, so here's your answer.</p> <h3 id="1-practicing-music"><a name="practice">1. Practicing music.</a></h3> <p>In middle school and high school, I played bass for the school orchestra. I played cello for that first year, but I like to pretend that didn't happen. I stopped playing music after that. Partly because I didn't own my own instrument and partly because I went to college somewhere with a basically non-existent music program.</p> <p>I started playing again after college when I moved to NYC. A skateboarding accident broke all three bones in my right ankle (the price you pay for turning your foot around backwards) and spent many months unable to walk or even stand. This is when I realized an electric bass was not that expensive and, while I maybe couldn't take music classes, I could at least own my own instrument for fairly cheap. I sent my friend to the store for me and he brought me this:</p> <p><img src="/blog/ARzAdQewd5CQNl0aD4aYYVlvbngG-2.jpg" alt="My first bass of my own." /></p> <p>Since then I have been learning and practicing and trying to recover my stale music skills. Here in Amsterdam I joined in on some jam sessions which helped to expose just how little I knew. Thanks to my excellent teacher, I've been able to learn quite a bit in the last few years. This year I've taken things a bit further and devoted more organized time to learning to play well. It's a real confidence boost to be able to hear someone play a song and pick up on the chord progressions and know what you can get away with playing. An even bigger boost when people react to what you decide to play.</p> <p>Right now I'm working on the skills necessary to play a few songs in addition to studying music theory and such; mostly I struggle with their speed.</p> <ol> <li><a href="https://www.youtube.com/watch?v=sdOLFtk9joI">Higher Ground</a></li> <li><a href="https://www.youtube.com/watch?v=6sIjSNTS7Fs">Sir Duke</a></li> <li><a href="https://www.youtube.com/watch?v=3dm_5qWWDV8">Hysteria</a></li> </ol> <h3 id="2-teaching-programming"><a name="teaching">2. Teaching programming</a></h3> <p>I've been told I'm also a great teacher. It's fun to help turn the light bulb on above someone's head. I've been practicing and working with people of various skill levels to better understand how people learn and how I can help. I've picked up some important things in the last year:</p> <ul> <li>Everything you do for someone at one skill level needs to be different for someone at another level.</li> <li>Programming is hard. Acknowledging that can be encouraging for students.</li> <li>The sooner you and your student can come up with a specific goal or project the better. The trick is coming up with something with an appropriate scope for their skill level and time abilities.</li> <li>Some people maybe think they need to learn to program, but don't actually want to do it. Maybe they're just not ready right now.</li> <li>There are few bad teachers, just bad teacher-student combinations.</li> </ul> <h3 id="3-developing-my-music-app"><a name="development">3. Developing my music app.</a></h3> <p>It's not an app that <em>plays</em> music, it's an app to help you get better at understanding it when you hear it. It's a tool to practice recognizing <a href="https://en.wikipedia.org/wiki/Interval_(music)">musical intervals</a> and <a href="https://en.wikipedia.org/wiki/Chord_(music)">chords</a>. If people buy it, I'll add support for <a href="https://en.wikipedia.org/wiki/Scale_(music)">scales</a> and <a href="https://en.wikipedia.org/wiki/Mode_(music)">modes</a>. My app will help you learn to recognize these sounds much like you learned to recognize when people speak English words. It's useful practice to help you get closer to hearing a song on the radio and playing along with it. Since it's in your pocket (on your iPhone), you'll be able to practice whenever it's convenient throughout the day.</p> <p><img src="/blog/Semitones-App-Icon-Circular-Schema.png" alt="Semitones logo" /></p> <p>I'm working with <a href="https://twitter.com/bloomingbridges">Florian Brueckner</a> who is solid designer and have a list of interested musicians ready to help test it. We expect to be ready to show everyone the app in the next few weeks.</p> Handling iOS image uploads with Paperclip Wed, 17 Jun 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/handling-ios-image-uploads-with-paperclip/ https://roundwallsoftware.com/blog/handling-ios-image-uploads-with-paperclip/ <p>If any of you happen to do some backend work and iOS work at the same time or work on a team that does, this might be a helpful tip for you:</p> <p>Let's say you have a Rails app that uses the <a href="https://github.com/thoughtbot/paperclip">Paperclip</a> gem by <a href="https://thoughtbot.com">Thoughtbot</a> to handle file uploads.</p> <p>Then let's say you have an iOS app that wants to upload photos to the server. You've tried building a multi-part request with something like <a href="https://github.com/AFNetworking/AFNetworking">AFNetworking</a> since <a href="https://github.com/Alamofire/Alamofire">Alamofire</a> doesn't support multi-part requests and <code>NSURLSession</code> isn't quite convenient enough to do it alone.</p> <p>In your Rails code, you'll do something like</p> <pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>@</span><span style="color:#bf616a;">photo</span><span>.image = params[</span><span style="color:#a3be8c;">:image</span><span>] </span></code></pre> <p>and you'll get a vague error about invalid characters.</p> <p>The problem here is, Rails has failed to properly detect the file in your multi-part request and then Paperclip can't do its job either. You're trying to send some <code>JSON</code> parameters and an image file and you're doing it the wrong way. Here's how you fix it in three easy steps:</p> <ol> <li>Base64 encode the <code>NSData</code> object representing your <code>UIImage</code>.</li> <li>Send it just like any other JSON parameter because now it's a string.</li> <li>In your Rails code, decode the Base64 value like so:</li> </ol> <pre data-lang="ruby" style="background-color:#2b303b;color:#c0c5ce;" class="language-ruby "><code class="language-ruby" data-lang="ruby"><span>@</span><span style="color:#bf616a;">photo</span><span>.image = </span><span style="color:#ebcb8b;">StringIO</span><span>.</span><span style="color:#8fa1b3;">new</span><span>(</span><span style="color:#ebcb8b;">Base64</span><span>.decode64(params[</span><span style="color:#a3be8c;">:image</span><span>])) </span></code></pre> <p>If you dig through Paperclip's code, you'll find that an attachment (your photo) has a number of handlers for turning input into a complete attachment. This makes use of the <code>StringIO</code> handler which writes out the data as a temporary file for Paperclip's internals to process. If you search around the internet for this problem, you'll find solutions like <a href="http://paoloibarra.com/2014/09/27/Image-Upload-Using-Rails-API-And-Paperclip/">this one</a> that suggests manually writing files yourself, but you don't have to bother! The smart people working on Paperclip have already considered this use-case and simply didn't document it well enough.</p> <p>Enjoy!</p> Gateway View Controller: A Pattern for handling app login Sun, 14 Jun 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/gateway-view-controller-a-pattern-for-handling-app-login/ https://roundwallsoftware.com/blog/gateway-view-controller-a-pattern-for-handling-app-login/ <p>As Apple recommends in their <a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/index.html#//apple_ref/doc/uid/TP40006556">Interface Guidelines</a>, apps should try provide functionality without requiring a login. Ideally, an app would only ask for your login information when you get to features in the app that does need it.</p> <p>Unfortunately, there are a great many apps that simply lock you out of the entire app until you have successfully logged in. Sometimes this is even necessary: like if you are making a private-messaging app. For the times when this <em>is</em> necessary, I'd like to explain a pattern to implement this.</p> <p>Your main entry point to your app will now be a special controller I call the Gateway View Controller. It will be the parent view controller of all others in your app. It will need a few handy functions:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">private func </span><span>embedFullScreenController(controller: UIViewController){ </span><span> controller.willMoveToParentViewController(</span><span style="color:#b48ead;">self</span><span>) </span><span> controller.view.frame = view.bounds </span><span> view.addSubview(controller.view) </span><span> addChildViewController(controller) </span><span> } </span></code></pre> <p>Embedding a child view controller takes a few steps, so instead of typing them every time, why not have a function for it? Because this controller will leave the job of presenting any UI to it's children, we can just assume the embedded controller needs to take up the full frame of the gateway. If you wanted to be fancy, this would be your opportunity to add some fade-out or page-flip animation. I'll leave that up to you.</p> <p>We'll also need a function for removing old children. Our Gateway won't stack up a pile of controllers, we want only one direct child at a time.</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">private func </span><span>removeOldChildren() { </span><span> </span><span style="color:#b48ead;">for</span><span> controller </span><span style="color:#b48ead;">in</span><span> childViewControllers as! [UIViewController] { </span><span> controller.willMoveToParentViewController(</span><span style="color:#d08770;">nil</span><span>) </span><span> controller.removeFromParentViewController() </span><span> } </span><span> } </span></code></pre> <p>Next, we'll need something to represent the idea that your user is logged in. I chose a simple little enum, but your implementation will likely involve something that holds a bit more information:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">enum</span><span> LoginStatus { </span><span> </span><span style="color:#b48ead;">case</span><span> LoggedOut </span><span> </span><span style="color:#b48ead;">case</span><span> LoggedIn(apiKey: String) </span><span>} </span></code></pre> <p>Either the app is logged out or the app is logged in. If it <em>is</em> logged in, my example app remembers the user's API key that should be used in server requests while the app is running. This ability to attach some information to an enum case is one of my favorite Swift features.</p> <p>Next we'll need a method to determine which controller should be shown based on our login status:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">private func </span><span>showAppropriateController() { </span><span> removeOldChildren() </span><span> </span><span> </span><span style="color:#b48ead;">switch</span><span> status { </span><span> </span><span style="color:#b48ead;">case </span><span>.LoggedOut: </span><span> </span><span style="color:#b48ead;">let</span><span> controller = storyboard?.instantiateViewControllerWithIdentifier(</span><span style="color:#a3be8c;">&quot;loggedOut&quot;</span><span>) as? LoggedOutViewController </span><span> controller?.gatewayViewController = </span><span style="color:#b48ead;">self </span><span> map(controller, embedFullScreenController) </span><span> </span><span style="color:#b48ead;">case </span><span>.LoggedIn(</span><span style="color:#b48ead;">let</span><span> apiKey): </span><span> </span><span style="color:#b48ead;">let</span><span> controller = storyboard?.instantiateViewControllerWithIdentifier(</span><span style="color:#a3be8c;">&quot;loggedIn&quot;</span><span>) as? LoggedInViewController </span><span> controller?.status = </span><span style="color:#b48ead;">self</span><span>.status </span><span> map(controller, embedFullScreenController) </span><span> } </span><span> } </span></code></pre> <p>If the app is logged in, we want to show something from the app's main UI and we should send along our information about the logged-in status of the app. That way the controller can use the API key we have stored for its business. If the app is not logged in, we want to show some login screen. The login screen needs a reference to the gateway controller much like all view controllers have a reference to their parent navigation or tab controllers. Otherwise we just use our embed function we wrote and we're all set.</p> <p>This controller-showing function will get triggered in two ways:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">var</span><span> status = LoginStatus.LoggedOut { </span><span> </span><span style="color:#b48ead;">didSet</span><span> { </span><span> showAppropriateController() </span><span> } </span><span> } </span><span> </span><span> </span><span style="color:#b48ead;">override func </span><span>viewDidLoad() { </span><span> </span><span style="color:#b48ead;">super</span><span>.viewDidLoad() </span><span> </span><span> showAppropriateController() </span><span> } </span></code></pre> <p>We'll want to show something as soon as your app loads, but also we'll want to change screens whenever the app's login status changes. Easy peasy.</p> <p>Last thing we need to add is an event to make the app's login status change. Our login-screen controller is going to do some magic with a user's username and password and decide when we're all set. It will simply need to inform the gateway controller when it's done:</p> <pre data-lang="swift" style="background-color:#2b303b;color:#c0c5ce;" class="language-swift "><code class="language-swift" data-lang="swift"><span style="color:#b48ead;">class</span><span> LoggedOutViewController: UIViewController { </span><span> </span><span style="color:#b48ead;">var</span><span> gatewayViewController: GatewayViewController? </span><span> </span><span> </span><span style="color:#b48ead;">@IBAction func </span><span>login() { </span><span> </span><span style="color:#65737e;">// Assume login magically worked just fine. </span><span> gatewayViewController?.status = .LoggedIn(apiKey: </span><span style="color:#a3be8c;">&quot;someimportantapikey&quot;</span><span>) </span><span> } </span><span>} </span></code></pre> <p>There you have it! We've encapsulated the idea of deciding what gets shown based on login to it's own controller. Just like <code>UIKit</code> provides us <code>UINavigationController</code> which is responsible for a trail of view controllers and <code>UITabViewController</code> which handles toggling between view controllers based on tab bar buttons, we've created a parent controller that toggles between controllers based on login status.</p> <p>This same kind of idea could be extended to do things like toggle what is displayed in your app based on network connectivity. This could even be used further down your navigation structure to lock out select portions of your app based on login status (which is way more friendly than locking the whole app).</p> <p>Source code for this example can be found <a href="https://github.com/sgoodwin/LoginExample">here</a></p> <p>Enjoy!</p> Resources for Getting Started Thu, 09 Apr 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/resources-for-getting-started/ https://roundwallsoftware.com/blog/resources-for-getting-started/ <p>First, let's start with some helpful resources:</p> <h2 id="ios-design">iOS Design</h2> <ul> <li><a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/MobileHIG/index.html#//apple_ref/doc/uid/TP40006556">Apple's Human Interface Guidelines</a></li> <li>WWDC 2015 Videos <ul> <li><a href="https://developer.apple.com/videos/wwdc/2015/?id=801">Designing For Future Hardware</a> (This is more about prototyping and incredibly informative)</li> </ul> </li> <li><a href="https://developer.apple.com/videos/wwdc/2014/">WWDC 2014 Videos</a> <ul> <li>Creating Custom iOS User Interfaces</li> <li>Designing Intuitive User Experiences</li> <li>Making a Great First Impression With Strong Onboarding Design</li> <li>Prototyping: Fake It Till You Make It</li> </ul> </li> <li><a href="https://developer.apple.com/videos/wwdc/2013/">WWDC 2013 Videos</a> <ul> <li>Customizing Your App’s Appearance for iOS 7 (Still valid for iOS 8)</li> <li>Building User Interfaces for iOS 7 (Still valid for iOS 8)</li> <li></li> </ul> </li> </ul> <h2 id="development-introductions">Development Introductions</h2> <ul> <li><a href="https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/">Apple's Intro Documentation</a></li> <li><a href="https://itunes.apple.com/us/book-series/swift-programming-series/id888896989?mt=11">Apple's iBooks</a></li> </ul> <h2 id="development-in-depth">Development In Depth</h2> <ul> <li><a href="https://gumroad.com/l/maybe-haskell/?utm_source=giant-robots&amp;utm_medium=blog&amp;utm_campaign=announcement">Maybe Haskell</a></li> <li><a href="http://nshipster.com/swift-collection-protocols/">NSHipster</a></li> <li><a href="https://robots.thoughtbot.com">Thoughtbot's blog</a></li> </ul> <p>If you're already familiar with Objective-C, an effective strategy can be to simply begin by writing Objective-C in Swift. That is to say, don't worry about any of the new or different features, just interact with Apple's frameworks like you used to as much as possible and keep working. Later, as you become more comfortable with the language and learn about the more in-depth features, go back and refactor your code to make effective use of all the Applicatives and Monads you like. Just because Swift <em>has</em> fancy features in the language doesn't mean you need to use them on day one.</p> One Platform At A Time Tue, 07 Apr 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/one-platform-at-a-time/ https://roundwallsoftware.com/blog/one-platform-at-a-time/ <p>We work and talk with many startups and a question that often comes up is, “Does it make sense to build our product for iOS and also for Android for our initial launch?” Our answer to this question is a resounding, “No.” This is not because we want our clients to only consider iOS and therefor only consider working with us (since we do not do Android work). We actually know several Android engineers and agencies we would be happy to recommend for Android projects. Whichever platform you choose, we think it is important to pick only one to begin your product. This question comes up so much that it made sense to write out an explanation:</p> <p>##Cost</p> <p>Most startups have a limited budget and are very concious about it. Launching on both iOS and Android at the same time means that you’ll need to hire twice as many engineers, twice as many designers, and potentially someone to manage all of these people. Before you even know if your startup will be successful, we don’t think it makes sense to bring on all these extra costs before you have even demonstrated your product will be worth anything.</p> <p>##Time</p> <p>First you will make a design decision for your Android app. Your team will discuss it, maybe argue about it, then come to an agreement and begin implementation. Then you will have the same discussion for iOS and come to a slightly different agreement. They are both mobile platforms, but they are not the same. Interactions are not the same, capabilities are not the same, and expectations of the user base are not the same. In our experience, if x is the time it takes to build your product on one platform, the time it takes to build on two platforms at once often takes more like 3x as long, not 2x.</p> <p>##Famous Examples</p> <p>Many successful startups you have heard of have taken this strategy and it has worked out great for many of them.</p> <ul> <li>Facebook: First launched a website, and years later launched an iOS app long before their Android app was released.</li> <li>Instagram: Famously launched iOS only, then developed a Web interface, then launched an Android app. For years the only way to interact with Instagram was to use the iOS app and they had millions of happy users and were acquired for famous amounts of money.</li> <li>Angry Birds: This game now runs on almost every device commercially available, we would not be surprised if it even ran on a toaster. It’s initial launch though, was only on iOS. Other versions were added after their initial wave of success.</li> </ul> Roundwall Software's Open Source Mon, 06 Apr 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/roundwall-software-s-open-source/ https://roundwallsoftware.com/blog/roundwall-software-s-open-source/ <p>At Úll this year, <a href="https://twitter.com/danieltiger">Arik Devens</a> challenged us to share more open source code with each other. Here at Roundwall Software, we benefit greatly from open source projects, both large and small ones, all the time, so it seems only right to give back. Now Roundwall Software's apps haven't been absurdly popular, but if sharing code helps someone, then good.</p> <p>Roundwall Software's first open source projects will be the apps we attempted to launch that did not work.</p> <h2 id="easyfollow"><a href="https://github.com/RoundwallSoftware/EasyFollow">EasyFollow</a></h2> <p>EasyFollow was designed to make it easy to find people on Twitter and follow them. Especially at conferences, it could be helpful to find and follow the 5 people you just met without opening your normal Twitter app which is full of mentions and messages. Unfortunately, Apple did not agree and the app was rejected for insufficient functionality.</p> <h2 id="chainguard"><a href="https://github.com/RoundwallSoftware/Chainguard">Chainguard</a></h2> <p>Chainguard has been written about <a href="http://roundwallsoftware.com/say-hello-to-chainguard/">before</a> on our blog. It did it's job for us, but not very many other people. After only 50$ in revenue, it is considered a failure and gets no more development time. Perhaps someone will find some interesting bits of code in here to help in their own projects. The code does also include a bunch of unit tests which might be interesting for people who wish to learn about testing code.</p> <p>You'll find more projects like this and perhaps some smaller libraries/utilities under the new <a href="https://github.com/RoundwallSoftware">organization</a> page.</p> Custom Segues for Fun and Profit Mon, 16 Mar 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/custom-segues-for-fun-and-profit/ https://roundwallsoftware.com/blog/custom-segues-for-fun-and-profit/ <p>With new devices like the iPhone 6+, even naysayers are considering using Storyboards. Adopting Storyboards, even for a portion of your app, means easy access to plenty of handy features such as <a href="https://developer.apple.com/library/ios/recipes/xcode_help-IB_adaptive_sizes/chapters/AboutAdaptiveSizeDesign.html">Size Classes</a>, <a href="https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/Introduction/Introduction.html">Auto-Layout</a>, and my new friend: custom animations in view controller transitions.</p> <p>Now you can technically make custom transitions without Storyboards. For example, the UINavigationControllerDelegate protocol defines a number of methods you can implement for custom transitions between view controllers when using UINavigationController's <code>-pushViewController:animated:</code>. With storyboards, we have a simpler interface that works for all transitions without needing a different kind of implementation for each situation.</p> <p>I have <a href="https://github.com/sgoodwin/ModalTranstition">an example project</a> to demonstrate. Let's walk through what's going on:</p> <h2 id="setup">Setup</h2> <p>First you'll need to be using a Storyboard for at least part of your app. This example app just has two view controllers. Each has a button, one to trigger the segue, and one to trigger an exit segue to come back. <a href="https://github.com/sgoodwin/ModalTranstition/commit/5bb3c409ee228043f2b51b8bbf64048b2032c44d">This commit</a> shows the project like this. Always good to commit before you start with the crazy stuff.</p> <h2 id="baby-steps">Baby Steps</h2> <p>First you'll need a subclass of UIStoryboardSegue. I called mine CustomSegue because I'm trying to explain a concept here, not be super creative. Your subclass needs to implement one single method, <code>-perform</code>. It is responsible for doing whatever you want to make your UI look amazing. It is also responsible for making sure that your final controller's UI is on the screen. There are some caveats to be aware of, so let's take take this in small steps.</p> <p>UIStoryboardSegue has a few properties:</p> <ul> <li>sourceViewController: This is the view controller that is doing the presenting. It has no type information, so you'll need to typecast it in Swift. So far I have tried to only cast it as UIViewController and not a specific view controller. This way your segue doesn't only work for a specific view controller subclass.</li> <li>destinationViewController: This is the view controller you're transitioning to. It shares the same type concerns as sourceViewController.</li> <li>identifier: This is so the rest of your app can see this segue coming. Your app will never manually create an instance of your custom segue, so this identifier is the only way your app will be able to find it. You probably won't need to do anything with this in your transition code.</li> <li>Additional properties: Since you're making a subclass, you can add your own properties.This gets a bit tricky, but sometimes this might be necessary. Use with caution.</li> </ul> <p>With all that information, here's my first custom subclass:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>class CustomSegue: UIStoryboardSegue { </span><span> override func perform() { </span><span> let source = sourceViewController as UIViewController </span><span> let destination = destinationViewController as UIViewController </span><span> </span><span> source.presentViewController(destination, animated: true, completion: nil) </span><span> } </span><span>} </span></code></pre> <p>We don't even do anything special! We just grab each view controller and use UIKit's existing code to do the hard work for us. Now you know how your segue was working before you provided this custom subclass. If this was all you needed, you can stop here.</p> <p>I'm going to assume you wanted something more than that, that's why you're reading an article about custom segues. Let's do that!</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>class CustomSegue: UIStoryboardSegue { </span><span> override func perform() { </span><span> let source = sourceViewController as UIViewController </span><span> let destination = destinationViewController as UIViewController </span><span> let window = UIApplication.sharedApplication().keyWindow! </span><span> </span><span> destination.view.alpha = 0.0 </span><span> window.insertSubview(destination.view, belowSubview: source.view) </span><span> </span><span> UIView.animateWithDuration(0.5, animations: { () -&gt; Void in </span><span> source.view.alpha = 0.0 </span><span> destination.view.alpha = 1.0 </span><span> }) { (finished) -&gt; Void in </span><span> source.view.alpha = 1.0 </span><span> source.presentViewController(destination, animated: false, completion: nil) </span><span> } </span><span> } </span><span>} </span></code></pre> <p>Now we've added a few things here. First we need to set the destination view's alpha to invisible and insert the destination view under the source's view. Without inserting the destination view, our animation would be fading the source controller into a black screen, not into our destination view.</p> <p>Then we can animate our crossfade. We fade the source out to invisible and fade the destination into fully visible. Crossfade!</p> <p>Finally we need to restore everything to the way it should be when our transition is done. We still use that <code>-presentViewController:animated:completion:</code> method, but this time without animation. This is to ensure our view controller hierarchy is correct when this is done. We also need to make sure we set the source view's alpha back to full. In early attempts to implement this, I left this tiny bit out and was thoroughly confused. Each time I tested the segue, presentation would work fine, but the exit would take me to a black screen. This is because the source view was still invisible. Save yourself the confusing and make sure you reset any changes you make to the source controller's view.</p> <h2 id="enjoy">Enjoy!</h2> <p>From here you're free to go nuts with all the transforms, animations, and dynamics you want to use to make your app unique and amazing. The sky is the limit! If your views are especially complicated, you might find methods like <code>- snapshotViewAfterScreenUpdates:</code> helpful.</p> Why Sun, 15 Feb 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/why/ https://roundwallsoftware.com/blog/why/ <p>One important part of success in any field is answering the question “Why?” As a musician, you might play a solo that everyone is impressed with, but unless you know why that solo worked, you’ll rely on luck to do it again the next time.</p> <p>In software this is just as important. Why is your app crashing for some users? Why did your app sell zero copies? Why did your app sell 500,000 copies? Knowing why makes it possible to consistently reproduce the results you like and avoid the ones you don’t.</p> <p>Your computer is running thanks to a ton of complicated electronics. None of this happens in your laptop by luck. Every circuit, every transistor, every diode, every capacitor, every piece is in your computer because someone figured out why they do what they do. Understanding why a transistor works enables them to pick the best transistors to do specifically what is needed in each part of your computer. Knowing why helps them allows them to make each piece cheaper and smaller as well.</p> <p>We should pay attention to details and try to understand why things happen. This will help your code, your business, and even your health. We may never know every detail, but every detail you <em>do</em> know will help you get those results you’re looking for.</p> App Video v2 Fri, 30 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/app-video-v2/ https://roundwallsoftware.com/blog/app-video-v2/ <p>My previous demo video was confusing to many people, so here is attempt #2. It's getting better!</p> <center><iframe width="560" height="315" src="https://www.youtube.com/embed/5Dj859jbwYU" frameborder="0" allowfullscreen></iframe></center> Operation Morale-Boost: Ghosty Progress Thu, 22 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/operation-morale-boost-ghosty-progress/ https://roundwallsoftware.com/blog/operation-morale-boost-ghosty-progress/ <p>While this project hasn't quite turned out like I originally intended, it has resulted in a fairly useful app I'm happy with. It has some rough edges that need some love and attention still, so here is a little video preview while you're waiting to play with the real thing.</p> <center><iframe width="420" height="315" src="//www.youtube.com/embed/fLdXv9K_YKU" frameborder="0" allowfullscreen></iframe></center> Untitled Sat, 10 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/untitled/ https://roundwallsoftware.com/blog/untitled/ <p>Project: Untitled</p> <p>something</p> <p>Total: $0.00</p> <p>Made with Chainguard.app itms://itunes.com/apps/chainguard</p> <p>(Everything above is my first published post from my app-in-progress <a href="http://roundwallsoftware.com/operation-morale-boost-meet-ghosty/">Ghosty</a>. It's not much, but this means it's working!)</p> Operation Morale-Boost: Meet Ghosty Thu, 08 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/operation-morale-boost-meet-ghosty/ https://roundwallsoftware.com/blog/operation-morale-boost-meet-ghosty/ <p>The first week of my morale-boosting project is nearly complete and I would like to show you what I've been working on.</p> <p>The app is (for now) called Ghosty. It is an iOS app for connecting to blogs using the hip platform, <a href="https://ghost.org">Ghost</a>. Like <a href="https://wordpress.org">Wordpress</a>, they provide an option to host it yourself or to pay for hosting using their service. Either way you do this, it would be great to be able to publish from your iPhone or iPad.</p> <p>iOS-based publishing is part of the reason services like Twitter and Instagram are so successful, so it seemed like a good choice to build (especially considering blogs I wrote on like this one are using Ghost).</p> <p>With sharing extentions, this effort becomes even easier, so I believe I'll be able to build a good working product in about a month. Ghosty doesn't need to be a great blog <em>editor</em>, simply a great blog <em>publisher</em>. There is a big pile of excellent editors available on iOS already, they just need a way to get that post onto your Ghost-run blog. Ghosty will provide that.</p> <p><img src="/blog/iOS-Simulator-Screen-Shot-Jan-8--2015--20-33-16-2.png" alt="" /></p> <p>Fingers crossed!</p> 2014: The Year Business Worked Wed, 07 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/2014-the-year-business-worked/ https://roundwallsoftware.com/blog/2014-the-year-business-worked/ <p>I didn't want to write a long retrospective, but I thought I should mention something about the last year.</p> <p>2013 was stressful and terrifying. I almost had to borrow money from my family to leave Amsterdam and move back to Oklahoma where I would need to live with my mom while I recovered. I didn't just hit the bottom, I smashed through the floor. That year I decided to take a risk and hold on. 2014 was the year my business worked. Now at the end of 2014 there is money in the bank, there are happy clients, residency is safe, and I was able to fly my mom and my aunt to come visit me.</p> <p>It's always hard to "know when to hold em" or "know when to fold em", especially when it involves your life or your career. I won't even pretend to claim I know what made things work, but I'm so very glad they did.</p> <p>Thank you to everyone who helped me survive. Thank you to everyone who put up with me.</p> Operation Morale-Boost Mon, 05 Jan 2015 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/operation-morale-boost/ https://roundwallsoftware.com/blog/operation-morale-boost/ <p>It's the start of a new year and that seems like a good excuse to try something different. I'd like to work on a project for funsies. For this project, I don't want to care about market, profits, making a client happy, or any of the junk that fills my RSS feeds with doom and gloom. I'd like to pick a fairly small idea and just execute on it well. It should meet a few requirements:</p> <ul> <li>Project time shouldn't take more than a month.</li> <li>It doesn't matter what gets built as long as it is built well.</li> <li>I must write about it.</li> <li>Ideally I could work on this with someone I enjoy working with. I work alone plenty already.</li> <li>It should be tested as much as possible.</li> </ul> <p>The final product will be published to the App Store for everyone to see. I don't plan on making more than about $20 from it, but an app isn't done until it's for sale. Perhaps I'll also publish the source code when it's done.</p> Say Hello To Chainguard! Sat, 09 Aug 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/say-hello-to-chainguard/ https://roundwallsoftware.com/blog/say-hello-to-chainguard/ <p>As I wrote in a <a href="http://roundwallsoftware.com/whats-wrong-with-my-app/">previous article</a>, my last attempt at making an app had problems. I'm pleased to announce today that I've finally launched something much better.</p> <p>Parts was an app to keep up with items you needed to buy for projects. It could remember where you were when you found an item, what the item looked like and how much it costs so you didn't have to. I removed Parts from sale shortly after it's release because of its problems. Chainguard is my attempt to make a better version of this app.</p> <p>First there were a host of design problems to address:</p> <ol> <li> <p>Lists could not be renamed. This meant typos lived on forever unless you wanted to delete your list and try again. Chainguard's UI allows people to simply tap on the title of a list and rename it. <img src="/blog/2krffJExC.png" alt="You can rename lists in Chainguard easily" /></p> </li> <li> <p>I was trying to be clever in Parts and make the app parse an item's name and price all in the same text field sort of like how the app <a href="http://flexibits.com/fantastical">Fantastical</a> works. This proved to be more confusing than useful and most people did not realize it was capable of this unless I specifically explained it to them. Chainguard is much less clever. <img src="/blog/2krf9QiTQ.png" alt="This is much easier to figure out." /></p> </li> <li> <p>The price input keyboard in Parts did not allow users to input decimal values. This was silly. Chainguard fixes this problem. <img src="/blog/2krfciakD.png" alt="Decimal Input For The Win!" /></p> </li> <li> <p>Parts had no sample data. First time users would find themselves with an empty list and an unfriendly experience. Chainguard fixes this by asking if the user would like a sample list on launch. The first submission of Chainguard simply included this sample list, but the Apple Review Board decided that was not good because it put data into a user's Documents folder without any action from the user. The Documents folder is only for data a user has created because iCloud backs up that data automatically. The first time prompt was my solution that allowed Chainguard to make it through the approval process. <img src="/blog/2krf8uQoh.png" alt="Now you can choose to add some sample data." /></p> </li> <li> <p>Parts suffered from my lack of design abilities. With the help of smart people like <a href="http://betterelevation.com">Dave Wiskus</a>, Chainguard looks much more like something I can be proud of.</p> </li> <li> <p>The name Parts was a poor choice. Search for "Parts" in the app store and you'll find over 1000 results. On a good day, my app was number 20 or so in that list, which is not great. Chainguard is a much better name since it is less generic. On launch day today, my app is the 3rd result for the word "Chainguard" and I'm hoping that it will move up to the 1st result soon.</p> </li> <li> <p>Parts asked for a user's location soon as you launched the app. To a first time user, this made it unclear exactly why Parts wanted their location. Chainguard is much smarter and doesn't ask for a user's location until they decide they want to record the location of a newfound item. <img src="/blog/2krfnbKsl.png" alt="Better Location-Permission Context" /></p> </li> <li> <p>Parts tried to use iCloud's document sharing feature to sync lists across devices. This proved to be a major technical problem that results in awful user problems such as missing images. Losing a user's data makes me sad, so this needed to end. Chainguard does not try to be to slick and avoids syncing through iCloud for now. I'd rather have data that doesn't sync than data that might disappear.</p> </li> </ol> <p>Now I have an app I can be proud of. It is not the best app ever created, but it is definitely an app I can show people without shame. It solves a real problem for me and hopefully for other people as well. It does this without loading screens, forced logins, or any of the other upsetting features I've built for my clients' apps.</p> <p>You can find it <a href="https://itunes.apple.com/us/app/chainguard/id895088117?mt=8">here</a>. I hope you enjoy it!</p> <center><a href="https://itunes.apple.com/us/app/chainguard/id895088117?mt=8">![Chainguard Logo](/blog/2krnbBmKd.png)</a></center> Magical Growing UITextViews inside UITableViewCells Thu, 10 Jul 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/magical-growing-uitextviews-inside-uitableviewcells/ https://roundwallsoftware.com/blog/magical-growing-uitextviews-inside-uitableviewcells/ <p>I've seen quite a few attempts at solving this problem and all of them seem to be more complicated than necessary.</p> <p>Many solutions I see involve using a method like this in your tableview's datasource:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath </span><span>{ </span><span>NSString *text = [self textForRowAtIndexPath:indexPath]; </span><span>CGSize newSize = [text </span><span> sizeWithFont:[UIFont preferredFontForTextStyle:UIFontTextStyleBody] </span><span> constrainedToSize:CGSizeMake(308.0f, CGFLOAT_MAX) </span><span> lineBreakMode:UILineBreakModeWordWrap]; </span><span> </span><span>return newSize.height+10.0f; </span><span>} </span></code></pre> <p>There are a few problems with this method.</p> <ol> <li>Magic numbers: Where does 308.0f come from? Where does 10.0f come from? Why should your tableview's datasource know or care? This puts details about your design into a place that shouldn't give a crap about your designs.</li> <li>Hard-coded fonts: What happens when you decide to use a different font in your textview? Get ready to bang your head against a wall until you remember to also change the font in your datasource.</li> <li>The sizeWithFont methods are now deprecated: If you're trying to write Swift, then you can't even use these methods and ignore the warnings. The method they added to replace these methods are broken and will return incorrect sizes all the time.</li> </ol> <p>Confronted with these problems, I poked around for a better way to work. First I made a cell like so:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>@protocol RWSGrowingCellDelegate; </span><span> </span><span>@interface RWSGrowingCell : UITableViewCell&lt;UITextViewDelegate&gt; </span><span>@property (nonatomic, weak) IBOutlet UITextView *inputField; </span><span>@property (nonatomic, weak) id&lt;RWSGrowingCellDelegate&gt; delegate; </span><span>@end </span><span> </span><span>@protocol RWSGrowingCellDelegate &lt;NSObject&gt; </span><span>- (void)growingCell:(RWSGrowingCell *)cell didChangeSize:(CGSize)size; </span><span>@end </span></code></pre> <p>This establishes a few things. First, the cell communicates to its delegate (your table's datasource) when the cell's size should change because of the textview. It doesn't say that text changed, it says that size changed. Now design details like a cell's height is inside the cell, not in the controller. This solves problem number 1 and 2. Yay!</p> <p>That just leaves problem number 3. How do we know how tall that cell should be? Here's what the implementation of the cell looks like:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>#import &quot;RWSGrowingCell.h&quot; </span><span> </span><span>@implementation RWSGrowingCell </span><span> </span><span>- (void)awakeFromNib </span><span>{ </span><span> [super awakeFromNib]; </span><span> </span><span> NSTextContainer *container = self.inputField.textContainer; </span><span> container.widthTracksTextView = YES; </span><span>} </span><span> </span><span>- (void)textViewDidChange:(UITextView *)textView </span><span>{ </span><span> CGFloat height = textView.contentSize.height; </span><span>height+= 16.0f; </span><span> [self.delegate growingCell:self didChangeSize:CGSizeMake(self.bounds.size.width, height)]; </span><span>} </span><span> </span><span>@end </span></code></pre> <p>Yep. That's it! First, you tell the text view's storage container that it's width should track with the text view. This tells the container not to try and layout text in a wider space than the text view on screen. We want it to grow vertically, not horizontally. Then you simply need to figure out the appropriate size of the cell by asking the text view for it's content size whenever text changes. No broken text-sizing methods, no deprecated methods, just let the text view do it's job and ask it how big it should be. Then you tack on whatever extra height you need for padding depending on the cell's design. It's ok to do it here because the cell knows about the cell's design.</p> <p>You can find the full-code version of this solution <a href="https://github.com/sgoodwin/Growing-Cell-Example">here</a>.</p> <p>So there you go. Now go forth and make a ton of apps with growing table cells without as much groaning and face-palming.</p> <p>**Also keep in mind: since iOS 8 adds support for self-sizing cells with Auto-Layout, even my solution might not be necessary anymore.</p> Services Mon, 24 Feb 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/services/ https://roundwallsoftware.com/blog/services/ <h2 id="app-development">App Development</h2> <p><strong><a href="https://zen.ly">Zenly</a></strong></p> <p>Zenly is a live map of your friends and family. It’s the most fun way to meet up — or just see what’s up! — so you can feel together, even when you're apart.</p> <p>Samuel was responsible for fixing up the app's support for right-to-left languages like Arabic. This meant fixes across over 300k lines of code in a project run by a fairly large team.</p> <p><strong><a href="https://www.oya.sg">OYA</a></strong></p> <p>OYA is an app to allow easy access to cell data. It seeks to simplify the signup and legal process to provision eSIM cards. No more needing to visit a store and do paperwork for a sim when you travel!</p> <p>Samuel was responsible for overhauling the early version of the app and preparing it to support multiple countries and devices.</p> <p><strong><a href="https://fairtiq.ch/en">Fairtiq</a></strong></p> <p>Fairtiq is an app to simplify travel on public transport and trains. Rather than buying tickets and passes, people can track their travel with the app. Fairtiq works behind the scenes to find the cheapest fare for your travels.</p> <p>Samuel was responsible for overhauling the underlying SDK and build version 4.0 of the app on top of it. It is well-tested and in the hands of people all over Switzerland and parts of Austria.</p> <p><strong><a href="http://human.co">Human</a></strong></p> <p>Human is an all-day activity &amp; calorie tracker that inspires you to move 30 minutes or more, every day.</p> <p>Samuel was responsible for improving performance, improving the architecture of the app, training the existing developer, and helping to add their latest social features.</p> <p><strong><a href="https://itunes.apple.com/us/app/schiphol-amsterdam-airport/id409161665?mt=8">Schiphol</a></strong></p> <p>The official app of the primary airport of the Netherlands. Samuel was the lead iOS engineer on a team with iOS engineers, Android engineers, back-end engineers, designers, a scrum master, and a product manager. They produced a clean rewrite of the app to deliver a fast and smooth experience for frequent fliers.</p> <p><strong><a href="https://itunes.apple.com/us/app/tiqets-museums-attractions/id1212818451?mt=8">Tiqets</a></strong></p> <p>A pleasant experience for buying tickets to major museums and venues in cities around Europe (and expanding). Samuel helped a recent convert to iOS grow and improve the app over the course of 4 months. This included new features as well as structural improvements to make things more consistent and easier to deal with. All crashes were eliminated.</p> <h2 id="training">Training</h2> <p>I am also available for training. If your existing team is having trouble, I'm happy to help get things back on-track. We can discuss frameworks, development processes, problem solving strategies, multi-threading issues, networking troubles, etc.</p> <p><strong><a href="http://www.theappacademy.nl">The App Academy</a></strong></p> <p>The App Academy is a 12-week bootcamp here in Amsterdam to help people become iOS developers. I taught one of these for 7 new iOS developers.</p> <p><strong><a href="https://itunes.apple.com/us/app/samenwerken-f3pix/id897714553?mt=8">Samenwerken: F3PiX</a></strong></p> <p>F3piX is an app by a local Dutch student. Without much previous programming experience, she developed this app to showcase some local Dutch photographers.</p> <h2 id="let-s-do-this">Let's do this!</h2> <p>We are located in Amsterdam and would be happy to discuss how we can help you.</p> <p>The best way to get things started is to drop us a line at <a href="mailto:[email protected]">[email protected]</a>.</p> What's Wrong With My App Fri, 31 Jan 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/what-s-wrong-with-my-app/ https://roundwallsoftware.com/blog/what-s-wrong-with-my-app/ <p><img src="/blog/9wj027cn.jpg" alt="Blynken&#39;s bummed too" /> Last year I launched my first app that wasn’t funded by a client or employer. It’s called Parts. I never expected it to make millions of dollars, but I had hoped it might at least make enough to pay the rent or some such. In total, it made $44 according to <a href="https://appviz.com">AppViz</a> (a great tool by the way).</p> <p>After some time being bummed out about this, working on client stuff, and considering attempting other apps, I thought it would be worth considering what was wrong with the app, and why it basically failed.</p> <p>Parts was originally intended to be “your purchasing todo list”. It was supposed to be your assistant when you looked for capacitors and pickups for your guitar customization, or the handlebars and cranks for your next bike build. It would accompany you while visited shops in your travels, and it would make sure you didn’t forget where you found that perfect pickguard. Parts was meant for anyone who couldn’t (or didn’t want to) buy a certain item right at the moment they found it in a shop.</p> <p>I’ve spoken with some of the app’s users, along with a group of developers and designers whom I respect, to get their opinions on the app—the good, bad, and the ugly. The results were interesting, and I thought I would share them for anyone else working on their first app.</p> <p>So, here lies my attempt at understanding what sucked about Parts:</p> <h2 id="design-problems">Design Problems</h2> <ul> <li> <p>You can’t rename lists once they’ve been named. Typos live forever.</p> </li> <li> <p>Most people didn’t realize the app could parse an item’s price and an item’s name in the same input field. <img src="/blog/92864zD2.png" alt="Secret feature" /></p> </li> <li> <p>When editing a part, the keyboard I chose for an item’s price didn’t allow for decimal values. <img src="/blog/9284EF6c.png" alt="No decimals" /></p> </li> <li> <p>Parts should have first-launched with some sample data to help explain the app’s abilities. I have been hearing this advice since before the iPhone came out, and yet, for some reason I didn’t do it.</p> </li> <li> <p>Various UI details were not implemented well. This was my first go at an app for iOS 7, and it was designed when there weren’t have many iOS 7 apps to use for guidance. And it shows.</p> </li> <li> <p>The name Parts was a bad choice. Search the app store for “parts” and you’ll find at least 1,500 results. Worse, my app isn’t even usually in the first 20 in the list.</p> </li> <li> <p>It was unclear to users why the app wanted to know the user’s location. To me, the app’s ability to remember where you were when you found something is a big part of what makes it useful. If the user doesn’t know why it’s happening, then it’s not helpful.</p> <p><img src="/blog/928s2Vww.png" alt="But... why?" /></p> </li> <li> <p>Parts made use of iCloud’s document sharing to share lists with other people. What people really needed was a way to send plain text or html that could be read by anyone, even without the app.</p> </li> </ul> <h2 id="technical-problems">Technical Problems</h2> <p>Parts was based on UIDocument. I thought this would work well since iCloud’s UIDocument syncing is supposed to work better/easier than iCloud’s Core Data syncing. This makes it a huge pain to build many useful features, such as showing a list’s remaining price before you open a list, or adding cross-list search type features.</p> <h2 id="the-solution">The Solution</h2> <p>Sometimes, you need to build an entire thing to really get a feel for how it should be designed. Many of the things I’ve learned about the problem Parts is trying to solve didn’t become clear until I had a mostly-built app, or even after it was published to the App Store.</p> <p>A big thing I will change is switching to using Core Data rather than UIDocument at the base of the app. The app will lose syncing, but gain the ability to easily have features like “show me items on any list that are closest to my location” or “show me items that cost less than this much because I only have $100 to spend on hobbies this month”. It will also mean I can be more confident that the app won’t have problems with losing a user’s image data (which happens often right now, sadly).</p> <p>As a developer, it’s easy to think the solution to a problem lies purely in code. However, my website and marketing information about the ap were also not awesome. I have enlisted the help of a friend who is <em>much</em> better at this who will be teaching me how to improve thi aspect.</p> <p>Death to Parts, long live codename Manifest. I don’t know exactly what it will be called, but it will no longer be called Parts. For now I’ll call it Manifest, because that sounds cool at the moment.</p> <p>On the plus side, an app that does poorly has very few people to disappoint when it tries to become better. I’ve learned quite a bit from making this app and everything that has happened since then.</p> Cargo Culting Wed, 15 Jan 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/cargo-culting/ https://roundwallsoftware.com/blog/cargo-culting/ <p>A cargo cult is any of various native religious cults of a millenarian and messianic character located in the southwestern Pacific islands, holding that spirits will bring large cargoes of modern goods for distribution among its adherents. [1]</p> <p>After the arrival of the computer and the practice software development, this term was used a slang to refer to a style of programming that is characterized by the ritual inclusion of code or program structures that serve no real purpose. [2].</p> <p>In a modern world of lies and skepticism, we developers have learned to question new ideas, especially when a new idea results in more work. We question ever new idea: TDD, BDD, DDD (basically, any three-letter acronym), patents, new languages, new operation systems, you name it.</p> <p>Many times, the person sharing this idea is not able to articulate the necessary information you need to decide that their idea is valid. However, just because someone is bad at explaining an idea does not mean they are simply cargo culting it without thought. Before dismissing a new idea as some cargo cult nonsense, perhaps you should stop to consider if there really is some value in there.</p> <p>Like my new hero <a href="http://www.sandimetz.com">Sandi Metz</a>, I’ll use a bike-based example.</p> <p>I ride a fixed gear bike everywhere I go. A fixed gear bike has only one speed (unless I take the bike apart to change it) and the pedals are fixed to the drive train. If the bike is rolling, then the pedals will be moving. No coasting. This is how all bikes worked before the invention if the freewheel and derailure (the part that enables gear-shifting while you roll).</p> <p>In recent years, this type if bike has become popular with kids on the street. Some might label these kids as hipsters. Many of these kids make poor examples of a fixed gear rider as they struggle to ride a bike that doesn’t allow coasting. To an outside observer, one might think they keep these bikes as a fashion trend.</p> <p>I have never thought if myself as a hipster. I’m just a guy trying to make his mom proud and feel welcome in his surroundings. For some reason, people are quick to call me a hipster and define my world and who I am by assuming I ride a certain bike thanks to cargo culting. I chose to ride a fixed gear bike for utilitarian reasons. They just maybe have never heard or don’t count those reasons as valid.</p> <p>Someone who spent time considering the choice would find that there are several utilitarian advantages it such a bike.</p> <ul> <li>No gear changing means no maintaining the gear-changing mechanism.</li> <li>No coasting means better control over you speed, especially in wet weather conditions that would negatively affect most brakes.</li> <li>Less parts means I can service most if my bike without help.</li> </ul> <p>When I first considered trying a fixed gear bike, I thought about the flack I might catch and the hipster labeling I might receive. Fortunately, I did not allow these opinions to keep me from trying something I now enjoy very much. I thought for myself, did my research and <em>then</em> made a choice.</p> <p>The same should be said for popular programming practices as well. TDD, functional programming, whatever new language or framework pops up, and anything else you’ve heard of should be properly researched before you dismiss it or embrace it. How can you make an informed decision without first being informed?</p> <p>By proper research, I definitely don’t mean just asking your friends about it or reading some blog post that rants about how awful it is. You can find someone who thinks almost any idea in programming is an awful one, often because of some bad experience they had once. Don’t take their word for it, find out real information so you can make real decisions.</p> <p>[1] <a href="http://dictionary.reference.com/browse/cargo+cult">http://dictionary.reference.com/browse/cargo+cult</a></p> <p>[2] <a href="http://en.wikipedia.org/wiki/Cargo_cult_programming">http://en.wikipedia.org/wiki/Cargo_cult_programming</a></p> Coding Exercises for Prospective Hires Are Stupid Fri, 03 Jan 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/coding-exercises-for-prospective-hires-are-stupid/ https://roundwallsoftware.com/blog/coding-exercises-for-prospective-hires-are-stupid/ <p>Earlier on App.net/Facebook I posted this:</p> <p>"Also, asking potential hires to build apps as part of the hiring process is stupid."</p> <p>Perhaps short-text social media is not enough to properly elaborate my thought. Here’s a better version:</p> <p>If you want to see professional work from a developer, pay them professional rates. That’s how capitalism works and that is the world we live in.</p> <p>I fully understand and sympathize with the goals of someone hiring a developer. They want to know that the developer they’re talking to can do the work. An excellent way to do that is to see some of their existing work. Totally valid request.</p> <p>Some developers have work already visible. They have public projects in places like <a href="http://github.com">GitHub</a> and <a href="https://bitbucket.org">BitBucket</a>. Problem solved, browse away and enjoy!</p> <p>But what happens when they don’t have code publicly available (or not code in the specific use you’re looking for)? Should we ask them to produce some code you want to see? Yes. Should we ask ask them to do this for free? Definitely not. Here is where I have a problem with these code assignments hiring companies ask developers to do. A human being has a limited time on this earth. A large amount of that time is spent sleeping. Another large chunk is taken out working (because we live in capitalist countries where not working means not eating mostly). What’s left is that special sacred time where a developer might spend time with their families or read a good book or maybe even sleep more.</p> <p>When a company asks a developer to write code for free as part of the hiring process, they’re asking that developer to give up this precious time for an arbitrary exercise.</p> <p>In the past, I have interviewed with companies who have found some good solutions to this. Some found what they were looking for on GitHub and my blog. Some paid people for a week or even a day of work where they could better evaluate someone. This is my favorite solution. Others will hire someone on a probationary period, like a couple months.</p> <p>Once a company is paying a developer for their time, whatever tests they want to do is fine. They don’t <em>need</em> to be doing work on the company’s product, that’s up to the company.</p> <p>Finding new developers is hard. Hiring people in any field is fairly difficult. Hard problems are what we solve for fun. Let’s find better solutions than asking a peer to do speculative work.</p> 2014: The Year I Wrote Better Thu, 02 Jan 2014 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/2014-the-year-i-wrote-better/ https://roundwallsoftware.com/blog/2014-the-year-i-wrote-better/ <h2 id="getting-better">Getting Better</h2> <p>Those of you who already read my blog know that I’m not the best writer. You’re likely either reading this because you’re either related to me or the technical information has helped you. Thank you for reading! This year is going to be better. More content, better writing, you’re in for a treat. The nice part about not being very good at anything is that it’s much easier to go from pretty-crap to good than it is to get from great to excellent, so this should be easy!</p> <h2 id="strategy">Strategy</h2> <p>Here’s the plot:</p> <ol> <li>Reduce friction to post.</li> <li>Reserve more time to post.</li> <li>Find a good editor (the person, not the application).</li> </ol> <p>In theory, these things will lead to higher frequency and better posts. I only wrote 15 blog posts in 2013. Some of which are maybe 2-minute reads. That’s no way to improve and not nearly enough information for people who are looking for it. If you have any other strategies for improvement, I’m happy to hear them!</p> <ol> <li>Reduce friction to post.</li> </ol> <hr /> <p>Right now I write notes about the posts I write on my phone or computer wherever I am. Then at home, I type my blog posts on my computer using <a href="http://www.vim.org">vim</a> or <a href="http://www.barebones.com/products/bbedit/">BBEdit</a> or whatever editor I feel like writing in that day. Then the post is copy/pasted to <a href="http://github.com">GitHub</a>'s <a href="https://gist.github.com">gist</a> system to easily share it with a few of my friends who make comments on readability and grammar mistakes. Once the post has been looked over by everyone, it gets copy/pasted into <a href="http://tumblr.com">Tumblr</a>. This is a pretty silly process that is rife with friction. In theory this can be reduced by streamlining the system. There are way better blogging solutions for people who want to do more than post a photo or link to an article. To actually write articles, something more like <a href="http://wordpress.org">Wordpress</a> or <a href="https://ghost.org">Ghost</a> or something I haven’t heard of is better.</p> <ol start="2"> <li>Reserve more time to post.</li> </ol> <hr /> <p>This one’s pretty straightforward. If you specifically block out time to write, you’ll actually write. It doesn’t need to be entire days at a time, just <em>some</em> time on a regular basis. Same strategy developers use to make sure they make consistent progress on their own apps. I’m going to aim for 30 minutes per day, but some more for the this first week to get things started.</p> <ol start="3"> <li>Find a good editor.</li> </ol> <hr /> <p>I’ve been told by many people that this one will help quite a bit. I know a few people who might be willing to do it. If you want to be my editor, feel free to let me know!</p> Intelligence Wed, 18 Dec 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/intelligence/ https://roundwallsoftware.com/blog/intelligence/ <p>Words like “intelligent” and “stupid” and “smart” and “genius” get thrown around quite a lot these days. I think they’re all mostly bullshit and distract from the real issues.</p> <p>In software, we often run into people who lack the depth of knowledge and experience in the various fields necessary to make apps. Many of us lack business and marketing experience, some of us lack experience in Object Oriented Programming or Functional Programming, many of us lack experience in a specific platform or language. Maybe now we use that new language or platform for work.</p> <p>Now, someone who <em>does</em> have experience in these fields (or at least thinks they do) might think “man, these other people are such idiots. I’m the most intelligent person here” or “they must be stupid because they don’t do xyz!” and that’s simply not the case.</p> <p>To say someone is stupid or an idiot or lack intelligence is reductive and definitely not helpful. In my limited time trying to teach people how to program better, I’ve run into many people who are <em>extremely</em> smart. Probably smarter than me (though I think comparisons like that are also unhelpful). The reason I’m teaching them isn’t because I’m smarter than them, it’s because I have more experience than them in the field I’m teaching them about.</p> <p>One of my clients recently hired me to come in and talk to their team about using Core Data in iPhone apps. These are all smart young people who make value in the form of software on a daily basis. They just haven’t ever used Core Data for any of that software yet, so they wanted to ask questions and get an explanation. They wanted to benefit from my experience so that they could more quickly get back to delivering value.</p> <p>So if you’re one of those people who lack experience, don’t worry about it. Un-isolate yourself, go out and find the people who can help you and absorb as much of their experience as you can. If anyone tries to put you down, you tell them to take it up with me.</p> <p>And if you’re the jerk calling everyone else idiots simply because they don’t have a depth of experience in the specific topic you’re trying to discuss or use, then shut up. Nobody cares if you’re <em>right</em> when nobody wants to talk to you or work with you anymore. Nobody listens when you call them an idiot. You probably won’t even listen to me because I called you a jerk.</p> #define Fri, 13 Dec 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/define/ https://roundwallsoftware.com/blog/define/ <h2 id="when-to-use-define">When to use <code>#define</code>:</h2> <ol> <li> <p>When you’re declaring macros like in Apple’s:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>#define UI_USER_INTERFACE_IDIOM() ([[UIDevice currentDevice] respondsToSelector:@selector(userInterfaceIdiom)] ? [[UIDevice currentDevice] userInterfaceIdiom] : UIUserInterfaceIdiomPhone) </span></code></pre> </li> </ol> <p>I’m actually not even sure why this wasn’t made as a function like <code>NSLog</code> instead of a macro.</p> <h2 id="when-not-to-use-define">When not to use <code>#define</code>:</h2> <ol> <li> <p>When you’re declaring constants. Constants should look like:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>NSString *const RWSSomeConstantName = @&quot;theValueDoesn&#39;tUsuallyMatter&quot;; </span><span>const NSInteger RWSIntegerConstant = 3; </span></code></pre> </li> </ol> <p>Why go through this effort to type extra letters?</p> <ol> <li>Constants declared using <code>#define</code> don’t show up in tools like the debugger. <code>#define</code> is raw text substitution, constants are symbols to your debugger, like class names.</li> <li><code>#define</code> won’t warn you if you use <code>#define</code> in some other file and change the value of your constant accidentally.</li> </ol> Duck Types and Objective-C Mon, 25 Nov 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/duck-types-and-objective-c/ https://roundwallsoftware.com/blog/duck-types-and-objective-c/ <p>I just finished the book, <a href="http://www.poodr.com">Practical Object Oriented Design in Ruby</a>. I haven’t been paid to write Ruby code in years and I still found the book incredibly useful. Sandy Metz writes excellent explanations for many things I have felt about code I have seen while working on projects.</p> <p>In the last chapter, Sandy discusses how to write effective tests. She walks through creating tests to document “duck types” by writing tests that make use of <code>assert_responds_to</code>. I think with Objective-C these tests are unnecessary for the most part. In Objective-C we can formalize these “duck types” with Protocols. This way the compiler will tells us when our object fail to implement the interface other objects expect. You know, assuming we don’t ignore the warnings coming from the compiler.</p> <p>A “duck type” gets is name from the phrase, “If it looks like a duck and quacks like a duck, it must be a duck”. The idea is that you can write code that doesn’t care what something actually is, so long as it responds to the right messages. This makes for loose coupling between objects because each object doesn’t have to know exactly what type of object it is working with, simply that it will respond to a certain set of messages.</p> <p>In theory, you could just use retro Objective-C tactics and declare these “duck types” as <code>id</code> instead of <code>id&lt;RWSTypeProtocol&gt;</code>. This is basically what Ruby does, objects technically have a type, but since you cannot specify the expected type of inputs to Ruby methods, there’s nothing to let you know when the wrong kind of object has been put in the wrong place.</p> <p>When we make use of the constructs provided to us by the language in Objective-C, we can eliminate the need to write tests that assert that our objects adhere to the “duck types” we expect them to. This is what protocols are for. As long as we make use of them, create them, use objects that adhere to them, and pay attention to the compiler’s warnings, we can write loosely coupled code that doesn’t need to be verified with tests. Who doesn’t like writing less tests? Certainly not this guy.</p> <p>This is why you shouldn’t arbitrarily typecast things in your Objective-C code. Typecasting tells the compiler, “shut up, I know what I’m doing!”. See <em>you</em> know what you’re doing right now, but you from 6-months-from-now might not. Certainly the new guy who now works on your project has no <em>clue</em> what you were doing.</p> <p>In conclusion:</p> <ul> <li>Embrace the language you’re writing.</li> <li>Avoid typecasting</li> <li>Make use of its advantages and avoid its disadvantages - Learn from the patterns and styles from other languages, <em>but</em> consider how they might not work exactly the same in the language you’re currently writing.</li> </ul> Capacity Thu, 31 Oct 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/capacity/ https://roundwallsoftware.com/blog/capacity/ <p>I try to be extra careful to consider my capacity before agreeing to do something for or with someone (or even for myself). Mis-judging capacity seems to often be the cause of frustration and failure. I think now I finally have the capacity to do something cool that isn’t for a client.</p> <p>I think about capacity in a few different ways.</p> <h2 id="skill">Skill</h2> <p>Producing anything, apps, music, books, needs skill. More skill and experience in theory allows a person to produce in less time. It certainly can allow a person to produce higher quality things.</p> <h2 id="social">Social</h2> <p>Some of us have a limited capacity for being around other people. Once this is depleted, we need to have some alone time to recharge. The internet makes this easier by allowing us more time to interact with others because it drains this capacity more slowly.</p> <h2 id="financial">Financial</h2> <p>Producing things also cost money. Supplies cost money, time costs money, travel and food cost money. It costs even more when you need to bring in other people to help. I think the reason many projects fail is because the people involved misjudge their financial capacity and were unable to get production far enough along to start making more. Many developers take on client work to help increase their financial capacity, but that comes at the cost of capacity for time, which often keeps them from finishing production as well.</p> <p>For the past 2 years, I have been focused on my financial capacity. I needed money to move to Amsterdam and then suddenly needed money to pay off my student loans. I could not continue to pay for my student loans on into my 30’s or 40’s, it had to be paid <em>now</em>. Fortunately that will all be over this week. I’ll be a free man with a new-found financial capacity.</p> <h2 id="time">Time</h2> <p>Human beings have a finite amount of time before we drop. Some of that time we spend sleeping, some we spend floundering about struggling to learn to walk and eat and talk, some we maybe spend in an old folks home trying to remember our names. In between we have a chance to do useful. There are only so many hours in a day and some are spent sleeping. Without enough un-allocated time, no one can produce anything cool.</p> <p>I’ve also been concerned with my capacity for time. I frequently ask myself, “Can I agree to help with this event or meet this friend given all the things I have already committed to this week?” or, “Can I build the software my clients want in the time they need me to?”</p> <h2 id="patience">Patience</h2> <p>Making things is hard. Increasing any kind of capacity needs a certain capacity for patience, including increasing patience. Capacity for patience also comes in handy during the hard slog portions of production.</p> <h2 id="let-s-do-this">Let’s Do This</h2> <p>I think now I finally have the capacity to do something new. I want to find other developers who also have enough capacity available and build something awesome. I know so many developers who have excellent skills and knowledge, high levels of patience, but often little time capacity. I’m not the desperate guy at the bar praying that he won’t be alone the rest of his life, mind you. This is just to say that I am ready, willing, and able to take up the arms and face software problems bigger than one person.</p> How to get started with remote pairing quickly Tue, 22 Oct 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/how-to-get-started-with-remote-pairing-quickly/ https://roundwallsoftware.com/blog/how-to-get-started-with-remote-pairing-quickly/ <p><a href="http://en.wikipedia.org/wiki/Pair_programming#Remote_pair_programming">Remote Pair Programming</a> is a thing now that people do. Basically, it enables you to learn from and work with anyone anywhere, ever. You can do that thing we love to do (write code) with a diverse range of people.</p> <p>So let’s say you’ve decided you like this idea of getting out of the bubble of programmers you know well and you can’t to go see how the rest of the world lives. I’ll show you how to setup your Mac to do just that:</p> <ol> <li>Install the tools you’ll need.</li> </ol> <hr /> <p>You’ll need a few things here, easiest way to install them all is with <a href="http://brew.sh">Homebrew</a>.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>brew install tmux </span></code></pre> <p><a href="http://tmux.sourceforge.net">Tmux</a> lets multiple people use the same terminal session. This is way faster than relying on any screen sharing application and works even on less-than-epic internet. It also lets you do a bunch of <a href="http://www.youtube.com/watch?v=JXwS7z6Dqic">fancy things</a> (4 minute video) to manage multiple windows and split-screens in your terminal session.</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>brew install vim </span></code></pre> <p>Technically your OS X install comes with this already, but the version we get is not the latest version of Vim. They have fixed bugs and made improvements in performance and such since the version that ships on OSX. Also, you might find that some plugin decide to use just won’t work on the older version. Might as well install it while we’re here.</p> <p>You’ll also want to grab a copy of <a href="https://ngrok.com">ngrok</a>. This is a convenient way to give people access to your laptop without needing to do all kinds of router configurations (which you maybe can’t do if you’re forced to use your ISP’s router, bleh). It also gives you some monitoring abilities, lets you track usage and keep up with who is using the connection.</p> <ol start="2"> <li>Let your partner onto your system.</li> </ol> <hr /> <p>Now I personally wouldn’t even give my password to my wife (if I had one), so here we’ll use ssh keys. Tell your pairing partner to send you their public key. If they use github already, you can find their keys by adding the extension “.keys” onto their profile url. For example, you can turn my profile url <a href="https://github.com/sgoodwin">https://github.com/sgoodwin</a> into <a href="https://github.com/sgoodwin.keys">https://github.com/sgoodwin.keys</a> and see all of the public keys I’ve added. If they don’t have one already, github provides excellent <a href="https://help.github.com/articles/generating-ssh-keys">documentation</a> to explain how.</p> <p>Once you have their key, you’ll want to add it to your <code>~/ssh/authorized_keys</code> file. If you don’t already have this file, feel free to create it. You’ll want to add an entry (just a new line) in the file that looks something like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>command=&quot;tmux attach&quot; ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC3iQgxhX0Ozb6oy9eORGIY3HowlDtUfKpWNJCwiU2F1aRA9alsxeSvjJpOxqKip3Z7//ynPUK151r/ZU9aBmv43n3RITSUQqM1BAYoXPTCrQeehBJIzEE4wXFm8NUkSwTiF7xHdCDo7rnluz58qUWDpHxcb/XwZFt17zGO/PPuJcFkuy79la37xvol2fb9No2TON54ZBbx8Xi2IzEbxbWTmMc6K+vwwxZPIAQz5EL3vBRrtyT8RHjhF3G5Z1KmD6YkX9BaYnUbX+3WYVhzo19QRgiRgE9I5IP6KNSKJuxKBb4SaYdPJbn/cUPgvZmMj1oaoTUW2236FXglIjAaDoEx [email protected] </span></code></pre> <p>The first bit there supplies a terminal command which should be executed as soon as they login. I chose the command “tmux attach” which puts them directly into my tmux session so we can get started. Whatever command you put here, the ssh connection will close as soon as the command finishes. If you put <code>ls</code> for example, it would print the contents of your home directory and then close the ssh connection. Now when they ssh into your machine, they’ll be tossed right into your terminal session and you two can get to coding.</p> <p>Once ssh is configured, turn on ngrok and let them in with the command:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>./ngrok -authtoken &lt;YOUR AUTH TOKEN GOES HERE&gt; -proto=tcp 22 </span></code></pre> <p>This will open the gates to your machine and display the url your partner can use to find your machine with ssh. Once they do get ssh to connect, you’ll be looking at the exact same terminal session and you can get the party started.</p> <ol start="3"> <li>Connect with some audio</li> </ol> <hr /> <p>Now that you can both type, it’s helpful if you can both speak. Skype does an ok job with this (audio only, video and screen sharing are slow and mostly bad). You could also use something like FaceTime Audio which has excellent audio quality in my experience. Whatever you two want to use is cool. Use your cell phones even.</p> <h2 id="some-warnings">Some warnings</h2> <ul> <li>I don’t know these Ngrok people very well, for all I know they’re recording all of my traffic to report to the NSA. Probably shouldn’t use it for your terrorist plots or for coding sessions where security of the code you’re looking at is super important. You’re working over ssh, so your work <em>is</em> encrypted, but you know, sometimes you need to be paranoid.</li> <li>If you start to feel uncomfortable about the person you’re going to pair with or even during the pairing session, pull the plug (just quit ngrok) and delete their key.</li> </ul> <p>Even with these caveats, there are some advantages to setting things up this way:</p> <ul> <li>No need to manage your configs on a different system. I’m not a fan of maintaining configurations on more than one machine, so this is a big win for me.</li> <li><a href="http://dtrace.org">Dtrace</a>. Another option you have is to share some server and work there instead of your own machine, but linux machines don’t get dtrace, your Mac does though!</li> <li>It will take you <em>maybe</em> 30 minutes to set this up. Your dev environment is already setup on your computer, all you have to add is the bits to share it with someone.</li> </ul> <p>Now that you’re equipped to pair with people, you can go <a href="http://www.pairprogramwith.me">here</a> to find more info about pairing and places you can go to find potential pairing partners. This is one of the ways we can get around not having enough more-senior developers near us. Go learn things!</p> Thank You Fri, 13 Sep 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/thank-you/ https://roundwallsoftware.com/blog/thank-you/ <p>The annual 360iDev conference just happened. I got to go, I enjoyed it very much. This time I wasn’t just an attendee, I joined the ranks of the great 360 speakers before me and attempted to do them proud. Soon my talk will be online and any of you who missed it can watch it. People I didn’t know told me they liked the talk. I even received a very nice email from a stranger thanking me for sharing my “success story”. It made me feel special.</p> <p>Twitter doesn’t have enough characters to thank the necessary people, so here we go:</p> <ul> <li>Mike Lee (@bmf): Thank you for your training.</li> <li>Judy Chen (@judychen): Thank you for being there.</li> <li>Collin Donnell (@collindonnell): Thank you for being there.</li> <li>Matt Henderson (@ghostM): Thank you for being there.</li> <li>Joe Keeley (@joekeeley): Thank you for the present and for being there.</li> <li>John Wilker (@jwilker): Thank you for giving me a shot.</li> <li>Nicole Wilker (@nwilker): Thank you for giving me a shot.</li> <li>George Woodliff-Stanley (@gmws82): Thank you for being there.</li> <li>Paul Darcey (@pauldarcey): Thank you for being there.</li> </ul> <p>Even this doesn’t really express how thankful I am for your help. I would not have been able to do it without you.</p> Make Git Friendlier: status Mon, 05 Aug 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/make-git-friendlier-status/ https://roundwallsoftware.com/blog/make-git-friendlier-status/ <p>Since I couldn’t sleep tonight, I watched a presentation by this guy <a href="http://zachholman.com/talks">Zach Holman</a> who works for GitHub.</p> <p>One tip he shares makes the output from <code>git status</code> much friendlier and I thought I’d share it so you don’t have to watch an hour of talking to find out about it.</p> <p>Instead of:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>git status </span></code></pre> <p>try typing:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>git status -sb </span></code></pre> <p>I went so far as to add this to my <code>~/.zshrc</code> file:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>alias stat=&#39;git status -sb </span></code></pre> <p>Basically, it makes the output from git go from something like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span> sgoodwin:Parts/ (master\*) \$ git status [4:13:13] </span><span> On branch master </span><span> Changes not staged for commit: </span><span> (use “git add …” to update what will be committed) </span><span> (use “git checkout — …” to discard changes in working directory) </span><span> modified: Parts/Parts-Info.plist </span><span> modified: Parts/RWSAppDelegate.m </span><span> modified: Parts/RWSListsStore.m </span><span> modified: Parts/RWSListsViewController.h </span><span> modified: Parts/RWSListsViewController.m </span><span> no changes added to commit (use “git add” and/or “git commit -a”) </span></code></pre> <p>to something more like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>sgoodwin:Parts/ (master\*) \$ stat [4:13:40] </span><span>master </span><span>M Parts/Parts-Info.plist </span><span>M Parts/RWSAppDelegate.m </span><span>M Parts/RWSListsStore.m </span><span>M Parts/RWSListsViewController.h </span><span>M Parts/RWSListsViewController.m </span></code></pre> <p>which is so much nicer because I don’t need all that other junk (also it’s in color if you have that turned on).</p> <p>Happy coding!</p> Community Thu, 13 Jun 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/community/ https://roundwallsoftware.com/blog/community/ <p>Being a part of <a href="http://appsterdam.rs">Appsterdam</a> and trying again to run my own <a href="http://roundwallsoftware.com">business</a> has made me think differently about community.</p> <p>We grew up as nerds in school and learned to expect people would think we’re lame for the things we loved. We kept the things we loved to ourselves and looked for the few people on Earth who also loved those things as much as we did so we could share with them.</p> <p>Community is really not just about sharing with other people who are as deep into what you love. It’s also about opening up what you love to teach it and make it accessible to people who are interested.</p> <p>Community is about helping and getting help from those around you. With one hand we each should reach out for help and with the other we should reach back to help those behind us. Together, we all pull each other up and the community thrives.</p> <p>The fact that the Objective-C community is still growing at a crazy rate and is already huge does not mean our community can’t be the one some of us know from before the iPhone. There’s just more of us to help each other out now, this is a good thing. We should all be reaching out so we can collectively lift the whole community up.</p> Manually set location on an item? Sat, 25 May 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/manually-set-location-on-an-item/ https://roundwallsoftware.com/blog/manually-set-location-on-an-item/ <p>Manually set location on an item?</p> <p><img src="/blog/tumblr_mndbxlkwvq1qfcjuuo1_1280.jpg" alt="" /></p> Think of the Children Thu, 21 Mar 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/think-of-the-children/ https://roundwallsoftware.com/blog/think-of-the-children/ <p>When I was in college for electrical engineering, I learned to program objective-C for the Mac and that has now become my full-time job instead of engineering. The primary reason for pursuing a life of software development instead of engineering was the community. The developers I read about like Mike Lee, Daniel Jalkut, Gus Mueller, and so on made me so very excited to write software for a living. When I had the chance to meet them in person, along with other developers, I was even more excited. It was a community, a group of people who gave a shit about each other.</p> <p>When you choose to take shortcuts in your freelance projects, consider the effect on the community. When you whip out that singleton because it’s quick and easy (and we all know it’s a bad idea), think of the community. Think of the other developer who has to add features and fix bugs on the project once you’re done. That poor guy (or gal) has children to feed, expenses to pay, and needs to take this project to make the money to take care of that. They have to add features onto that project full of your shortcuts. They have to pay the technical debt you acquired to get the job done. Like all clients, the client is hassling them to get it done as quickly as possible for as little money as possible.</p> <p>Your shortcuts have made life harder for that person. That person can’t afford to tell the client it can’t be done because they have bills to pay. The client won’t listen when that person says the app needs to be re-written because of the mess you left bend.</p> <p>Think of the children when you choose to take those quick, easy, delicious shortcuts. The children the next guy has to feed by adding features and fixing bugs in your code. Don’t just try to write better code for yourself, do it for the community. Sure, no one is perfect, but do your best to learn how to write things so that you can be nice to your fellow developers.</p> Failure Wed, 20 Feb 2013 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/failure/ https://roundwallsoftware.com/blog/failure/ <p>It is becoming popular, especially in American tech culture, to encourage failure as a natural part of learning and a key to success. Humans fail in many casual ways to learn basically anything, even basic skills like walking.</p> <p>Failure in software is often expected on a very minor level. Maybe I’ll release this software and it will have a bug in it. Maybe I won’t get that job at that big tech company I applied for.</p> <p>The real failure in software is when all the work you have done falls apart so badly, largely from your own mistakes, that you lose everything you have spent years working for and have to beg for a place to sleep with your tail between your legs barely able to afford the shirt on your back.</p> <p>It’s the kind of failure they joked about in engineering school when discussing friends who were artists and musicians and maybe even dropped out of school. We felt safe studying engineering, a profession so obviously useful that surely we will all have jobs and be paid well.</p> <p>We were wrong. Horribly wrong. If it wasn’t for software like Rails and iOS and my fortune in having a skill to work with them, I would have been unemployed indefinitely after college. Many of my friends spent <em>years</em> living with their parents maybe working at the local grocery store or mall. They had engineering degrees from a serious school with way better GPAs and qualifications than I did. The truth is everyone who is working a job they enjoy and getting paid, a job they actually wanted to work, is incredibly lucky.</p> <p>I have spent most of my life reaching out further than I should, often a breath away from failure. The day I received my rejection letter from MIT, I locked myself in my room and didn’t sleep. I refused to get out of bed in the morning, I just wanted to be left alone to cry. My mother (a decently smaller person than me) literally dragged me out of bed and would have slapped me if necessary to get me to put on clothes and go to school. She believed in me when I no longer did and refused to allow me to quit. I had people like her who saw something in me and helped me to continue and do the awesome things I have been able to do. Without them I would be the failure I’m afraid of.</p> <p>I was incredibly lucky to have a job before I graduated working on Rails and iOS projects. I was incredibly lucky that I didn’t fall flat on my face when I decided to leave that job with no plans and no savings. I was lucky to get a well-paying job in NYC. I was lucky to be in a position to even consider leaving that job to go to Amsterdam.</p> <p>I had no plan when I got to Amsterdam, I just foolishly thought it would magically work out. Turns out it didn’t work out completely. I neglected important details like having any savings, paying off my college loans (which are expensive because I failed to keep my scholarships), and the business development necessary to keep contract work coming. Now true complete failure is entirely a possibility. Not just a failed feature or a failed project. A failed, broken, person.</p> <p>Fortunately for me, my lifetime of reaching further than I should has left behind dear friends and family members who would accept that broken shell of a man, desperate and hungry, back from Europe, and they would make sure Blynken and I weren’t out on the street. Even in absolute failure I am incredibly lucky to have these people.</p> <p>This is what keeps me up at night.</p> Web Developer Admits: Objective-C > HTML5 Thu, 27 Dec 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/web-developer-admits-objective-c-html5/ https://roundwallsoftware.com/blog/web-developer-admits-objective-c-html5/ <p><strong><a href="http://engblog.polyvore.com/2012/12/web-developer-admits-objective-c-html.html?m=1">Web Developer Admits: Objective-C &gt; HTML5</a></strong></p> <p>"It felt like hammering nails into in a wonky floorboard. We fixed one problem and another would pop up. We probably spent over 80% of our time fixing weird side-effect glitches and making our code work within PhoneGap correctly. But eventually we felt it was much better than our HTML mobile version of our site with transitions and notifications and so we launched it."</p> Down with NSLog Thu, 06 Dec 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/down-with-nslog/ https://roundwallsoftware.com/blog/down-with-nslog/ <p>NSLog is a fun debug tool, but it has no place living in your version control history. Those of you who came from other languages and platforms might be used to a world where NSLog’s cousins, console.log, print, puts, and the sort were all you had to try to figure out what was wrong with your code.</p> <p>In Objective-C we have something much better: breakpoints. I think you get them in Python or Ruby or some such depending on the platform, but I don’t know about those, so I’m not talking about them.</p> <p>Here’s why the breakpoint is far superior to NSLog for your OS X and iPhone projects:</p> <ol> <li>They don’t get mixed in with your actual code.</li> <li>It takes one click or key combo to disable all of them and run your app without noise.</li> <li>You can’t forget to remove them. The binary you distribute won’t print out junk all over your customer’s computer.</li> <li>You can do way more than simply print out things when you hit a breakpoint.</li> <li>They’re not slow like NSLog. Every NSLog statement needs to create a calendar object and date formatter to put a timestamp on your information. These are expensive to create. Gross.</li> <li>You co-workers don’t need to see a stream of information in the console that has nothing to do with what <em>they’re</em> trying to fix.</li> </ol> <p>Sounds powerful, huh? It certainly is. You can even do much fancier things with breakpoints like tell the computer to make noise when your code gets to that spot. If you know python at all, you can have it execute arbitrary scripts to capture any info you like and record it any way you like. If you have an account with Apple, you can check out <a href="http://developer.apple.com/itunes/?destination=adc.apple.com.16351707109">this video</a> where they spend an hour or so talking about all the god-like power you have — all without tainting up your source code with lines that don’t directly contribute to your customer’s happiness.</p> <p>There are some times where you need legit logging. You need your app to keep a record of what it was doing. That’s fine, server-based systems do this all the time. Your own computer does it. For logging like this, you need a better solution than NSLog. People have built things like this. Try having a look <a href="https://github.com/search?q=logging&amp;p=1&amp;ref=searchbar&amp;type=Repositories&amp;l=Objective-C">here</a> for some ideas.</p> <p>Now you might be thinking “Wow, that sounds so much better, why would I bother with NSLog?” To which I would respond, “Exactly! Glad you liked my article.”</p> <p>You might also be thinking “dude, I love my NSLogs so much, shame on you for talking bad about them.” To which I’d reply, “sure, sure, use your NSLogs all you like, just don’t commit them and share them with the team.”</p> No Comments Left Behind Sat, 01 Dec 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/no-comments-left-behind/ https://roundwallsoftware.com/blog/no-comments-left-behind/ <p>I would like to explain my hatred for commented-out code. Some might call this hatred extreme. They’re probably right.</p> <p>When I look at code interspersed with commented-out lines, I think of my own home which is often covered in dog hair. Now my dog is adorable, but he does in fact shed quite a bit.</p> <p><img src="http://25.media.tumblr.com/tumblr_llvqqvm1Js1qfcjuuo1_r1_500.png" alt="Blynken" title="He&#39;s not very big, but he has a lot of hair." /></p> <p>Now for me, in my home, by myself, this dog hair bothers me maybe 2% of the time I am there. Most of the time I don’t even notice it. For <em>everyone</em> else who comes over, however, they notice it all of the time. It’s on the floor, it’s collecting in corners, it gets in your shoes. They notice it and most of the time it bothers them. I don’t vacuum for myself, I vacuum for them.</p> <p>Now back to your code. Comments collect in files, sometimes in big ugly piles, and you don’t even notice. You don’t care, you know why those lines are commented out. “Big deal,” you might say. “I might need to use those lines later if someone changes their mind.” Meanwhile, everyone else who looks at that code sees those lines and it bothers them. “Is a feature broken because that line is commented out? Was that just to debug something?” We don’t vacuum for ourselves, we do it for them. We clean up our code and delete those lines so that the next person can read our code without having a panic attack.</p> <p>Now code isn’t <em>entirely</em> the same as dog hair. Commented-out code <em>could</em> actually be useful in the future. Fortunately, for code, we have an easy solution: version control. I don’t care which one you use, just pick one and use it. You’re probably already using one. If that code you just deleted instead of commenting out really does need to be resurrected, version control will do it.</p> <p>So take a deep breath, relax, and delete those lines. Trust your version control system, it’s there to make life easier. It’s going to be ok.</p> Switches Get Stitches Mon, 19 Nov 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/switches-get-stitches/ https://roundwallsoftware.com/blog/switches-get-stitches/ <p>We have a new rule here at Roundwall Software: switches get stitches.</p> <p>Switches and if statements can easily pile up in a method and make it difficult for someone to understand what a method does. They also makes testing harder because tests that cover a method need to cover every possible branch path one might take through the code. Combining ifs and switches in the same method can quickly turn into madness. They’re not very object oriented either and we’re mostly writing code in Objective-C.</p> <p>We began with a challenge — can we write our code without switch statements? Turns out most of it could be replaced with clearer switch-free implementations without too much effort.</p> <p>A common use case for switches appears in a table view’s data source. When the table needs to support multiple section, some of these sections are static information or perhaps each section is made of different kinds of dynamic data, the mixed-section requirement means you cannot simply depend on NSFetchedResultsController’s support for sections to do this for free. You might try to accomplish this with something like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section </span><span>{ </span><span>switch(section){ </span><span>case 0: </span><span> return 2; </span><span> break; </span><span>case 1: </span><span> return [self.items count]; </span><span> break; </span><span>} </span><span>} </span></code></pre> <p>It works, sure, and you can even try to eliminate magic numbers and such using enums like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>typedef enum { </span><span>RWSSectionInfo, </span><span>RWSSectionDynamic, </span><span>RWSSectionCount </span><span>} RWSSection; </span><span> </span><span>const NSInteger kNumberOfInfoRows = 2; </span><span> </span><span>- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section </span><span>{ </span><span>RWSSection sectionIndex = section; </span><span> switch(sectionIndex){ </span><span> case RWSSectionInfo: </span><span> return kNumberOfInfoRows; </span><span> break; </span><span> case RWSSectionDynamic: </span><span> return [self.items count]; </span><span> break; </span><span> } </span><span> } </span></code></pre> <p>This way at least gives some meaning to all the switches and makes it harder to ruin everything by accidentally typing a 4 when you meant to type a 2. The compiler will even remind you when you add a new section and forget to add it to one of your switches.</p> <p>We could take it a step further and move the stuff in each case into its own method like so:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>typedef enum { </span><span> RWSSectionInfo, </span><span> RWSSectionDynamic, </span><span> RWSSectionCount </span><span>} RWSSection; </span><span> </span><span>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath </span><span>{ </span><span> RWSSection section = indexPath.section; </span><span> switch(section){ </span><span> case RWSSectionInfo: </span><span> return [self infoCellForRow:indexPath.row]; </span><span> break; </span><span> case RWSSectionDynamic: </span><span> return [self dynamicCellForRow:indexPath.row]; </span><span> break; </span><span> case RWSSectionCount: </span><span> NSAssert(NO, @&quot;This should never happen!&quot;); </span><span> break; </span><span> } </span><span>} </span></code></pre> <p>Now you might say, “dude, that’s so easy to read now and I don’t think we need to do anything more,” to which I would reply, “certainly not, we have a switch to eliminate!” Much like mobster movies, we begin to plot a way to make sure these switch statements “sleep with the fishes”.</p> <p>A fun place to get inspiration for architecting your Objective-C stuff is to look at Apple’s own frameworks. NSFetchedResultsController handles multiple sections without switches. Maybe we should model our solution to the way that works NSFetchedResultsController has an array of section objects that adhere to NSFetchedResultsSectionInfo. We don’t even know specifically what type they are, but we do know that we can ask for <code>-numberOfObjects</code> and <code>-indexTitle</code> and such to get the info we need to answer a table view’s data source questions. These objects let’s us write our data source methods without any switch statements (yay!). NSFetchedResultsController allows us to do nifty, object-oriented, switchless things like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section </span><span>{ </span><span> if&lt;NSFetchedResultsSectionInfo&gt; section = [self.resultsController sections][indexPath.section]; </span><span> return [section numberOfObjects]; </span><span>} </span></code></pre> <p>Let’s do the same huh? Let’s make classes that can contain the information we need for each section and push the datasource responsibilities of our theoretical datasource into the appropriate section. Let’s take a quick stab at a solution. Perhaps something like this?</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>@protocol RWSSection </span><span>- (NSUInteger)numberOfObjects; </span><span>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row; </span><span>@end </span><span> </span><span>@interface RWSStaticSection : NSObject&lt;RWSSection&gt; </span><span>@property(nonatomic, readonly) NSArray *items; </span><span>@end </span><span> </span><span> </span><span>@implementation RWSSection </span><span> </span><span>- (id)init </span><span>{ </span><span> self = [super init]; </span><span> if(self){ </span><span> _items = @[@&quot;Help I&#39;m trapped in the debugger!&quot;, @&quot;No no, save me instead!&quot;]; </span><span> } </span><span> return self; </span><span>} </span><span> </span><span>- (NSUInteger)numberOfObjects </span><span>{ </span><span> return 2u; </span><span>} </span><span> </span><span>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRow:(NSInteger)row </span><span>{ </span><span> static NSString *staticIdentifier = @&quot;staticSectionCell&quot;; </span><span> UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:staticIdentifier]; </span><span> </span><span> cell.textLabel.text = self.items[row]; </span><span> cell.detailLabel.text = @&quot;I&#39;m a static row!&quot;; </span><span> </span><span> return cell; </span><span>} </span><span> </span><span>@end </span></code></pre> <p>Now we have a static section that works in a similar fashion to the NSFetchedResultsController style. No more switches, yay! We can easily make use of the section like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>@implementation RWSViewController </span><span> </span><span>- (id)initWithStyle:(UITableViewStyle)style </span><span>{ </span><span> self = [super initWithStyle:style]; </span><span> if (self) { </span><span> RWSStaticSection *section1 = [[RWSStaticSection alloc] init]; </span><span> RWSStaticSection *section2 = [[RWSStaticSection alloc] init]; </span><span> _sections = @[section1, section2]; </span><span> } </span><span> return self; </span><span>} </span><span> </span><span>#pragma mark - Table view data source </span><span> </span><span>- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView </span><span>{ </span><span> return [self.sections count]; </span><span>} </span><span> </span><span>- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section </span><span>{ </span><span> RWSSection section = self.section[section]; </span><span> return [section numberOfObjects]; </span><span>} </span><span> </span><span>- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath </span><span>{ </span><span> RWSSection section = self.section[indexPath.section]; </span><span> return [section tableView:tableView cellForRow:indexPAth.row]; </span><span>} </span><span> </span><span>@end </span></code></pre> <p>Look at that! No switches, just objects. If you were writing unit tests, you could feed the controller a mock section to test that the view controller sends expected messages to your fake section. You could then test sections individually as you added them. Easier testing. Easier reading. Easier maintenance. Happy days are here again.</p> Code Examples Thu, 15 Nov 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/code-examples/ https://roundwallsoftware.com/blog/code-examples/ <p>The point of sample code is to show you just enough code to communicate a <em>concept</em>. Sometimes code for a given demonstration (such as showing how to use parts CoreData or CoreText) requires a bit of boilerplate in order to show the concept.</p> <p>For example, let’s consider a sample project to demonstrate how to draw text in a spiral using CoreText. You would need code to allow you to actually display your CoreText example code. This code has nothing to do with what you’re trying to demonstrate, but it is necessary for the project to function. Now you don’t want to bundle your sample project with an entire production-ready, functioning app because that would be way more code than a developer wants to look at to learn about CoreText’s ability to draw in a spiral. This is a super cool feature, we want to make sure people know how to make this happen. In an effort to reduce the amount of code included in your sample project, you might chose to do things that you would <em>never</em> do in a production app. You might violate all kinds of development principles and have one object handle 45 different jobs. You might put all kinds of functionality into only a few methods. This means a few things for the person reading your sample code:</p> <ul> <li>Just because you see it done in example code, <em>even from Apple</em>, doesn’t mean that is how you should do things in your own app.</li> <li>The code is to show you a concept, not write your app for you.</li> <li>They probably aren’t properly handling errors.</li> </ul> <p>So kids, never copy-paste code out of an example into your app blindly. It is for educational purposes and not to save you the thought and time needed to write your app.</p> External Storage with Core Data Sun, 11 Nov 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/external-storage-with-core-data/ https://roundwallsoftware.com/blog/external-storage-with-core-data/ <p>New since iOS 5.0, we have a new weapon at our disposal for Core Data. External Storage lets you tell Core Data that you’re going to jam large data blocks (mp3s, pngs, whatever) into a property on your Managed Object. Normally you might say “Oh no! Why would you do that? Everyone knows you shouldn’t give large data blobs to Core Data! You should write them out as a separate file you idiot!” iOS 5.0 changed everything, it’s ok. Now you can let Core Data handle those large files and stop bothering to write all kinds of files and handling deleting them yourselves. Yay less code!</p> <p>If you check the Google or Apple’s docs, you’ll see articles explaining how to get things running, but what you won’t find is an explanation for how Core Data deletes those files. I setup a test application to find out for myself. You can find it <a href="http://cl.ly/340I29362001">here</a>. My tests show that the external files get deleted when you simply nil out the property and save your context. It will also delete the file if you delete the whole entity. Since all you need to do is nil out the property, you don’t need to make separate entities for your data blob properties. Whooo!</p> Quote: Growing Object Oriented Software Mon, 05 Nov 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/quote-growing-object-oriented-software/ https://roundwallsoftware.com/blog/quote-growing-object-oriented-software/ <p><strong><a href="http://%20https//itunes.apple.com/us/book/growing-object-oriented-software/id401429854?mt=11">None</a></strong></p> <p>We cannot emphasize strongly enough that first-cut code is not finished. Its good enough to sort out our ideas and make sure we have everything in place, but its unlikely to express its intentions cleanly. That will make it a drag on productivity as its read repeatedly over the lifetime of the code. Its like carpentry without sandingeventually someone ends up with a nasty splinter.</p> A Massive Speed Difference In iPhone CoreData Fri, 19 Oct 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/a-massive-speed-difference-in-iphone-coredata/ https://roundwallsoftware.com/blog/a-massive-speed-difference-in-iphone-coredata/ <p>It was brought to my attention last night by my good friend, <a href="http://collindonnell.com">Collin Donnel</a>, that using a fetch request to grab objects based on their objectID’s was much slower than simply using a for loop and the NSManagedObjectContext method -existingObjectWithID:error: to grab each object one by one. My initial response was supreme disbelief. How could this be true? I needed a break today from my pile of work to do, so I thought I would investigate and see what’s up.</p> <p>I started with a dummy project to create a scenario I could measure. You can find that project <a href="http://cl.ly/3j2E0Q3D3w1O">here</a>. I just created a single-view app with 2 buttons, one to trigger a fetch with a predicate and one to trigger finding objects with a for loop. When the app launched, it creates 1000 objects to have some test data. Nothing fancy, a simple object with a few properties. The code for these two fetch methods are fairly straightforward as you can see here:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>+ (NSArray *)fetchRequestobjectsWithIDs:(NSArray *)objectIDs inContext:(NSManagedObjectContext *)context </span><span>{ </span><span> NSFetchRequest *request = [NSFetchRequest fetchRequestWithEntityName:[self entityName]]; </span><span> [request setPredicate:[NSPredicate predicateWithFormat:@&quot;SELF in %@&quot;, objectIDs]]; </span><span> </span><span> NSError *fetchError = nil; </span><span> NSArray *results = [context executeFetchRequest:request error:&amp;fetchError]; </span><span> if(!results){ </span><span> NSLog(@&quot;Error fetching: %@&quot;, fetchError); </span><span> } </span><span> return results; </span><span>} </span><span> </span><span>+ (NSArray *)forLoopObjectsWithIDs:(NSArray *)objectIDs inContext:(NSManagedObjectContext *)context </span><span>{ </span><span> NSMutableArray *objects = [NSMutableArray arrayWithCapacity:[objectIDs count]]; </span><span> [objectIDs enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { </span><span> NSError *findError = nil; </span><span> NSManagedObject *object = [context existingObjectWithID:obj error:&amp;findError]; </span><span> if(object){ </span><span> [objects addObject:object]; </span><span> }else{ </span><span> NSLog(@&quot;Error finding object %@&quot;, findError); </span><span> } </span><span> }]; </span><span> return objects; </span><span>} </span></code></pre> <p>I ran this test project on my iPhone 4 (I know, so old) and was quite surprised by the results. The trace it generated looks like this:</p> <p><img src="http://f.cl.ly/items/431s3s3T1o1B0C3e1k18/SpeedTestTrace.png" alt="The Speed Test Trace" /></p> <p>The first bit of activity there is the app launching, and the 1000 objects getting created. Not interesting for the purpose of this article. That big spike there is the fetch request. In between those 2 spikes, the tiny bit you can barely see, is the for loop version. Amazing right? Which do you want in your app, that activity spike to fetch 1000 objects? That tiny bit of CPU usage you can hardly see on the timeline?</p> <p>Now go forth and enjoy your faster app! Also, be sure to buy a copy of Collin’s app, <a href="https://itunes.apple.com/us/app/pinbook-for-pinboard/id564452716?mt=8">Pinbook</a> to say thanks for the tip.</p> My Apology Sun, 19 Aug 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/my-apology/ https://roundwallsoftware.com/blog/my-apology/ <p>I take back everything I said about <a href="http://www.rubymotion.com">RubyMotion</a>. While spending some time hanging out with some old musicians (guys in their 60s) I came to realize just how terrible I sound by saying pretty much anything negative about efforts in software-land like RubyMotion. Largely my motivations for disliking the project stemmed from my fear that I would have to work with people who chose to use it to make their project. That fear is entirely invalid. I will probably never <em>need</em> to work on a project using RubyMotion same as I won’t ever need to work on a Java project or any other language I might not like. I am fortunate enough to have the skills and friends necessary to easily find work whenever I need it. I then repaid the world for my good fortune by openly bashing a project which did not deserve my hatred. This disappoints me as much or more than it disappointed you.</p> <p>You should do whatever it takes for you to develop your app, wether it’s some language/framework I don’t like or some intense yoga routine. As long as you’re not stealing or trying to screw over somebody, go nuts. It certainly might end up being a terrible way to develop software, but there are <a href="http://www.sparrowmailapp.com">plenty</a> of other <a href="http://arstechnica.com/information-technology/2011/03/twitter-tells-third-party-devs-to-stop-making-twitter-client-apps/">reasons</a> your app will disappear (or <a href="http://www.wired.com/business/2009/07/apple-rejects-google-voice/">fail</a> to <a href="http://blog.macromates.com/2">launch</a>) anyways. Besides, even if you write your next iOS app with Objective-C like “normal” people do, you could still end up with a spaghetti-pile of madness no sane programmer would enjoy. It happens.</p> <p>At some point, it is entirely possible for the iPhone to fall or for Apple to decide to use a different language for development. In either case, I’ll be able to learn whatever I need to get moving on the next platform/language I need assuming I stop acting like an asshole.</p> Buzz Andersen: Getting Final Cut Thu, 14 Jun 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/buzz-andersen-getting-final-cut/ https://roundwallsoftware.com/blog/buzz-andersen-getting-final-cut/ <p><strong><a href="http://log.scifihifi.com/post/25094818476/getting-final-cut">Buzz Andersen: Getting Final Cut</a></strong></p> <p>I love this post. Buzz is awesome.</p> Your Best Wed, 06 Jun 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/your-best/ https://roundwallsoftware.com/blog/your-best/ <p>At work, everyone expects you to do your best. Wether you are a freelancer, a completely-independent developer, or a company person, everyone expects you to do your best. But what does that really mean? Your best? The best work you’ve ever done? This phrase keeps me up at night. This phrase makes me panic at work. This phrase has lead me to act like a jerk to people who didn’t deserve it. This phrase has made me hate projects and experiences that weren’t actually that bad.</p> <p>What you have in your mind right now about what your “best work” looks like is probably wrong. It most definitely does not match what your client/employer/customer thinks is your best.</p> <p>The work you produce is influenced by many things. If your client hands you designs and pays you to build an app, there isn’t much you can do about the quality of those designs. Maybe they don’t even make sense to you. That doesn’t matter. Your client liked whatever those designs described and they just need your to build it. This will not be your best app you’ve ever written. The app’s audience may not even like it. Your best in this case is simply to accurately reproduce what the designs depict. As a programmer, you might put effort into architecting the code so that changes later can be made easily and bugs can be kept to a minimum, but you can do nothing about how that app behaves beyond making it behave as your client expects given the designs they gave you. You can make yourself so much more stressed, unhappy, and unpleasant by expecting more from your work.</p> <p>In the short term, it’s ok that your work is not amazing. You best is simply your best considering the circumstances. Bad designs, your own lack of skill in an area, low-skilled co-workers, crazy product managers, lame CEOs, all affect the quality of your work and how closely it matches your concept of the best you could have done.</p> <p>In the long term, if you are going to continue to work with these people/requirements/situations that influence your work, the question is: can these be improved? Can you learn from this project to make better code next time? Can you learn more about your platform to be able to do things in a simpler fashion next time? Can you educate your co-workers in coding techniques/practices that lead to less bugs? Can you help your product manager/designer understand the platform you are doing your work on? Is there something you can do that will help over time?</p> <p>If the answer is yes, do those things and relax. Nothing changes instantly and expecting anything short of that is silly. You and your team are making progress and that’s all you can expect.</p> <p>If the answer is no, maybe you should be looking to change positions/jobs/fields. Not everyone is willing to change no matter how you approach them. That’s ok too, that’s their choice. You just need to chose different people to work with.</p> <p>You should assume everyone is trying to do their best. Nobody thinks to themselves, “I think I will do a shit job today, that sounds like fun. Let me try to ruin this project and make everyone hate me. I hope this is the worst app to ever be seen by human beings.”</p> <p>For example: I’m a pretty terrible writer and this post is probably poorly written, but that’s not because I sat down and said “how can I waste everyone’s time by writing the most unclear pile of failure I can into a blog post.”</p> I have decided Wed, 23 May 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/i-have-decided/ https://roundwallsoftware.com/blog/i-have-decided/ <ul> <li>I will make myself proud of myself.</li> <li>I will no longer use my current situation as an excuse for my lack of action or progress.</li> <li>I will not be distracted by tools or toys, but press on to the solution and that which gets me there.</li> <li>This is the year I become who I want to be instead of simply planning it.</li> <li>I will continue to do that which is terrifying and/or embarrassing.</li> <li>I have the strength necessary to do what must be done.</li> <li>I will eliminate the fear that causes me to hesitate.</li> </ul> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>I must not fear. </span><span>Fear is the mind-killer. </span><span>Fear is the little-death that brings total obliteration. </span><span>I will face my fear. </span><span>I will permit it to pass over me and through me. </span><span>And when it has gone past I will turn the inner eye to see its path. </span><span>Where the fear has gone there will be nothing. </span><span>Only I will remain. </span><span>- Litany Against Fear. </span></code></pre> The Amsterdam Plan Fri, 23 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/the-amsterdam-plan/ https://roundwallsoftware.com/blog/the-amsterdam-plan/ <p>Now that I am moving to Amsterdam, I need a new plan. I have learned so very much since the last time I wrote out a plan and I am both excited and terrified.</p> <p>Previously I was very much against freelance/agency type work due to my frustrating experiences in this field. In the last year I have learned that most of these frustrations weren’t really problems with freelancing or working for an agency, but more a problem with my own experience and abilities. I feel that most of these things will no longer be as frustrating and will only get better as time goes on.</p> <p>Much of what I have learned has become part of my own theory about how software development should be handled. People throw around words like XP and Agile, but there are some important aspects of these practices that I will be using in my own development:</p> <ul> <li>Frequent feedback from stakeholders (or customers/clients/whatever-you-want-to-call-them) is absolutely necessary</li> <li>Code testing is very important to prevent regressions and give you the ability to safely refactor as necessary as things change and/or requirements become more clear.</li> <li>XP is not an excuse to just hack and slash and hope everything works out. Concern must be given to application architecture and design as the project evolves.</li> <li>Estimating the time necessary to complete each requirement is quite helpful for discussions involving deadlines, scope, cost, and priorities (this and your basic <a href="http://en.wikipedia.org/wiki/Project_management_triangle">product management triangle</a>)</li> </ul> <p>Other important things I have learned come from working on 2 fairly-large projects that have been running for a long time. WhoIsNear? and AreYouInterested? are both long-term projects that have had several people touch them. Requirements changed, new features came, old features were removed and designs were changed sometimes frequently. Projects I worked on in Denver only ran for about as long as it took to initially build them for the most part. Many did not get updated after their release while some did not even see the light of day at all. Projects like WhoIsNear? and AreYouInterested? helped to illuminate how important proper XP-like practices are, not just to get the initial project built, but to allow for a healthy project that can continue to be stable and effective years later.</p> <p>In short-term projects it can be tempting to agree to some short-term wins even it results in a large pile of technical debt. Who cares if you’re only working on the project for a few weeks eh? Effective projects need to be built in such a way that technical debt you acquire initially can be more easily paid off in the future when you can. It’s cool to make use of someone else’s library or framework when necessary, but keep in mind that their code now counts as a part of your total inventory. That giant framework you’re only going to use a part of? That’s <em>all</em> your code now. It’s all part of your project. Either you need to be willing to accept this code, contribute to it, and get involved in the inner workings or your project needs to be designed in such a way that that library/framework can be replaced later when you have the opportunity.</p> <p>A classic example for iOS developers comes from the three20 framework. When it was initially released, many developers jumped to use it in their new projects without truly owning the code inside. Months after its release, all of those initial developers were denied entrance to the App Store because of code in three20 (undocumented code for the win). They did not own the code and become involved in the inner workings and got slapped in return. Three20 was also such a substantial framework that it became very difficult for developers to remove the code from their apps even if they wanted to. To this day, there are still plenty of active projects that have been using three20 for the last 2-3 years because no one had the time to take on the monumental task of removing three20 from their applications.</p> <p>So with all of this, I will be taking on freelance projects in Amsterdam while I work on some of my own ideas. I’m looking forward to using these smallish projects as an opportunity to try different types of architecture and design while also perfecting my business/dev-process methods. Turns out making awesome code is way more than simply knowing how to do some nifty things in code. There’s a whole world to it beyond just going clickety-clack on your keyboard and expecting amazingness to come about. Should be fun :)</p> Trying this "mechanical keys" thing. Wed, 21 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/trying-this-mechanical-keys-thing/ https://roundwallsoftware.com/blog/trying-this-mechanical-keys-thing/ <p>Trying this “mechanical keys” thing.</p> <p><img src="/blog/tumblr_m17pxaJq1f1qfcjuuo1_1280.png" alt="" /></p> Why I'm leaving Tue, 20 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/why-i-m-leaving/ https://roundwallsoftware.com/blog/why-i-m-leaving/ <h2 id="adventure">adventure</h2> <p>You’re lying to yourself and everyone around you if you ever say you don’t want to go somewhere new. It’s ok, I lie to myself about all kinds of things. Exploration is a critical part of the human condition. A biological imperative to continue the species. Amsterdam is a very new place. A whole new country and continent as well. What I’ll be doing in Amsterdam will be a more difficult, more challenging, more terrifying version of what I have been doing. I thought the same about NYC before I arrived last year, but I’m here now and the fear is mostly gone. That tells me it is time to step it up a bit and scare myself.</p> <h2 id="opportunity">opportunity</h2> <p>I cannot count the number of times I have talked to someone and listened to them talk about how “one day” they’re going to go to a place or do something. I fear that some of these people will spend their entire lives and never once actually see that place or do that thing. Tomorrow is not guaranteed which means we should be doing the things we talk about. I don’t want a year to go by where I sat around wishing I had taken the opportunity to go live abroad and be part of something huge. Opportunities like this don’t come often and you must be ready to go when the bell rings. It is my time and I would be an asshole to sit around and ignore it.</p> <h2 id="freedom">freedom</h2> <p>When someone tells you you cannot do something, that is suddenly all you want to do. I spent a few months this year unable to walk after breaking all three bones in my ankle. Even though I had plenty to do from my couch, all I really wanted to do the whole time was walk outside and skate. This sort of frustration couldn’t be avoided with my broken ankle, but it <em>can</em> be avoided in my career.</p> <p>This new freedom, of course, comes with more responsibility. On my own, it is much harder to blame my failures on anyone else but myself. No one will give me a paycheck if my ideas don’t work out. No one will pay for my health insurance (although, in Amsterdam, this is less of an issue). This is scary and will likely be difficult. I like to force myself to do scary things as often as I can.</p> <h2 id="further-reading">further reading</h2> <ul> <li><a href="http://networkcultures.org/wpmu/topologies/2012/01/23/appsterdam/">About Appsterdam</a></li> <li><a href="http://sealedabstract.com/rants/do-hard-things/">Do Hard Things</a></li> </ul> No more SOLID Tue, 13 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/no-more-solid/ https://roundwallsoftware.com/blog/no-more-solid/ <p>The more I research these SOLID principles, the more I see how they don’t necessarily apply to modern programming and even less so to Objective-C. It’s easy to want to over-engineer, over-architect, or over-work anything you do as a programmer. Who wants to miss an opportunity to flex their muscles and improve their skills?</p> <p>I’m not going to write about the LID in SOLID, there’s plenty of info on the internet if you care to look. I <em>am</em> going to continue my research and come back with more once I’ve learned enough that I feel I could write a more useful blog post. If you’re bummed about this want want to discuss the SOLID principles (or any principles, methods, patterns, isms, etc), feel free to contact me. I’m down to talk often, though my responses may be delayed.</p> <p>Thanks for reading (all 10 of you)! I appreciate it.</p> My first talk, "Refactoring and stuff" given at NYC Cocoaheads Fri, 09 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/my-first-talk-refactoring-and-stuff-given-at-nyc-cocoaheads/ https://roundwallsoftware.com/blog/my-first-talk-refactoring-and-stuff-given-at-nyc-cocoaheads/ <p><a href="http://speakerdeck.com/u/sgoodwin/p/refactoring-and-stuff">My first talk, "Refactoring and stuff" given at NYC Cocoaheads</a></p> SOLID Part 2 Tue, 06 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/solid-part-2/ https://roundwallsoftware.com/blog/solid-part-2/ <p><strong>Open/Closed Principle</strong></p> <p>Ivar Jacobson once said</p> <blockquote> <p>All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version.</p> </blockquote> <p>This is why we want to try to architect our projects in such a way that the future doesn’t become ever so painful. The principle I’ll discuss in this article helps to make that a reality. Back about 2 years after I was born, Bertrand Meyer described the principle when he stated</p> <blockquote> <p>"Software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification."</p> </blockquote> <p>Another way to state the principle is that your classes, modules, and functions should be written in such a way that future changes can be accomidated by extending existing code with new code instead of rewriting the original code. If the existing code in a class works and you don’t need to modify it to accomidate the new changes that need to happen, that existing code <em>still</em> works. This also means any code depending on that class still works. This ideal situation is a win for everyone. QA doesn’t have to roll their eyes and file regression bugs (and by QA I mean you if you’re a one-man shop), product managers don’t suffer from the nervousness that stems from changes in code resulting in broken issues elsewhere, programmers can enjoy writing new code instead of figure out what broke, smooth sailing for all.</p> <p>This is probably one of the hardest to implement of the SOLID principles. It’s not really possible to close your code from all possible changes while still making it flexible enough to handle whatever your next client meeting comes up with. The more experience you gain however, the easier it can be to predict the kinds of ways your code might need to change and make plans accordingly. Some common changes I have encountered includes</p> <ul> <li>"Maybe this list will need to be in a different order"</li> <li>"Maybe request URLs will need to change in this HTTP requester"</li> <li>"Maybe the routing style/method/order will need to change in this HTTP server"</li> <li>"Maybe these UI elements will need to also be used on the iPad"</li> </ul> <p>The list goes on, but you get the idea.</p> <p>Another part of this principle is that modules should depend on less-volatile modules. If code changes in a module your code depends on, that means your code needs to change too. This would violate the Open/Closed principle. It’s not a hard law and all, but you definitely save yourself headache the more you’re able to adhere to this principle.</p> SOLID Part 1 Fri, 02 Mar 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/solid-part-1/ https://roundwallsoftware.com/blog/solid-part-1/ <p><strong>The Single Responsibiliy Principle</strong></p> <p>Robert C. Martin wrote about the principle in <a href="http://www.objectmentor.com/resources/articles/srp.pdf">his article</a> back in 2002. The principle states:</p> <blockquote> <p>"There should never be more than one reason for a class to change." — Robert C. Martin</p> </blockquote> <p>As requirements for your project change, your code will need to change to meet them. The more reasons you have to change a given class, the greater chance you have of introducing bugs. Consider the case of the often misused Application Delegate. According to Apple’s <a href="https://developer.apple.com/library/ios/#documentation/UIKit/Reference/UIApplicationDelegate_Protocol/Reference/Reference.html">documentation</a>, an Application Delegate’s one responsibility is to respond to application-level events such as your application being sent to the background or your application has finished launching. You might feel tempted to add additional responsibilities to your Application Delegate for convenience. Handling your Core Data stack, downloading content to display in the Application, and other such responsbilities taint the one single job of the Application Delegate. A few problems arise when this happens:</p> <ul> <li>Changes in the Application Delegate’s other responsibilities require changes to the same class responsible for responding to application events. This potentially results in breaks in your class’s original responsibility: responding to application-level events.</li> <li>Multiple responsibilities also reduce your ability to reuse code across your application and/or other projects. Should a command-line utility that wants to use your app’s same storage mechanism require your Application Delegate (and any other classes the Application Delegate needs to do that new job) to do it’s work? Hopefully not, because you don’t get UIKit when you leave the iPhone. This could be painful.</li> <li>The extra responsibilities also lead to increased compile time. When you change one class, Xcode needs to re-compile every class that depends on this class. More responsibilities means more classes depending on your class which means more twiddling your thumbs while your computer struggles. This becomes more noticable the larger your application gets.</li> </ul> <p>Apple’s own code follows this rule fairly closely. UITableView, for example, is responsible for handling a collection of cells in a vertically scrolling list. It is not responsible for actually drawing each cell, it leaves this job to cell objects that you supply to the table. It doesn’t keep up with the data that is reflected in the list, it leaves that job to the the object who’s job is to be the datasource (which you provide). Because the table view is only responsible for one job, you don’t need to manipulate or generally even subclass UITableView. To change how information is displayed in the list, simply supply a different UITableViewCell. Want to change how your app knows how many rows to show in your table? Simply supply a different datasource (or change your existing one if necessary). Keeping to one responsibility makes UITableView useful in about a bajillion applications that display all kinds of different lists. Generally it doesn’t hurt to mimic Apple’s own code structure, lots of super smart people have, currently do, and will work there.</p> Object graph dependency analysis Sun, 26 Feb 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/object-graph-dependency-analysis/ https://roundwallsoftware.com/blog/object-graph-dependency-analysis/ <ol> <li>How to generate the graph</li> </ol> <hr /> <p>Generating and reviewing a graph of the object decencies in your app is a super handy way to be aware of your app layout, spot problems in your architecture, and make better decisions when adding new code in the future. With very simple applications, you could certainly imagine the graph in your head easily enough, but as your project grows, you are more likely to run out of the mental space necessary to hold the entire data-set in memory.</p> <p>For objective-c or c projects, <a href="https://github.com/nst/objc_dep">objc_dep</a> is a handy tool to quickly generate the graph for you. The script can be run like so:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>python objc_dep /path/to/your/project &gt; projectname.dot </span></code></pre> <p>This dot file you generate can be opened in apps like <a href="http://www.omnigroup.com/products/omnigraffle/">Omnigraffle</a> or the free alternative (not as amazing, but if you’re desperate) <a href="http://www.graphviz.org/">Graphviz</a>. Omnigraffle is far superior and has better tools for helping you try to figure out what you’re looking at and arrange it. Once you have the graph, see if it matches your mental model of the project and follow along as I explain how to get some value out of your picture.</p> <ol start="2"> <li>Unused code</li> </ol> <hr /> <h3 id="spot">Spot</h3> <p>Once you have your graph and look around, the most obvious thing you should be able to spot is entirely unused code. If an object or group of objects in your graph aren’t connected to anything else then you probably found some unused classes that were never deleted.</p> <p>Another handy tool to use to spot unused code is the application <a href="http://www.jetbrains.com/objc/">Appcode</a> from Jetbrains. It’s code-inspection tool can point out a number of issues, one of which is the unused code. Do not blindly delete everything it points out though because it has issues with a number of false-positives such as:</p> <ul> <li>Methods that get used via bindings.</li> <li>Methods connected in Interface Builder</li> <li>Methods inside #if statements.</li> <li>Probably others</li> </ul> <p>With any of these refactorings, <strong>you should be using version control and commit before any step</strong>. Things like this are exactly what refactoring is for. If you’re using Git, the <code>git bisect</code> command is super handy here too.</p> <h3 id="refactor">Refactor</h3> <p>First step to reorganizing your code is to ditch all this mess that doesn’t do anything. If you have commented-out code: delete it. If you have unused code or even entire classes: delete them. If you’re using version control like any sane individual would, all of that code will live on in your records. There’s no need to keep old junk around in the present when it is also safely stored in your history. Burn it all down, it’ll be ok.</p> <ol start="3"> <li>How to spot MVC violations</li> </ol> <hr /> <h3 id="spot-1">Spot</h3> <p>Correct MVC dependencies look like this:</p> <p><img src="http://upload.wikimedia.org/wikipedia/commons/b/b5/ModelViewControllerDiagram2.svg" alt="MVC Architecture Graph" /></p> <p>In this example, solid lines represent direct dependencies while the dotted lines represent indirect dependencies via observation (or bindings if you’re on OS X). You should be able to arrange all items on the graph to clearly show which classes in your project are models, views, and controllers. Your views should only directly depend on your model, your controller should depend on your model and your views, and your models should be dependent on nobody. This is the point of the MVC architecture.</p> <h3 id="refactor-1">Refactor</h3> <p>Cleaning this part of the graph up can prove to be a simple quick step or something far more complicated.</p> <p>In the best cases, the dependencies in your graph are simply typos. Unnecessary <code>#include</code> statements and the sort can generate false-alarms that can make it look like your architecture is more broken than it really is. If this is the case, simply delete the unnecessary statements and celebrate.</p> <p>Problems get worse when you have more serious problems such as models that depend on your views and/or controllers. Objective-C (and more specifically Cocoa/Cocoa Touch) offer a few mechanisms to help you break these dependencies and spread the MVC love. An explanation for each of these mechanisms can be found in the book <a href="http://www.amazon.com/Cocoa-Design-Patterns-Erik-Buck/dp/0321535022">Cocoa Design Patterns</a> Eric B. and Donald Y and in Apple’s own <a href="https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CocoaFundamentals/Introduction/Introduction.html#//apple_ref/doc/uid/TP40002974">documentation</a> (no login required even). A few notable patterns include:</p> <ul> <li>Chain of Responsibility: Views don’t need to know or care about what controller is going to respond to the actions they send, simply that someone in the chain of responsibility will answer the message. Thus eliminating dependencies of views upon controllers.</li> <li>Observer: Views can observer controllers or models or the global notification center rather than directly depending upon a given controller.</li> </ul> <ol start="4"> <li>Conclusion:</li> </ol> <hr /> <p>Generating and analyzing your object graph can help to ensure that the awesome architecture you imagined in your head is actually reflected in your code. It can also help you and/or your team discuss how to improve the architecture of your code and make a plan for the future. Nothing is ever perfect, certainly not on the first try. The point is to get better and use tools like this to get closer.</p> Quote: You Are Not Ruthless Enough Sun, 19 Feb 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/quote-you-are-not-ruthless-enough/ https://roundwallsoftware.com/blog/quote-you-are-not-ruthless-enough/ <blockquote> <p>"Even on a tight schedule, last-minute block-ship bugs appear and what should have been a simple, straightforward bug fix will turn into some Giger-esque state-driven nightmare causing everyone associated with the project to invent new profanities because the ones they have don't seem emphatic enough." — Chris Parker</p> </blockquote> <p><a href="http://playswithfire.com/blog/2012/02/19/you-are-not-ruthless-enough/">http://playswithfire.com/blog/2012/02/19/you-are-not-ruthless-enough/</a></p> Quote: Super Quiet Non-Understanding Thu, 09 Feb 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/quote-super-quiet-non-understanding/ https://roundwallsoftware.com/blog/quote-super-quiet-non-understanding/ <blockquote> <p>If I had to quit a job over this, I would.</p> </blockquote> <p><a href="http://inessential.com/2012/02/09/super-quiet_non-understanding">http://inessential.com/2012/02/09/super-quiet_non-understanding</a></p> Quote: A Design Primer For Engineers Mon, 06 Feb 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/quote-a-design-primer-for-engineers/ https://roundwallsoftware.com/blog/quote-a-design-primer-for-engineers/ <blockquote> <p>Software had been around and making piles of money long before Netscape, but it became a worldwide phenomenal when anyone, anywhere could mail anyone else a picture of their cat.</p> </blockquote> <p><a href="http://www.randsinrepose.com/archives/2012/01/16/a_design_primer_for_engineers.html">http://www.randsinrepose.com/archives/2012/01/16/a_design_primer_for_engineers.html</a></p> Quote: 10 Tips For Agile Leaders Wed, 01 Feb 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/quote-10-tips-for-agile-leaders/ https://roundwallsoftware.com/blog/quote-10-tips-for-agile-leaders/ <blockquote> <p>"Failing I can deal with. I fail all the time. I write blog posts that dont resonate. I write software no one cares about. I regret how I handle certain situations and conversations. But I try. And as long as I know I did my best, Im good. Thats what lets me sleep at night.”</p> </blockquote> <p>—- Jonathan Rasmusson <a href="http://pragprog.com/magazines/2012-02/ten-tips-for-agile-leaders">link</a></p> File watching for fun and profit. Sun, 29 Jan 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/file-watching-for-fun-and-profit/ https://roundwallsoftware.com/blog/file-watching-for-fun-and-profit/ <p>Let’s say you want to write some javascript. You’re a good programmer, but you’re not so cocky that you feel infallible. Good news! Just because you are writing in an interpreted language, doesn’t mean you have to cross your fingers when you go to execute your script. <a href="http://www.jshint.com/">JSHint</a> is here to the rescue. Copy-pasting your code into the web page there would be annoying in about 10 seconds, but fortunately there are better options.</p> <p>JSHint is also available as an executable you can run from the command-line. Simply follow these steps and continue on to the javascript party:</p> <ol> <li>Install node.js: If you’re on OSX, it’s as easy as <code>sudo brew install node</code></li> <li>Install npm: <code>sudo brew install npm</code></li> <li>Install JSHint: <code>npm install jshint</code></li> <li>Run JSHint: <code>jshint SOME_JAVASCRIPT_FILE</code></li> </ol> <p>Now JSHint can show you potential errors and problems in our code! But that’s not quite good enough. You probably want this information as you work, not just when you think to run the program. This is going to take a little more effort.</p> <p>Now you <em>could</em> install some extra bundle/plugin/script into your editor of choice to have a hotkey for JSHint or automatically execute it when you save a file, but that’s less awesome. With a few more steps, it doesn’t matter which editor you use and you can also automatically check whenever you svn up or git pull or whatever as well. A few more steps to joy brought to you by <a href="https://github.com/guard/guard">Guard</a>:</p> <ol> <li>Install guard: OSX already has rubygems, so just <code>sudo gem install guard</code></li> <li>Add a guardfile to your project: <code>touch guardfile</code></li> <li>Run guard: <code>guard -c -n f</code> from inside our project</li> </ol> <p>Your guard file should look something like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span># Super simple Guardfile </span><span> </span><span>guard :shell do </span><span>watch(/.\*.js/) { `jshint *.js` } </span><span>end </span></code></pre> <p>Now you’ll see a screen showing the output of the last time JSHint was executed. Full documentation can be found on Guard’s website in case you want to do something a little more elaborate when your files change. Like so:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>profile-test.js: line 23, col 4, Expected an identifier and instead saw ‘export’ (a reserved word). </span></code></pre> <p>My version is probably some of the most basic applications Guard can be used for. Hopefully this helps you make something awesome with a little more ease and/or speed.</p> Tools Sat, 14 Jan 2012 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/tools/ https://roundwallsoftware.com/blog/tools/ <p>When I talk to people who are serious about their computer-related tools (which happens frequently since I write software), I often end up in discussions about how X tool is better than Y tool. These discussions are silly and overdone arguments such as Android is better than iPhone, OSX is better than Windows, Vim is better then Emacs, you get the idea. These discussions are mostly holy-wars or eternal feuds or whatever you want to call them and are silly. Neither side is going to change their mind or be enlightened when the discussion devolves this far.</p> <p>Many times the reason you make the statement X tool is better than Y is that you have trained yourself to make use of a set of features that make your work go smoother and faster and you do not now how to do those same things in the tool you claim is inferior. This tool might actually have the ability to do what you just learned, just maybe with different key commands or some additional setup required. Now your statement is invalid.</p> <p>Your statement might not also be provable or logical at all. When faced with the choice of a variety of similar tools, it is human nature to convince yourself that the choice you decide on is far superior to the other tools you could have chosen. This is not really logical, this is human nature. These debates are pointless because each side is not operating entirely on logic. More fruitful discussion about tools should ideally sound more like this:</p> <pre style="background-color:#2b303b;color:#c0c5ce;"><code><span>Them: Hey, check out this nifty feature my editor has to make work easier. </span><span>You: Oh cool, I wonder if my editor can do that. </span><span>Them: Pardon me while I go get work done faster with this new feature. </span></code></pre> <p>Maybe it can, maybe it cant. If it cant maybe <em>that</em> is when you decide that you need to switch and use the other persons editor. Not because they claimed their editor was superior, but because it did things that you wanted to be able to do. Maybe you find that your editor of choice <em>can</em> do those things and now the other person can decide if they want to use your editor of choice. Theres no real reason to change using any tool unless another tool is way more helpful or yours is broken somehow and needs replacing.</p> <p>As far as Im concerned, youre welcome to use whatever tool you want assuming it gets the job done, its not ruining your day, and I dont have to use it.</p> Alternative editors? Mon, 12 Dec 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/alternative-editors/ https://roundwallsoftware.com/blog/alternative-editors/ <p>I’m especially curious how anyone is able to use something other than Xcode for developing things with Objective-C. This is specifically an issue for Objective-C projects as opposed to Rails/Node.js/whatever-else-is-cool projects because those can be done in pretty much any editor.</p> <p>Current Xcode does not allow you to set another editor as the default for a given file-format, so it’s not nearly as convenient as it was in Xcode 3 to simply open files in your favorite editor. Isn’t it a hassle to jump between Xcode and your editor of choice? You would need to edit in one, but debug, refactor and analyze in another. This doesn’t slow down work and/or bother anyone? Xcode is more than just a text-editor, it’s a whole IDE. Everything it does beyond basic text editing is pretty fancy and I wonder how much effort it is worth to try to recreate any of that in another editor no matter how nifty that editor might be. Someone tell me I’m wrong and there’s actually really nice ways to build Objective-C projects in other editors (and not that Eclipse-looking IDE that popped up, not much interest there).</p> RSS Syncing and my attempt at a solution. Tue, 01 Nov 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/rss-syncing-and-my-attempt-at-a-solution/ https://roundwallsoftware.com/blog/rss-syncing-and-my-attempt-at-a-solution/ <p>Some time ago, I read <a href="http://inessential.com/2010/02/08/idea_for_alternative_rss_syncing_system">this article</a> by <a href="http://inessential.com/">Brent Simmons</a> about an alternative to the Google Reader syncing system pretty much everybody uses to sync RSS Readers together across your laptops, desktops, iPhones, iPads, whatever. Google Reader is nifty and all, but the problems Brent outlines are actually pretty annoying. I read a bunch of fairly-rare feeds that not many follow ( you probably haven’t heard of them ). These feeds don’t get updated very often by Google’s system and I sometimes end up missing updates by days or even a week. This is lame. A single point of failure for all of your feeds is pretty lame too.</p> <p>I attempted a <a href="https://github.com/sgoodwin/RSSS">solution</a> a little while ago, but sort of gave up for various reasons. Recently I had some ideas about how to make this attempt nicer and possibly something developers would maybe do something other than laugh at me for. So, without further adeiu, I give you <a href="https://github.com/sgoodwin/RSSSyncer">RssSyncer</a>!</p> <p>RSSSyncer was built with Sinatra and Redis (could be a redis cluster even). The goal is to allow for a simple system to setup syncing the read/unread/whateverstatus of news items in a timeline. The same system would work for things like Instagram and twitter as well as RSS. These are similar technologies to what <a href="http://www.manton.org/">Manton Reece</a> is using for his service, <a href="http://tweetmarker.net/">Tweetmarker</a>. While this isn’t quite as simple as putting some php onto your existing web-server, services like heroku (which host my version you can play with here: <a href="http://rsssyncer.heroku.com"></a><a href="http://rsssyncer.heroku.com">http://rsssyncer.heroku.com</a> make running simple ruby apps crazy simple. The setup process for hosting it yourself is fairly simple too, since this is just a Sinatra app with very few dependencies.</p> <p>RSSSyncer is my interpretation of what Brent called for in his article that inspired my efforts. The code itself is pretty simple, all of the request logic is one file, model manipulations happen in items.rb and subscription.rb with a few modules I created for the functionality the two models share. It saves only item status, not item content. It does not check your feeds for you, it leaves that to your actual feed-reading app. It stores item status in an 8-bit field so that the first bit can indicate read/unread status while the other bits can be used to indicate starred/not-starred status and still have bits for other things.</p> <p>To test features, I am also developing the world’s simplest RSS reader which is basically just a ruby script from the command line. <a href="https://github.com/sgoodwin/RSS-Repl">RSS-Repl</a> just gives me a way to test this out until one of my favorite developers decide to implement syncing with RSSSyncer into their existing apps.</p> <p>The only feature I haven’t figured out is how to handle user authentication. I figure I -could work out something like <a href="http://twitter.com/antirez">Salvatore Sanfilippo</a> used to authenticate users for <a href="http://lamernews.com">http://lamernews.com</a>. If anyone has a better/more-desired authentication scheme, I’m all ears! Feel free to email me, harass me on twitter, file issues on the github project page, whatever is convenient.</p> <p>Thanks for your time.</p> :( Thu, 06 Oct 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/sad/ https://roundwallsoftware.com/blog/sad/ New wider Venture 5.8 Sat, 01 Oct 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/new-wider-venture-5-8/ https://roundwallsoftware.com/blog/new-wider-venture-5-8/ <p>Shortly after DLX acquired Venture, Venture announced that they would be releasing new, wider trucks. Previously, Venture only made trucks that were 8 inches and 7.75 inches wide. This is all fine and dandy for all skaters who enjoy riding boards that are 7.5-8.0 inches wide or so, but those of us who enjoyed 8.38, 8.4, and 8.5 inch deck widths were forced to choose trucks from somebody else like Independent, Thunder, Tracker, etc. These new trucks Venture released are exactly what we bigger-board-riders have been waiting for.</p> <p>These new Ventures are the same width (comparing hangars and axels) as Independent 149s and Thunder 149s. The Venture 5.8s are just a little bit taller than my Thunder 149s and my Independent forged 149s. This new width makes for a nice choice if you like to ride decks between 8.25 and 8.75 inches roughly. I personally would ride these happily with decks in the range of 8.25 inches to 8.5 inches wide.</p> <p>This new truck rides better than its narrower brothers. Even with the stock bushings, they turn smoothly and effectively. They have a more progressive lean than Independents or Thunders. That is to say that, the further you tilt your deck to one side, the more resistance youll feel. Different bushings could affect this and change it if youre not a fan.</p> <p>You can see the collorways available <a href="http://venturetrucks.net/products/trucks-fall-11/" title="here">here</a> and start hassling your local shop for a pair today!</p> Special place for my team. (Taken with instagram) Wed, 03 Aug 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/special-place-for-my-team-taken-with-instagram/ https://roundwallsoftware.com/blog/special-place-for-my-team-taken-with-instagram/ <p>Special place for my team. (Taken with <a href="http://instagr.am">instagram</a>)</p> <p><img src="/blog/tumblr_lpdhcfz9il1qfcjuuo1_1280.jpg" alt="" /></p> XPC and how it might affect Mac App Architecture Fri, 29 Jul 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/xpc-and-how-it-might-affect-mac-app-architecture/ https://roundwallsoftware.com/blog/xpc-and-how-it-might-affect-mac-app-architecture/ <p>Just finished watching the developer video about XPC from Apple. This is actually pretty cool. For those of you who aren’t hip to the goods, XPC is an attempt at a simper-simple way for separate applications to talk to each other. This feature works well with Lion’s new Sandboxing ability which more strictly reduces the abilities of an application. Now Mac apps can follow the security model that my security-paranoid friends told me about where applications/users/whatever only get the bare minimum privaliges they need to accomplish their task. This allows for app-makers on the Mac to split up parts of their application into it’s own sort of mini-app to do simple, specific units of work that might be dangerous/unstable/etc. These individual chunks then only need the specific privaliges necessary to accomplish their task, so tasks like “decode this video file” can’t be exploited to read a user’s address book or phone home.</p> <p>For example:</p> <p>​1) Apple’s Preview.app uses XPC to separate actually parsing a PDF file into a separate little mini-app. That way, bad PDFs won’t cause <em>all</em> of Preview.app to crash and PDF’s attempting to exploit the PDF parser can’t ruin the rest of your app or the user’s computer.</p> <p>​2) Apple’s Quicktime.app uses XPC to decode video files with a mini-app that only has enough permissions to use the cpu to handle the data it gets fed. This way mean hackers won’t be able to trick Quicktime with bad video files and try to make Quicktime phone home with all of a user’s address book. Also bad files won’t cause the whole application to crash, yay.</p> <p>I tried to come up with my own uses for XPC and the first that came to mind was forText Editors: use xpc to crate a mini-app that parses text files. This way large text files won’t cause your application to crash, nor will they pinwheel your text editor for a while (cough cough Textmate).</p> <p>Feel free to contact me and tell me I’m an idiot if I understand this wrong. :)</p> My San Francisco Side Trip Thu, 09 Jun 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/my-san-francisco-side-trip/ https://roundwallsoftware.com/blog/my-san-francisco-side-trip/ <p>For those of you who don’t know, this week is <a href="http://apple.com/wwdc">WWDC</a> here in San Francisco. I have been super lucky the last few months and just happen to work with a company cool enough to send me out here to enjoy the fun. WWDC is pretty epic. I love every minute of it.</p> <p>While I’m here in SF, I had the awesome opportunity to meet <a href="http://twitter.com/jimt43">Jim Theibaud (@jimt43)</a>. He was kind enough to give me a tour of the DLX Headquarters where all the magic happens to create products for <a href="http://dlxsf.com">Anti-Hero, Real, Thunder, Spitfire, and Krooked</a> (skateboard products if you haven’t guessed). I couldn’t take pictures, but let me just tell you it was sweet! Right next to DLX HQ is the place where Thunder, Indy, and Venture trucks are actually manufactured. I watched the most popular skateboard trucks in the country being created, hot molten aluminium and all!</p> <p>Also saw a super sick clip of Alex Perelson doing something awesome. Not sure if I should spoil the surprise, but the video comes out in like a week.</p> Everything You Wanted To Know About Mud Client/Server Interaction But Were Afraid To Ask Thu, 02 Jun 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/everything-you-wanted-to-know-about-mud-clientserver-interaction-but-were-afraid-to-ask/ https://roundwallsoftware.com/blog/everything-you-wanted-to-know-about-mud-clientserver-interaction-but-were-afraid-to-ask/ <p><strong><a href="http://cryosphere.net/mud-protocol.html">Everything You Wanted To Know About Mud Client/Server Interaction But Were Afraid To Ask</a></strong></p> Telnet Server Commands For Dummies Wed, 01 Jun 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/telnet-server-commands-for-dummies/ https://roundwallsoftware.com/blog/telnet-server-commands-for-dummies/ <p>The <a href="http://en.wikipedia.org/wiki/Telnet">telnet protocol</a> is an entirely ascii-based method of transmitting data that was developed way back before I was born. Telnet lives in what they call the “application layer” of the internet protocol suite. It’s like SSH only without the S (the one for secure). To communicate in telnet land, servers and clients send information back and forth in ascii streams. Server commands are communicated with ascii sequences that aren’t for printing. These sequences start with a value of 255, followed by various values to reflect the command, then sometimes additional values to specify options for that command. You can find a nice table explaining what value means what command here on <a href="http://support.microsoft.com/kb/231866">Microsoft’s website</a>.</p> <p>To illustrate, here is the set of sequences that first arrive when you connect to the telnet server (in this case specifically a mud client) at <a href="http://Lusternia.com">Lusternia.com</a>:</p> <p><strong>255 251 25: IAC WILL (use) END-OF-RECORD,</strong> this means the server wants to use the sequence 255 25 to indicate the end of a chunk of data instead of relying on carriage returns and line feed (often written as \r and \n).</p> <p><strong>255 251 200: IAC WILL (use)ATCP,</strong> this is a mud-specific command to indicate that the server would like to use the <a href="http://www.ironrealms.com/rapture/manual/files/FeatATCP-txt.html">Achaea Telnet Client Protocol </a>.</p> <p><strong>255 251 201: IAC WILL (use)GMCP,</strong> this is another mud-specific command to indicate that the server would like to use the <a href="http://www.aardwolf.com/wiki/index.php/Clients/GMCP">Generic Mud Communication Protocol (aka ATCP2)</a>.</p> <p><strong>255 251 86: IAC WILL (use) MCCP2,</strong> this is yet another mud-specific command to indicate that the server would like to use the <a href="http://www.mudpedia.org/wiki/MCCP">Mud Client Compression Protocol v2</a></p> <p>So basically, the server just says “Oh hey, welcome to the server. I want to use the end-of-record feature and I would also like to use one of the following mud compression protocols.” These compression protocols are to help reduce bandwidth consumption (because text can get expensive of course) and to communicate things a regular telnet connection wouldn’t communicate such as the prompt (health, mana, etc).Then finally, after the block of text introducing the server, the server sends along one final command sequence:</p> <p><strong>255 249: IAC GO AHEAD,</strong> this is a fairly old command, not sure why this server is using it. The go-ahead command tells you, the client, that it’s ok to send data back. Go-ahead was developed for back in the day when connections were not full duplex and only one side of the connection could speak at a time.</p> <p>After receiving these commands from the server, the client will want to send back a few commands in response:</p> <p><strong>255 253 25: IAC DO (use) END-OF-Record,</strong> this reply tells the server that you (the client) agrees to make use of the end-of-record feature.</p> <p><strong>255 253: IAC DO (use) compression protocol,</strong> this reply tells the server that you (the client) agree whichever of the possible compression protocols listed by the server.</p> <p>From here, assuming the client knows how to deal with the mud-specific protocol, a user can go on with their evening and enjoy their game!</p> <p>More later after I get further along!</p> Things have changed Sat, 28 May 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/things-have-changed/ https://roundwallsoftware.com/blog/things-have-changed/ <p>The two people reading this have probably been wondering to themselves, why self, I do wonder why that splendid chap, Samuel, has neglected to write anything in quite so long? Well two people, your prayers have been answered.</p> <p>For those of you who just tuned in, heres what you missed:</p> <ul> <li>My name is Samuel Goodwin</li> <li>I write software and ride skateboards</li> <li>I was a programmer for hire and worked on various web and iPhone projects for money.</li> <li>I lived in Denver with a really cool roommate.</li> <li>I have a dog named Blynken</li> </ul> <p>Since my last blog post, I have decided to drastically change my life in many ways such as:</p> <ul> <li>I applied for actual jobs and now work as a full time engineer for Snap Interactive, specifically as a senior engineer for <a href="http://whoisnear.com">WhoIsNear?</a></li> <li>I moved to Manhattan and live by myself</li> </ul> <p>Things are decently interesting now. I have been here for a few weeks now and am starting to get used to life here. I have found all the important things such as:</p> <ul> <li>Where to get internet</li> <li>Where to get food</li> <li>Where to skate</li> <li>Where to get skate things</li> </ul> <p>Now I spend my weeks at work in a room packed (almost quite literally) with very smart people all working to build the best product they can build. I learn quite a bit every day and look forward to the next day every morning. Meanwhile, I have plots to accomplish a few things while I am here:</p> <ul> <li>Start contributing to open source projects</li> <li>Learn something new beyond programming and skateboards</li> <li>Get better at meeting people</li> <li>Write more</li> <li>Be a speaker at conferences</li> </ul> <p>So stay tuned for a new season, you will be glad you did! For those of you on twitter, I am @samuelgoodwin. If either of you are going to WWDC, drop me a line and I will see you there!</p> This is my dog Sat, 28 May 2011 00:00:00 +0000 Unknown https://roundwallsoftware.com/blog/this-is-my-dog/ https://roundwallsoftware.com/blog/this-is-my-dog/ <p>This is my dog</p> <p><img src="/blog/tumblr_llvqqvm1Js1qfcjuuo1_r1_1280.png" alt="" /></p>