<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[iOS in Over Engineering on Medium]]></title>
        <description><![CDATA[Latest stories tagged with iOS in Over Engineering on Medium]]></description>
        <link>https://medium.com/over-engineering/tagged/ios?source=rss----efddcdaa36a--ios</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>iOS in Over Engineering on Medium</title>
            <link>https://medium.com/over-engineering/tagged/ios?source=rss----efddcdaa36a--ios</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 24 Apr 2026 09:23:08 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/over-engineering/tagged/ios" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <item>
            <title><![CDATA[The curious case of FileManager’s 512 error on iOS]]></title>
            <link>https://medium.com/over-engineering/the-curious-case-of-filemanagers-512-error-on-ios-c89ad0a249f9?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/c89ad0a249f9</guid>
            <category><![CDATA[file-manager]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Daniel Galasko]]></dc:creator>
            <pubDate>Tue, 12 Jan 2021 11:23:41 GMT</pubDate>
            <atom:updated>2021-01-12T11:23:41.249Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7ofYgq0Zy5--MQiiVcQCdQ.jpeg" /><figcaption>A fellow developer, trying to debug a complex problem. Credit: <a href="https://unsplash.com/">Unsplash</a></figcaption></figure><p>For many years of having <a href="https://apps.apple.com/us/app/over-graphic-design-maker/id535811906">our App</a> in the App Store, a particular bug alluded us most mischievously. Various reports would come in that distilled down to a simple problem: Customers with plenty of space on their device could not make any changes to the file system. Some of these bugs manifested as follows:</p><ol><li>Unable to download new files.</li><li>Unable to upload new files.</li><li>Unable to duplicate a file.</li><li>Unable to save changes to an existing file.</li><li>Unable to delete files.</li></ol><p>When we received these reports, we were painfully unaware they were the same bug since they manifested in seemingly unrelated parts of the App. We captured device logs and searched the internet and still found ourselves unable to understand what was happening.</p><p>So, we filed a support ticket with Apple to get an engineer’s eyes on this.</p><p>After some productive back and forth and a user who could to help us that could replicate the issue, he led us down a path that helped us get the error in question. In this instance, we looked at why UIDocument would fail to save changes to an existing document. He instructued us to override the handleError API and log the outputs.</p><p>The following error message revealed itself:</p><pre>&quot;localizedDescription&quot;: &quot;The operation couldn’t be completed.&quot;,<br>&quot;domain&quot;:&quot;NSCocoaErrorDomain&quot;,<br>&quot;code&quot;:512</pre><p>Our helper at Apple pointed us to an <a href="https://developer.apple.com/forums/thread/128927?answerId=631839022#631839022">obscure post</a> in the forums that said the following:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ih4zY3aBPUqN-j1nmsO3Vg.png" /></figure><p>In a nutshell, there is an arbitrary limit set on items in the Caches and tmp directories, that limit is 1000. Meaning, for our longtime customers, when those folders got too big, everything stopped working 🤯. Furthermore, iOS was not automatically pruning these directories as one might expect.</p><p>Our first step was to ship a build that had a deeplink to clear Caches and tmp. We sent this to users that experienced the problem and voilà, it worked! 💃Taking this a step further, we decided to write a utility that pruned these folders on each app update. We watched our errors go down to zero in no time.</p><p>It is worth mentioning that some developers have reported that this seems to be a problem, mainly when creating entries in these folders where the filename is generated using UUID().uuidString. We definitely make use of this API but if it’s affecting you too, I hope this helped you on your journey.</p><h3>🧠 Moral of the story</h3><p>If you find your App failing to make any changes using FileManager, look at your error code and see if it matches 512 and you have a large Caches and tmp directory.</p><p>I hope this saves you the time it took us to get to the bottom of this so that you can get your customers back and running in no time.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c89ad0a249f9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/the-curious-case-of-filemanagers-512-error-on-ios-c89ad0a249f9">The curious case of FileManager’s 512 error on iOS</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Monitoring a folder for changes in iOS]]></title>
            <link>https://medium.com/over-engineering/monitoring-a-folder-for-changes-in-ios-dc3f8614f902?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/dc3f8614f902</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[directory]]></category>
            <category><![CDATA[folders]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[software-development]]></category>
            <dc:creator><![CDATA[Daniel Galasko]]></dc:creator>
            <pubDate>Tue, 04 Feb 2020 17:55:12 GMT</pubDate>
            <atom:updated>2020-02-27T15:11:27.316Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*5CUIBt2UzrWlbf-v02utjA.png" /><figcaption>Photo by <a href="https://unsplash.com/@imandrewpons?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Andrew Pons</a> on <a href="https://unsplash.com/s/photos/folder?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><h3>Detecting changes to a folder in iOS using Swift</h3><blockquote>In this post, you will learn how you can observe a specific folder for new/deleted/updated files. Some of this code is adapted from Apple sample code that is no longer available online. The final result will look like the GIF below:</blockquote><figure><img alt="" src="https://cdn-images-1.medium.com/max/240/0*O0wpJb9cXfq0a8Xo.gif" /></figure><h3>🌄 Background</h3><p>Most of us are familiar with <a href="https://developer.apple.com/documentation/foundation/filemanager">FileManager</a> and <a href="https://developer.apple.com/documentation/foundation/filemanager/1413768-contentsofdirectory">contentsOfDirectory</a>. In most cases, that is good enough when peering into the contents of a folder. Sometimes though, we come across situations where we want to know precisely when the contents of a directory/folder have changed.</p><p>At <a href="https://www.madewithover.com/">Over</a>, we allow users to create project documents that live inside a folder. As the user makes edits to a project, we need to update its thumbnail and, in the case of a new project, add it to the project feed. The coordination required to manage this manually could be highly error-prone. Since cache invalidation is one of the <a href="https://quotesondesign.com/phil-karlton/">hardest programming challenges</a>, we need a solution that gives us the closest source of truth.</p><p>We could use <a href="https://developer.apple.com/documentation/foundation/timer">Timer</a> to monitor a folder by polling for changes but, that would incur a lot of unnecessary file system reads and overall feels wasteful.</p><p>Enter DispatchSourceFileSystemObject. This nifty GCD object monitors a given file descriptor for events. One can consult FileSystemEvent for the list of events we can monitor, but in this instance, we care about FileSystemEvent.write.</p><h3>🏃‍♀️Getting Started</h3><p>We begin with a demo project that displays files in a folder. We will have functionality to add and remove a file.</p><p>Our first step is to start a new SwiftUI project, and in the ContentView, add support for showing a list of files.</p><pre>struct ContentView: View {<br>    <a href="http://twitter.com/ObservedObject">@ObservedObject</a> var folder = Folder()<br>    <br>    var body: some View {<br>        NavigationView {<br>            List(folder.files) { file in<br>                Text(file.lastPathComponent)<br>            }<br>            .navigationBarTitle(&quot;Folder Monitor&quot;)<br>            .navigationBarItems(leading: createFileItem, trailing: deleteFileItem)<br>        }<br>    }<br>}</pre><p>Above, we have the definition of a view that will show a list of files and update the list on any changes. We can create and delete files and expect the UI to be updated. Ignore what Folder does, for now, that will be explained later. Just know it returns a list of files and publishes changes whenever that list changes.</p><h3>📹 Monitoring a Folder</h3><p>When monitoring a folder, two things are essential:</p><ol><li>A file descriptor pointing at the folder we want to observe.</li><li>A DispatchSourceFileSystemObject that will use the descriptor to report changes. We can construct one using the DispatchSource.makeFileSystemObjectSource API.</li></ol><p>Using them together would look like:</p><pre>let folderToObserve: URL = //a folder somewhere on device<br>let descriptor = open(folder.path, O_EVTONLY)<br>let folderObserver = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredDirectoryFileDescriptor, eventMask: .write, queue: someQueue)<br>directoryMonitorSource?.setEventHandler { in<br>    print(&quot;Something changed in our folder&quot;)<br>}</pre><p>In the above, we use eventMask: .write so that we only get events for file writes to that folder. This mask will give us updates that include new files, deletions, and updates (ie, an existing file has been updated).</p><p>When it comes to GCD APIs like this, it’s good practice to add a higher-level abstraction. This enables an API that is easier to consume and more customizable for your purposes.</p><p>Our abstraction will be called FolderMonitor. Before I share the code, there are some essential concepts to consider when using folder observers. Namely, we need to manage the lifetime of our objects so that we do not keep monitoring files when we no longer need to. We will cater for this by adding functionality to release the folder monitor as required.</p><p>With that in mind, this is what our monitor API should look like 👇</p><pre>class FolderMonitor {<br>    let url: URL<br>    var folderDidChange: (() -&gt; Void)?<br>    init(url: URL)<br>    func startMonitoring()<br>    func stopMonitoring()<br>}</pre><p>A simple enough API to consume and understand. Now, let’s expand upon it.</p><p>First, we need references to the two essentials mentioned earlier:</p><pre>class FolderMonitor {    <br>    /// A file descriptor for the monitored directory.<br>    private var monitoredFolderFileDescriptor: CInt = -1</pre><pre>    /// A dispatch source to monitor a file descriptor created from <br>        the directory.<br>    private var folderMonitorSource: DispatchSourceFileSystemObject?</pre><pre>    /// A dispatch queue used for sending file changes in the <br>        directory.<br>    private let folderMonitorQueue = DispatchQueue(label: &quot;FolderMonitorQueue&quot;, attributes: .concurrent)</pre><pre>    let url: URL<br>    var folderDidChange: (() -&gt; Void)?<br>    init(url: URL)<br>    func startMonitoring()<br>    func stopMonitoring()<br>}</pre><p>Now we can implement our startMonitoring function:</p><pre>func startMonitoring() {<br>    guard folderMonitorSource == nil &amp;&amp; monitoredFolderFileDescriptor == -1 else {<br>        return<br>    }<br>    // Open the folder referenced by URL for monitoring only.<br>    monitoredFolderFileDescriptor = open(url.path, O_EVTONLY)<br>    <br>    // Define a dispatch source monitoring the folder for additions, deletions, and renamings.<br>    folderMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredDirectoryFileDescriptor, eventMask: .write, queue: directoryMonitorQueue)<br>    <br>    // Define the block to call when a file change is detected.<br>    folderMonitorSource?.setEventHandler { [weak self] in<br>        self?.folderDidChange?()<br>    }<br>    <br>    // Define a cancel handler to ensure the directory is closed when the source is cancelled.<br>    folderMonitorSource?.setCancelHandler { [weak self] in<br>        guard let self = self else { return }<br>        close(self.monitoredFolderFileDescriptor)<br>        self.monitoredFolderFileDescriptor = -1<br>        self.folderMonitorSource = nil<br>    }<br>    <br>    // Start monitoring the directory via the source.<br>    folderMonitorSource?.resume()<br>}</pre><p>Above, you can see the usage of the monitor source to handle both file changes and also cancellation. Isn’t it neat that it will send events for any file that is updated inside the folder!</p><p>Closing the loop is easy, to stop observing folder changes:</p><pre>func stopMonitoring() {<br>    folderMonitorSource?.cancel()<br>}</pre><h3>👟 Tying it up to our UI</h3><p>At this point, we have a capable folder observer. To prepare it for SwiftUI, we can wrap it into a UI-friendly ObservableObject. In this example, all I am doing is observing the files in the Temporary Directory:</p><pre>class Folder: ObservableObject {<br>    <a href="http://twitter.com/Published">@Published</a> var files: [URL] = []<br>    <br>    let url = URL(fileURLWithPath: NSTemporaryDirectory())<br>    private lazy var folderMonitor = FolderMonitor(url: self.url)<br>    <br>    init() {<br>        folderMonitor.folderDidChange = { [weak self] in<br>            self?.handleChanges()<br>        }<br>        folderMonitor.startMonitoring()<br>        self.handleChanges()<br>    }<br>    <br>    func handleChanges() {<br>        let files = (try? FileManager.default.contentsOfDirectory(at: url, includingPropertiesForKeys: nil, options: .producesRelativePathURLs)) ?? []<br>        DispatchQueue.main.async {<br>            self.files = files<br>        }<br>    }<br>}</pre><p>This gives us an observable that uses a directory monitor and publishes the full list of URLs whenever they change.</p><p>Since our UI will be displaying these URLs in a list, we need to make them Identifiable.</p><pre>extension URL: Identifiable {<br>    public var id: String { return lastPathComponent }<br>}</pre><p><em>Note to reader: Be careful when extending foundation types like this as it can have unintended consequences in a large application. I am doing this only for illustrative purposes.</em></p><p>Finally, we can finish our UI Demo:</p><pre>struct ContentView: View {<br>    <a href="http://twitter.com/ObservedObject">@ObservedObject</a> var folder = Folder()<br>    <br>    var body: some View {<br>        NavigationView {<br>            List(folder.files) { file in<br>                Text(file.lastPathComponent)<br>            }<br>            .navigationBarTitle(&quot;Folder Monitor&quot;)<br>            .navigationBarItems(leading: rightNavItem)<br>            .navigationBarItems(leading: rightNavItem, trailing: deleteFileNavItem)<br>        }<br>    }<br>    <br>    var deleteFileNavItem: some View {<br>        Group {<br>            if folder.files.count &gt; 0 {<br>                Button(action: deleteFile) {<br>                    Image(systemName: &quot;trash&quot;)<br>                }<br>            }<br>        }   <br>    }<br>    <br>    var rightNavItem: some View {<br>        Button(action: createFile) {<br>            Image(systemName: &quot;plus.square&quot;)<br>        }<br>    }<br>    <br>    func createFile() {<br>        let file = UUID().uuidString<br>        try? file.write(to: folder.url.appendingPathComponent(file), atomically: true, encoding: .utf8)<br>    }<br>    <br>    func deleteFile() {<br>        try? FileManager.default.removeItem(at: folderObservable.files.first!)<br>    }<br>}</pre><h3>🙇‍♀️ Conclusion</h3><p>You will notice there is still a lot of room in FolderMonitor for expansion. We could design it so that it returns a <a href="https://en.wikipedia.org/wiki/Diff">diff </a>of the files inside a folder. We could even have it report changes to specific files with a specific extension. Please, feel free to take your monitor on its own journey. The purpose of this post was to shine some light on often overlooked APIs.</p><p>Thank you for taking the time to read this. Hopefully it made a difference to your iOS journey this day 💖.</p><p><em>Want to hear more? Be sure to follow us on Medium and Twitter </em><a href="https://twitter.com/EngineeringOver"><em>@</em><strong><em>EngineeringOver</em></strong></a></p><h3>💼 Full Code</h3><p>The full code for our FolderMonitor looks as follows:</p><pre>class FolderMonitor {<br>    // MARK: Properties<br>    <br>    /// A file descriptor for the monitored directory.<br>    private var monitoredFolderFileDescriptor: CInt = -1</pre><pre>/// A dispatch queue used for sending file changes in the directory.<br>    private let folderMonitorQueue = DispatchQueue(label: &quot;FolderMonitorQueue&quot;, attributes: .concurrent)</pre><pre>/// A dispatch source to monitor a file descriptor created from the directory.<br>    private var folderMonitorSource: DispatchSourceFileSystemObject?</pre><pre>/// URL for the directory being monitored.<br>    let url: Foundation.URL<br>    <br>    var folderDidChange: (() -&gt; Void)?</pre><pre>// MARK: Initializers</pre><pre>init(url: Foundation.URL) {<br>        self.url = url<br>    }</pre><pre>// MARK: Monitoring</pre><pre>/// Listen for changes to the directory (if we are not already).<br>    func startMonitoring() {<br>        guard folderMonitorSource == nil &amp;&amp; monitoredFolderFileDescriptor == -1 else {<br>            return<br>            <br>        }<br>            // Open the directory referenced by URL for monitoring only.<br>            monitoredFolderFileDescriptor = open(url.path, O_EVTONLY)</pre><pre>// Define a dispatch source monitoring the directory for additions, deletions, and renamings.<br>            folderMonitorSource = DispatchSource.makeFileSystemObjectSource(fileDescriptor: monitoredFolderFileDescriptor, eventMask: .write, queue: folderMonitorQueue)</pre><pre>// Define the block to call when a file change is detected.<br>            folderMonitorSource?.setEventHandler { [weak self] in<br>                self?.folderDidChange?()<br>            }</pre><pre>// Define a cancel handler to ensure the directory is closed when the source is cancelled.<br>            folderMonitorSource?.setCancelHandler { [weak self] in<br>                guard let strongSelf = self else { return }<br>                close(strongSelf.monitoredFolderFileDescriptor)</pre><pre>strongSelf.monitoredFolderFileDescriptor = -1</pre><pre>strongSelf.folderMonitorSource = nil<br>            }</pre><pre>// Start monitoring the directory via the source.<br>            folderMonitorSource?.resume()<br>    }</pre><pre>/// Stop listening for changes to the directory, if the source has been created.<br>    func stopMonitoring() {<br>        folderMonitorSource?.cancel()<br>    }<br>}</pre><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=dc3f8614f902" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/monitoring-a-folder-for-changes-in-ios-dc3f8614f902">Monitoring a folder for changes in iOS</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How Our iOS Team Streamlined Our Processes Through Continuous Integration]]></title>
            <link>https://medium.com/over-engineering/how-our-ios-team-streamlined-our-processes-through-continuous-integration-f46b9bb5f70f?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/f46b9bb5f70f</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[gitflow]]></category>
            <category><![CDATA[github]]></category>
            <category><![CDATA[continuous-integration]]></category>
            <category><![CDATA[git]]></category>
            <dc:creator><![CDATA[Naudé Cruywagen]]></dc:creator>
            <pubDate>Tue, 14 May 2019 07:49:30 GMT</pubDate>
            <atom:updated>2019-05-14T07:49:30.027Z</atom:updated>
            <content:encoded><![CDATA[<p><em>Delivering code is how developers present their hard work to the world and this makes it, in my opinion, one of the most important parts of our job. However, knowing when code will be ready to deliver can be hard. That’s why our team started moving towards Continuous Integration (CI), a process that allows us to upload our work to the main development branch on the go. Here’s how we started using CI, what we’re currently doing with it and where we plan to go next.</em></p><p><em>Originally posted </em><a href="https://www.offerzen.com/blog/how-we-streamlined-our-processes-through-continuous-integration"><em>here</em></a><em>.</em></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hW9EjZENYp2CtsTz6YjJ2w.jpeg" /></figure><p>I joined the iOS engineering team at Over early in 2018. If you’re not familiar with <a href="https://www.madewithover.com">Over</a>, it is a photo and video editing app that gives you the power to create visuals that will help you and your business succeed. Our iOS project’s setup is pretty much what you can expect from an iOS app: a mix of Objective-C and Swift (all new code is written in Swift, obviously), a single code repository on GitHub, buddybuild for automated builds and deployments, and a hint of fastlane for some more automation.</p><p>When I joined the team, we were using a process called GitFlow to isolate features and release changes to the App Store. While we are not a particularly big team, even eight people trying to coordinate changes in the codebase using GitFlow was daunting. We found ourselves struggling to get releases out, because we were dealing with plenty of merge conflicts and semantic code conflicts right at the end of feature work, when we were supposed to release. Fixing these conflicts resulted in a lot of extra debugging, bug fixing and testing. This made it difficult to gauge when we would be able to release and it also decreased our confidence in releases.</p><p>Whilst my first priority is to write code, I have a natural tendency to try and improve all manner of processes and GitFlow raised a red flag for me. In my previous role, the team had also worked with GitFlow before scrapping it in favour of CI. This experience made it easy for me to identify the bottlenecks that GitFlow was creating for us and how CI could help us eliminate them. I explained to our team at Over how CI avoids long lived feature branches (which are a natural product of GitFlow) and instead merges each developer’s work onto the main code branch as soon as possible. We agreed to try it out and are now busy moving towards CI.</p><p>This has improved our communication during code reviews and given the team more confidence in telling product managers when a feature will be ready. It has also allowed us to ship more features, more often, leading to better user experiences and ultimately happier users. Here’s how we have been doing it.</p><h3>🥴 Our old process and the headaches it caused us</h3><p>GitFlow had been the preferred method for delivery for a while before I joined Over. On the surface, GitFlow is inviting because it allows developers to do their own work without worrying about it getting tangled up with other developers’ work.</p><p>When using GitFlow, a developer’s code is kept separate from the main branch, in a feature branch, until just before it needs to be released. At this point, a release branch is created from the main branch and the feature branch is merged into this release branch. The release branch is then deployed to production. All of this seems fine until more than one developer wants to release and get their work into the shared release branch. This tends to result in last minute panic.</p><p>The ‘independent work’ benefits of GitFlow seemed alluring but it was letting us down by hiding file level and semantic conflicts until the different long lived feature branches were merged into the release branch. These issues could be hidden for weeks or even months which led to an almost impossible task of accurately being able to determine when we’d be able to ship to production.</p><h4>🛠 What we did to solve this</h4><p>While facing similar release issues at my previous job, I was introduced to CI by a software engineering coach. He pointed me to this <a href="https://martinfowler.com/articles/continuousIntegration.html">blog post</a> by Martin Fowler, which gives a great overview of the technique and how to use it. He also does an excellent job at describing how feature branches fit in with CI and why it is bad getting sucked into long lived feature branches (as used in GitFlow) in his article called <a href="https://martinfowler.com/bliki/FeatureBranch.html">FeatureBranch</a>. This was something we needed to do urgently, and so, having experienced the benefits of CI before, I pitched the idea of trying it out during one of our informal lunch-and-learn sessions. (A lunch-and-learn is merely a chat we have, about cool stuff we know, while we have lunch 😁) The team was keen and so, we decided to try out CI for our next big release.</p><h3>🚒 CI to the rescue: What it is and how it works</h3><blockquote>It is important to note that CI is a practice, not a tool.</blockquote><p>When using CI, a developer’s code is integrated into the main branch as often as daily (sometimes even as often as every commit). The main branch is kept in a releasable or stable state and can therefore be deployed to production at any time. This is a far cry from the normal rush we experienced to integrate everyone’s work and get a release out amidst major conflicts.</p><p><strong>There are many tools that facilitate CI </strong>(in our case it’s buddybuild)<strong>, but no tool can <em>give</em> you CI since it is a practice that developers in a team need to adopt.</strong></p><p>In its purest form, CI works like this:</p><ol><li>You check out the latest version of the code from the main development branch onto your local machine.</li><li>Using your local copy, you make all the changes you require, including changes to the production code as well as automated tests.</li><li>After making changes, you run a build — i.e. you run the app and run the automated tests.</li><li>If the build and tests pass, you commit the changes.</li><li>You repeat steps 2 to 4 until all the required work is completed.</li><li>Next, you pull any newer changes from the main branch, since other people have most likely added code.</li><li>You rebuild and fix any compilation issues or failing tests.</li><li>You push your local changes to the shared code repository.</li><li>Finally, you run a build (and tests) on an integration machine, creating a releasable app.</li></ol><p>If the build on the integration machine passes, you have new version of your app ready to be released.</p><blockquote>Keeping the changes small and integrating often in this way ensures stable production code with few bugs.</blockquote><p>Of course build failures do occur, but since everyone stays up to date with the main branch frequently, such failures never last long.</p><h4>📋 How we applied CI to our project</h4><p>Keeping the ‘pure’ CI scenario in mind, our team at Over needed to make some adaptations for it to suit us. Being a fully remote, distributed team, often working asynchronously, we rely on pull requests to get our code into our main branch. This provides a level of visibility that is very important for a team that is not always in the same physical space.</p><p>What is a pull request, you might ask? It is a way for developers to request that their code be pulled into a target branch. A pull request shows you all the changes between your branch and the target branch, and that’s why it is often used for code reviews.</p><p>In order to accommodate pull requests, we needed to adapt the list above by including a pull request before local changes are pulled into the main branch — i.e. before step 8 above. This is what our process currently looks like:</p><ol><li>After checking out the latest code from the main branch, we create a feature branch. The goal is for these feature branches to be short lived — a day or two.</li><li>We make our changes on these local, feature branches.</li><li>After making changes we run the app and any associated tests (manual or automated).</li><li>We commit the working changes.</li><li>We repeat steps 2 to 4 until all the required work is completed.</li><li>We pull any newer changes from the main branch (since other people have most likely added code).</li><li>We rebuild and fix any compilation issues or failing tests.</li><li>When we’re ready, we open a pull request to merge our changes into the main branch.</li><li>An opened pull request results in an automated build and test cycle handled by buddybuild.</li><li>Once a pull request is merged, buddybuild kicks off another build and test cycle and then deploys our app.</li></ol><p>Getting feedback about the build status on a pull request allows us to fix any broken builds immediately. You might wonder what we do with features that aren’t due to ship soon: Those get the same treatment described above. However, this is where feature flags play a crucial role. Feature flags allow us to have all our code in the same codebase, even if some code is not yet available for general consumption. We even gave our implementation of feature flags its own blog post, which you can read <a href="https://medium.com/over-engineering/building-a-lightweight-feature-flagging-system-51a8a59845ce">here</a>.</p><h3>📈 The results we saw</h3><p>Less file level conflicts? Yes ✅<br>Less semantic code conflicts? Yes ✅<br>Less of a mad rush to get releases shipped? Yes ✅<br>More confidence in shipping our features? Yes ✅<br>More confidence in estimating when a feature will be shipped? Yes ✅</p><p>The above results were promised by CI and it delivered 🥁. Pun intended. We did, however, see two more results that hadn’t necessarily been expected: <br>• More meticulous code reviews 🧐 and<br>• More thorough testing 🛠️</p><p>Since the new system ensures that all our pull requests are being merged into our main development branch, there are no more chances to try and fix things later. Once a pull request is merged we deem the code ready to be shipped. This has resulted in a greater sense of accountability and clearer communication within our team.</p><p>Using CI has opened our eyes to what an effective process can do to help teams communicate more effectively, work productively and be accountable at an individual level. If you’re keen to learn more about CI, check out this <a href="https://www.continuousdeliveryconsulting.com/blog/organisation-pattern-trunk-based-development/">article</a> I recently read to hear the reason Dave Farley and Jez Humble declared “<a href="http://www.amazon.co.uk/dp/0321601912">we can’t emphasise enough how important this practice is in enabling continuous delivery of valuable, working software</a>“.</p><p>Thanks for reading and we hope you enjoyed this article 🤓. We would love to hear your thoughts so leave some feedback down below and be sure to stay up to date by following us on Medium and Twitter <a href="https://twitter.com/EngineeringOver">@<strong>EngineeringOver</strong></a></p><p>Also, <a href="https://www.madewithover.com/careers"><em>We’re hiring! </em>😉 so come work-at-over</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f46b9bb5f70f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/how-our-ios-team-streamlined-our-processes-through-continuous-integration-f46b9bb5f70f">How Our iOS Team Streamlined Our Processes Through Continuous Integration</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS @ Over: 3 months into the iOS Platform Team]]></title>
            <link>https://medium.com/over-engineering/ios-over-3-months-into-the-ios-platform-team-96aff7ab9cdf?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/96aff7ab9cdf</guid>
            <category><![CDATA[platform]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[process-improvement]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Jamie Raad]]></dc:creator>
            <pubDate>Tue, 23 Apr 2019 15:59:40 GMT</pubDate>
            <atom:updated>2019-04-28T21:59:38.135Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*ecvgPL1uQkJ1LTxjTEpMtw.png" /><figcaption>The platform team at work</figcaption></figure><p>A while ago we wrote about starting an iOS platform team at Over (<a href="https://medium.com/over-engineering/ios-over-starting-a-platform-team-df8b1dcb30d5">read it here</a>). From the start, we decided to tackle various tasks that the iOS team doesn’t always have time to look at. We thought, now would be a perfect time to look back on the success of the platform team and what we have managed to achieve thus far.</p><p>The Platform team’s primary purpose is to facilitate the success of the entire iOS team at Over. This ranges from tasks like handling urgent bugs, assisting other squads with platform changes needed, and cleaning up technical debt. Another responsibility we take on is to ensure that non-critical company requests from outside of engineering also get attention.</p><p>These are some of the bigger tasks we completed.</p><h3>🧹 Code Cleanup</h3><p>During the initial planning for the Platform Team, we identified code cleanup as the biggest need in the iOS Team. 2018 was a busy year for us. It was also marked by a period of growth hacking where we shipped a lot of experimental features. We also had to sunset our Circles tab which, code-wise, was a major feature.</p><p>We started off by removing the Circles feature. The majority of the classes were now unused and simply had to be deleted. The end result was about <strong>10 016</strong> lines of code being removed across <strong>80+</strong> files 🤯.</p><p>Next up were the experimental features. We shipped around 17 of these in 2018. They where all part of our internal ABTesting service and most of them were built over several one week sprints. Many of these features were discontinued by turning off the feature flags but the code still existed in our codebase. They all varied from a few lines to several thousand lines of code. We did our best to abstract these as much as possible, but some still found their way into parts of our codebase we weren’t comfortable with them being in.</p><p>Our biggest concern with these features is that no single person in the team had the full context over all of them. Things started to go wrong when our users began reporting bugs in production on features that were part of these ABTests, but, should have been, essentially “turned off”. 🙈</p><p>The first step was identifying each experimental feature and then followed by documenting the context of its impact on the codebase. Once that was done, we started tackling them one at a time to ensure our Pull Request’s (PR’s) were manageable for the rest of the team to review. The end result was 15 PR’s removing a total of 7,474 lines code.</p><blockquote><strong>All in all, we removed around 17,500 lines of unused code </strong>🎉</blockquote><h3>🚑 Continuous Integration Backup Plan</h3><p>We use <a href="https://www.buddybuild.com/">Buddybuild</a> for continuous integration as well as automating our App Store submissions. This ensures everyone in the company has access to the very latest snapshot of our codebase in an Adhoc &amp; TestFlight build.</p><p>To simplify our environment, our team members Mac’s aren’t configured to build &amp; sign for the App Store. This becomes a problem when the CI server goes down. While this almost never happens, it has before, and on an important release day 😒.</p><p>What we wanted was something that was quick &amp; easy to set up, with clear documentation of how to use it.</p><p>We had 2 conditions we wanted to achieve.</p><ol><li>It must be quick to set up on any existing dev machine. (ie. no 3rd Party software to install)</li><li>It shouldn’t require any changes to the Xcode project.</li></ol><p>What we ended up with was a build script able to build the Production and Beta IPA, our private key stored in an accessible but secure location and a wiki article explaining how to configure and run it. All in all, it probably takes about 10 minutes to set up.</p><p>The build script makes use of the exportOptionsPlist option introduced in Xcode 9. This lets us manually configure the build independently from what is configured in Xcode. Since we also have multiple Notification extensions, it also lets us define different Provisioning Profiles per bundle identifier.</p><h3>🐛 Bug &amp; Crash Reporting</h3><p>For years the suite of services provided by <a href="https://fabric.io">Fabric</a> have been our crash and bug reporting tools of choice. However, Fabric is being shut down very soon. This created an opportunity for us to evaluate other solutions out there for our bug and crash reporting. Our team isn’t sold on the idea of moving to Firebase. A big reason being that Crashlytics will require an extra 8 Google/Firebase libraries to be installed with it. <strong>That’s a lot</strong>.</p><p>After evaluating a few different options, we have now moved to <a href="https://instabug.com">Instabug</a>. It’s still early days, and while it doesn’t give us as good of an overall state of the app that Crashlytics did, it’s going well. We are now also using Instabug for bug reporting and feedback on our internal builds, replacing Buddybuilds feedback feature.</p><h3>💾 Removing Realm</h3><p>A few years ago Realm was implemented in Over with different requirements in mind. Those requirements changed and we where left having to support a 3rd Party framework that we don’t need anymore. 3rd Party Frameworks can become a problem around the time of having to upgrade Xcode and Swift. When Xcode 10.2 and Swift 5 came out, we found ourselves in a bit of a pickle. We couldn&#39;t immediately upgrade because of an issue with Realm and we had to wait for them to resolve it. This pushed us to reevaluate the urgency of removing Realm from our codebase.</p><p>We had 2 features in Over backed by Realm. Neither of them required any complex querying but did make use of Realms object notifications. Since we don’t deal with 1000’s of database entries, it was clear we could replace Realm with a simple caching mechanism.</p><p>This meant updating all our older models to conform to Codable. We effectively replaced thousands of lines of code in Realm with a very simple caching class that is able to insert, update, delete and retrieve the objects. This new class is backed by <a href="https://github.com/pinterest/PINCache">PINCache</a>. As of today, we no longer have Realm as a dependency. This also had a nice side effect of reducing our build times 😃.</p><h3>🔨 What Else?</h3><p>Besides these bigger planned tasks, we did manage to help out other departments with various requests and fix some nasty bugs like our Google users getting logged out on every app launch.</p><h3>🚀 What’s Next?</h3><p>There will always be more code-cleanup to do, but as we move forward this will have a lower priority now that the biggest culprits are gone. We are now focusing on improving key areas of our codebase to better help the rest of the iOS team.</p><p>We’re also looking into where the Platform team can help in Customer Success to ensure issues reported by our users go through the correct flow of being assigned &amp; actioned by Engineering.</p><p>As the responsibilities of the Platform team grows, we may also look at expanding the team later this year.</p><p>Thanks for reading and we hope you enjoyed this article 🤓. We would love to hear your thoughts so leave some feedback down below and be sure to stay up to date by following us on Medium and Twitter <a href="https://twitter.com/EngineeringOver">@<strong>EngineeringOver</strong></a></p><p>Also, <a href="https://www.madewithover.com/careers"><em>We’re hiring! </em>😉 so come work-at-over</a>!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=96aff7ab9cdf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/ios-over-3-months-into-the-ios-platform-team-96aff7ab9cdf">iOS @ Over: 3 months into the iOS Platform Team</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[iOS @ Over: Starting a Platform Team]]></title>
            <link>https://medium.com/over-engineering/ios-over-starting-a-platform-team-df8b1dcb30d5?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/df8b1dcb30d5</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[xcode]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[build-time]]></category>
            <category><![CDATA[process-improvement]]></category>
            <dc:creator><![CDATA[Michael Dube]]></dc:creator>
            <pubDate>Thu, 28 Feb 2019 14:11:02 GMT</pubDate>
            <atom:updated>2019-02-28T14:11:02.348Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*x2YWBe7W-ojntzApud6TJw.jpeg" /><figcaption>Photo by <a href="https://unsplash.com/photos/LvOGj1X2jHE?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">boostinjay</a> on <a href="https://unsplash.com/search/photos/industrial?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText">Unsplash</a></figcaption></figure><p>The challenge that all scaling teams face is in dealing with the growing pile of technical debt and performance problems. With a few people in a team, it is easier to coordinate on refactoring and knowledge sharing. With a larger team, collaboration and communication become increasingly complex. Once those teams start moving faster, it becomes even harder to slow down and deal with that growing pile of tech debt.</p><p>In this post, we will explain what happened within our team at Over and what led to the creation of the iOS Platform Team 🚀</p><h3>🏚 The cracks of a scaling team</h3><p>As our team scaled, so too did our codebase. We develop primarily in Swift and this has a massive impact on our overall build times (hopefully this improves this WWDC 🤞). It would take around 20 minutes for our build server on <a href="http://www.buddybuild.com">Buddybuild</a> to build and run all our tests for a Pull Request. A clean build of Over can take up to 3 whole minutes. These times add up each day and it makes things worse when we started noticing how we would switch to Slack, or surf the web while we waited. All that context switching while waiting for builds really hurt our productivity.</p><p>Another pain point we experienced was a growing pile of technical debt. One such example is when we replaced our Circles feature with Teams. This was quite a large refactor that was also implemented as a <a href="https://medium.com/over-engineering/building-a-lightweight-feature-flagging-system-51a8a59845ce">feature flag</a>. This meant that both Circles and Teams were coexisting. Once we finally shipped, the team continued moving on to support Teams and couldn’t really justify the time it would take to delete the old code. After all, shipping meaningful features to our users is our top priority.</p><p>The above issues are just the tip of the proverbial iceberg ❄️. Once we took a step back, it was evident that we have a need for a dedicated team that is free from product work and can focus on helping our iOS team be more successful. We identified an individual on our team, <a href="https://medium.com/u/a2c505df45af">Jamie Raad</a>, that was willing to dive into creating the Platform Team and defining the workload and prioritisation effort. Let’s see how that went down… 😃</p><h3>✍️ Defining our platform team</h3><p>The platform team exists to ensure the continued success of user-facing feature teams. As the platform team is not tied to feature work and product deadlines, it is free to focus on broader challenges that inhibit the team from either shipping more smoothly or working productively.</p><p>Here was a list of tasks we prioritized as a team that we should start with:</p><ol><li>Improve build times (locally and on Buddybuild).</li><li>Investigate new crash reporting tools.</li><li>Remove Realm from the App.</li><li>Ad-hoc requests from various departments.</li></ol><p>The platform team is also responsible for listening to the technical needs of the team and working on new and improved APIs to assist with software delivery. Having a dedicated team also means we have the resources to investigate things such as obscure bugs that are hard to reproduce, image preloading mechanisms and approaches to reusable generic feeds. These are just a few off-the-cuff examples of areas in which the platform team can improve the way we work.</p><h3>👀 The first set of results</h3><p>With the platform team set up and ready to go we embarked on our first challenge: <strong>Build times</strong>. The goal here was to try and determine which of our frameworks took the longest to build so we can figure out exactly where to optimize. Ultimately, being able to reduce build times by at least 50% would be a huge accomplishment.</p><p>To get the results we followed <a href="https://github.com/fastred/Optimizing-Swift-Build-Times#slowly-compiling-files">these guidelines</a> of passing different diagnostic options into the compiler. We passed in -driver-time-compilation &amp; -Xfrontend -debug-time-compilation. More about them <a href="https://github.com/apple/swift/blob/master/docs/CompilerPerformance.md#diagnostic-options">here</a>.</p><pre>xcodebuild -destination &#39;platform=iOS Simulator,name=iPhone 8&#39; \</pre><pre>-sdk iphonesimulator -workspace Over.xcworkspace \</pre><pre>-scheme Over -configuration Debug \</pre><pre>clean build \</pre><pre>OTHER_SWIFT_FLAGS=&quot;-driver-time-compilation \</pre><pre>-Xfrontend -debug-time-compilation&quot; | \</pre><pre>tee profile.log</pre><p>We decided not to use debug-time-function-bodies since we’re just looking for overall build times, not the time per function. We then wrote 2 scripts to process the output of each option and this finally gave us an indication of our build times.</p><p><strong>Script Results</strong></p><p>Our script read through the build logs and calculated the total time of all Swift files inside each CocoaPod and we ignored files in Over. Below is just a short list of the top offenders</p><pre>RealmSwift</pre><pre>Execution Time: 13.662400000000002</pre><pre>Wall Time: 38.39059999999999</pre><pre>Unbox</pre><pre>Execution Time: 12.765</pre><pre>Wall Time: 30.538700000000006</pre><pre>Alamofire</pre><pre>Execution Time: 9.7638</pre><pre>Wall Time: 26.3308</pre><pre>FacebookCore</pre><pre>Execution Time: 8.8349</pre><pre>Wall Time: 24.321099999999994</pre><pre>AlamofireImage</pre><pre>Execution Time: 3.0501</pre><pre>Wall Time: 10.8478</pre><pre>FacebookLogin</pre><pre>Execution Time: 1.3653999999999997</pre><pre>Wall Time: 5.8422</pre><pre>KeychainAccess</pre><pre>Execution Time: 1.2674</pre><pre>Wall Time: 1.9256</pre><pre>OverLogging</pre><pre>Execution Time: 0.5084</pre><pre>Wall Time: 1.1639</pre><pre>CocoaLumberjack</pre><pre>Execution Time: 0.3282</pre><pre>Wall Time: 0.4215</pre><p>Armed with this knowledge we knew what we had to do first. After identifying Realm as the slowest building 3rd Party framework we wanted to test the impact of manually adding and configuring it without using CocoaPods. Thankfully, Realm supplies a pre-built binary that you can manually add to your project. The advantage of a pre-built framework is that Xcode will not compile it when building the project. This speeds up the build process versus CocoaPods, which builds and compiles our frameworks every time we do a build after a clean or delete of derived data. This one change had a massive impact on our build times. We saw a reduction of roughly 100 seconds! 🎉</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*CpUP5UM19fwpx38kYtjR3Q.png" /><figcaption>Before with Realm as a Pod</figcaption></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oe3wUGxK0m6rLeBY_hW3Yg.png" /><figcaption>After with Realm as a pre-built framework</figcaption></figure><p>One disadvantage is that several steps are involved when adding frameworks manually. What helps is that Realm supplies a ready-to-go compiled Framework, not all libraries do that and for those cases, we would have to compile them ourselves.</p><p>Since this was a cumbersome process, if this continued, <a href="https://github.com/Carthage/Carthage">Carthage</a> would be a good alternative dependency manager. The advantage being that it will compile the frameworks for us, as well as provide 1 script to strip the architectures from all frameworks.</p><p>Nevertheless, the above exercise demonstrated the importance in inspecting our build times and that there are tangible ways to improve them. Our goal was to reduce build times by 50%, which we did 😬. This is good enough for now and we have some really interesting opportunities for reduction in future.</p><h3>🚀 What’s Next?</h3><p>The platform team will continue to experiment with build times among many other things on our backlog. We honestly can’t wait to see what we achieve with this new team. From big things like investigating new architectures to small bugs that we have struggled to fix (looking at you iCloud ⛅️).</p><p>One really exciting avenue to explore is the potential of such a team to incubate new talent. Since they are free from feature work it could be a great place for a junior developer to join and focus on exploring the codebase and picking up issues that do not have hard deadlines.</p><p>Want to hear more? Be sure to follow us on Medium and Twitter <a href="https://twitter.com/EngineeringOver">@<strong>EngineeringOver</strong></a></p><p><a href="https://madewithover.com/work-at-over/"><em>We’re hiring! </em>come work-at-over</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=df8b1dcb30d5" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/ios-over-starting-a-platform-team-df8b1dcb30d5">iOS @ Over: Starting a Platform Team</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A Background Repeating Timer in Swift]]></title>
            <link>https://medium.com/over-engineering/a-background-repeating-timer-in-swift-412cecfd2ef9?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/412cecfd2ef9</guid>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Daniel Galasko]]></dc:creator>
            <pubDate>Tue, 26 Feb 2019 16:23:34 GMT</pubDate>
            <atom:updated>2023-02-10T11:00:20.925Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ad-wfiuTwLCq9IxaZ5xFbg.png" /></figure><p>Background timers are a very usefool tool in one’s developer toolbox. Most of the time when we want to schedule a repeating unit of work we consult NSTimer . Problem is, NSTimer requires an active run loop which is not always readily available on background queues. The main thread has an active run loop but this defeats the purpose of having our timer run in the background so its a definite no go. So, to get a dedicated background-queue-friendly timer, we use GCD.</p><h3>Hello DispatchSourceTimer 👋</h3><p>Like most APIs we deal with from Apple, there is always a lower level counterpart. When it comes to background timers, we have DispatchSourceTimer . The code to construct one is rather simple:</p><pre>let t = DispatchSource.makeTimerSource()<br>t.schedule(deadline: .now(), interval: .seconds(1))<br>t.setEventHandler(handler: { [weak self] in<br>    // called every so often by the interval we defined above <br>})</pre><p>This will create a repeating timer that will fire events on a default background queue unless one is specified in makeTimerSource(). All we need to do is create one and we have a repeating background timer 🚀…right?</p><h3>Not So Fast 🐒</h3><p>Unfortunately, it’s not that easy. Whilst that code will create a timer, you will experience crashes if you ever try deallocating that timer, or if you are looking to implement pause and resume functionality. Lets fix that 👩‍⚕️</p><h4>Pausing and Resuming</h4><p>GCD timers can be somewhat sensitive. If you try and resume/suspend an already resumed/suspended timer, you will get a crash with a reason like:</p><blockquote><em>BUG IN CLIENT OF LIBDISPATCH: Over-resume of an object</em></blockquote><p>This tells us we have tried to resume an already resumed timer 💩. Luckily the fix is simple, we just need to balance the calls to suspend and resume, like the <a href="https://developer.apple.com/library/content/documentation/General/Conceptual/ConcurrencyProgrammingGuide/GCDWorkQueues/GCDWorkQueues.html#//apple_ref/doc/uid/TP40008091-CH103-SW8">documentation states</a>:</p><blockquote>As a result, you must balance each call to dispatch_suspend with a matching call to dispatch_resume before event delivery resumes.</blockquote><p>Now is a good time to create a safe wrapper to use these timers</p><pre>class RepeatingTimer {</pre><pre>    let timeInterval: TimeInterval</pre><pre>    init(timeInterval: TimeInterval) {<br>        self.timeInterval = timeInterval<br>    }</pre><pre>    private lazy var timer: DispatchSourceTimer = {<br>        let t = DispatchSource.makeTimerSource()<br>        t.schedule(deadline: .now() + self.timeInterval, repeating:                             self.timeInterval)<br>        t.setEventHandler(handler: { [weak self] in<br>            self?.eventHandler?()<br>        })<br>        return t<br>    }()</pre><pre>    var eventHandler: (() -&gt; Void)?</pre><pre>    private enum State {<br>        case suspended<br>        case resumed<br>    }</pre><pre>    private var state: State = .suspended</pre><pre>    func resume() {<br>        if state == .resumed {<br>            return<br>        }<br>        state = .resumed<br>        timer.resume()<br>    }<br>    <br>    func suspend() {<br>        if state == .suspended {<br>            return<br>        }<br>        state = .suspended<br>        timer.suspend()<br>    }<br>}</pre><p>This allows us to have a timer that will ensure not to over resume/suspend itself. We should also ensure its accessed from the same thread/queue otherwise we will need to add an internal serial queue to prevent race conditions.</p><h3>Preventing Deallocation Crashes 💥</h3><p>One final hurdle before our timer is complete is to ensure it can be deallocated properly. To deallocate a timer, it needs to be cancelled. If this is not done, GCD will trigger the timer and call the event handler on a deallocated object and 💥. We don’t want this. Furthermore, we also don’t want to cancel a timer that has been suspended(paused) because this will also trigger a crash. Unfortunately, this isn’t really documented but I did find a post in the <a href="https://forums.developer.apple.com/thread/15902">Developer Forums</a> that outlined how to ensure timers don’t crash when they are cancelled and deinitialised.</p><p>Our deinitialisation function can now be written as:</p><pre>deinit {<br>    timer.setEventHandler {}<br>    timer.cancel()<br>   /*<br>    If the timer is suspended, calling cancel without resuming<br>    triggers a crash. This is documented here<br>    https://forums.developer.apple.com/thread/15902<br>    */<br>    resume()<br>    eventHandler = nil<br>}</pre><h3>And Finally 🏁</h3><p>Now that we have our timer up and running, using it is simple:</p><pre>let t = RepeatingTimer(timeInterval: 3)<br>t.eventHandler = {<br>    print(&quot;Timer Fired&quot;)<br>}<br>t.resume()</pre><p>This timer will automatically fire events on a background queue. The reasoning for this is that DispatchSource.makeTimerSource() creates a timer that uses a default background operation queue as you will see in the documentation:</p><pre>class func makeTimerSource(flags: <a href="apple-reference-documentation://hsvg3aZP_K">DispatchSource</a>.<a href="apple-reference-documentation://hsY-R6qnZO">TimerFlags</a> = default, queue: <a href="apple-reference-documentation://hs26PogYiH">DispatchQueue</a>? = default) -&gt; <a href="apple-reference-documentation://hskna6NHG-">DispatchSourceTimer</a></pre><p>If you want to configure the queue it uses I leave that up to you 👍</p><p>I included the full timer code in a gist that can be found below. If you won’t be using your timer on a dedicated background serial queue you should definitely include a serial queue inside it to ensure you don’t have any race conditions.</p><p>Happy coding 🤸‍♂️</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/14880cc715678f4c206f376a1d25f09f/href">https://medium.com/media/14880cc715678f4c206f376a1d25f09f/href</a></iframe><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=412cecfd2ef9" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/a-background-repeating-timer-in-swift-412cecfd2ef9">A Background Repeating Timer in Swift</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[An easier way to dequeue cells in iOS]]></title>
            <link>https://medium.com/over-engineering/an-easier-way-to-dequeue-cells-in-ios-5c8b8de4dfed?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/5c8b8de4dfed</guid>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[uicollectionview]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[uitableview]]></category>
            <category><![CDATA[generics]]></category>
            <dc:creator><![CDATA[Michael Dube]]></dc:creator>
            <pubDate>Thu, 21 Jun 2018 10:44:00 GMT</pubDate>
            <atom:updated>2018-06-21T10:44:00.208Z</atom:updated>
            <content:encoded><![CDATA[<p>Using the power of generics to improve cell registration and reuse 🤓</p><h3><strong>The problem we are trying to solve</strong></h3><p>Whenever we have to deal withUICollectionView or UITableView, we need to register a cell before we use it, for example.</p><pre>collectionView.register(Cell.self, forCellWithReuseIdentifier: &quot;Foo&quot;)</pre><p>Later, when you need to use it …</p><pre>let cell = collectionView.dequeueReusableCell(withReuseIdentifier: &quot;Foo&quot;, for: indexPath) as! Cell</pre><p>This is what Apple gives us out of the box but what could be wrong with that? 🤷‍♂ . Well, for starters, we are dealing with a stringly-typed API. We have to register and dequeue our cells based on a string identifier. These two can easily get out of sync and can become a pain to maintain. Not to mention, we will only know its broken in production!</p><p>When we dequeue our cells, we also have to make a force cast to the cell type. It would be nice if we could avoid this entirely. Lastly, we have to remember to register the cell. What if we could have it all and automatically register the cell the moment we tried to dequeue them 🤔</p><h3>The proposed solution</h3><p>Our aim is to simplify cell reuse and registration to just one line of code</p><pre>let cell: Cell = collectionView.dequeueReusableCell(for: indexPath)</pre><p>This seems like a job for generics 😄</p><blockquote>The solution here is inspired by this <a href="https://gist.github.com/gonzalezreal/92507b53d2b1e267d49a">gist</a>.</blockquote><p>While generics are a fairly advanced topic, they are one of the most powerful features of Swift. For those new to Generics.</p><blockquote>Generic code enables you to write flexible, reusable functions and types that can work with any type, subject to requirements that you define. You can write code that avoids duplication and expresses its intent in a clear, abstracted manner.</blockquote><p>A large portion of the Swift standard library is built with generic code.</p><p>Since we would like to work with both UICollectionView and UITableView, we can start off by defining a protocol for a reusable view that has a defaultReuseIdentifier.</p><pre>public protocol ReusableView: class {<br>    static var defaultReuseIdentifier: String { get }<br>}</pre><p>Since cell registration always needs a reuse identifier, let’s use the class name as the default identifier. We seldom need different reuse identifiers for the same view. Let’s use String(describing: self).</p><pre>extension ReusableView where Self: UIView {<br>    public static var defaultReuseIdentifier: String {<br>        return String(describing: self)<br>    }<br>}</pre><p>One thing to be mindful of, is that we can also load our cells from a nib file. So lets go ahead and add a protocol for a nib loadable view.</p><pre>public protocol NibLoadableView: class {<br>    static var nibName: String { get }<br>}<br><br>extension NibLoadableView where Self: UIView {<br>    static var nibName: String {<br>        return String(describing: self)<br>    }<br>}</pre><p>Now that we have our ReusableView &amp; NibLoadableView protocol, we can implement a generic way to register and dequeue a cell:</p><pre>extension UITableView {<br>    func register&lt;T: UITableViewCell&gt;(_: T.Type) where T: ReusableView {<br>        register(T.self, forCellReuseIdentifier: T.defaultReuseIdentifier)<br>    }</pre><pre>    func register&lt;T: UITableViewCell&gt;(_: T.Type) where T: ReusableView, T: NibLoadableView {<br>        let bundle = Bundle(for: T.self)<br>        let nib = UINib(nibName: T.nibName, bundle: bundle)<br>        register(nib, forCellReuseIdentifier: T.defaultReuseIdentifier)<br>    }</pre><p>This can be used on any UITableView instance. Notice how we have two register functions. One is for ReusableViewand the other one is for NibloadableView . When you register it will always look like table.register(CustomCell.self) and depending on the protocol that the cell conforms to, it will use the relevant one. Try it out 😄</p><p>Now that we have a way to register, lets put together a nice way to dequeue our cells</p><pre>func dequeueReusableCell&lt;T: UITableViewCell&gt;() -&gt; T where T: ReusableView, T: NibLoadableView {<br>    register(T.self)<br>    return dequeueReusableCell(withIdentifier: T.defaultReuseIdentifier) as! T<br>    }</pre><pre>func dequeueReusableCell&lt;T: UITableViewCell&gt;() -&gt; T where T: ReusableView {<br>    register(T.self)<br>    return dequeueReusableCell(withIdentifier: T.defaultReuseIdentifier) as! T<br>    }</pre><p>Every time we dequeue we call register first and in our experience the performance cost is negligible.</p><h3>Mission accomplished 🚀</h3><p>Now that we have everything put together, this is how you would use it</p><pre>//make your subclass conform to the protocol<br>extension CustomCell: ReusableView {}</pre><pre>//then you would  dequeue your cell <br>func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -&gt; UICollectionViewCell {<br>   let cell: CustomCell = collectionView.dequeueReusableCell(for: indexPath)<br>   return cell<br>}</pre><p>That’s it! We have removed so much boilerplate code now and our lives are so much easier. We no longer have to worry about registering cells and supplementary views. We never have to think about what string identifiers are in use and best of all, its all typesafe. With just two lines of code you are good to go 🎉</p><blockquote>Download the full code <a href="https://gist.github.com/dubemike/79b3a60b369bf745d56102d1fa9be079">here</a></blockquote><p>I hope this helped you out and you learned something new today. If you enjoyed reading this article be sure to throw your hands together for a couple of claps 👏👏 . Feel free to stalk me on <a href="http://www.instagram.com/dubemike">Instagram</a> and <a href="http://www.twitter.com/dubemike">Twitter</a> 😄.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5c8b8de4dfed" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/an-easier-way-to-dequeue-cells-in-ios-5c8b8de4dfed">An easier way to dequeue cells in iOS</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Limit your pull requests and see what happens. This is our story.]]></title>
            <link>https://medium.com/over-engineering/limit-your-pull-requests-and-see-what-happens-this-is-our-story-6e64e33c7e79?source=rss----efddcdaa36a--ios</link>
            <guid isPermaLink="false">https://medium.com/p/6e64e33c7e79</guid>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[work-in-progress]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[lean-software-development]]></category>
            <category><![CDATA[pull-request]]></category>
            <dc:creator><![CDATA[Naudé Cruywagen]]></dc:creator>
            <pubDate>Fri, 01 Jun 2018 15:20:26 GMT</pubDate>
            <atom:updated>2018-06-01T15:22:07.171Z</atom:updated>
            <content:encoded><![CDATA[<p>A problem many developer teams face is an increasing number of pull requests. Have you ever heard yourself say “The work is done, it just needs to be reviewed”, only to have it merged days (or weeks 😱) later?</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/300/1*bko0KhUMQZxLvTkoY7V4GQ.jpeg" /><figcaption>Seem familiar?</figcaption></figure><p>Believe it or not, our iOS team also had this problem. It was not uncommon for us to have 22 pull requests open for review. Let’s put this in context: 22 pull requests in a team of 8 developers. If all of those pull requests are counted as work in progress all of us were doing 2.75 things at once.</p><p>It seemed like a few people did most of the reviewing, which also meant getting anyone to review your pull request was a tough process.</p><p>I hope this is not the first time you hear this, but multitasking is a myth. Us humans are terrifically bad at it. What we actually do when we “multitask” is task switch (or context switch).</p><p>Having recently joined and seeing this as a pain point in the team, I felt we could run two fun exercises to highlight this problem. Enter our first game…</p><h3><strong>The first game: Proving you don’t multitask well</strong></h3><ol><li>Draw four columns on a page</li><li>Write one of these at the top of each column: “1–12”, “3–36”, “Jan-Dec” and “7–84”</li></ol><p>The objective: Fill out all the columns so that the first column would have 1, 2, 3 etc., the second column will have 3, 6, 9, 12 etc., the third column Jan, Feb, Mar, etc. and the fourth column 7, 14, 21 etc.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*nUUHZng21CsYGLaBxRv3zg.png" /></figure><p>Time yourself doing this.</p><p><strong>Round 1:</strong></p><p>Complete all columns by writing one value in each column, i.e. jumping between columns as you go. (You’ll write 1, 3, Jan, 7 in the first row and then move on to the next row, writing 2, 6, Feb, 14 etc.)</p><p>This should take you about 90–120 seconds. Go ahead. I’ll wait.</p><p><strong>Round 2:</strong></p><p>Complete all columns by finishing one column completely before moving on to the next column. (You’ll write 1, 2, 3 … 12 in the first column, then move on to the next column 3, 6, 9 … 36 until all columns are finished.)</p><p>This should take you about 60 seconds.</p><p><strong>The result</strong></p><p>Sorry to break it to you, but like billions of people, you are also bad at multitasking/task switching/context switching. In round 1 you paid the price of task switching by taking nearly twice as long to complete all the columns. Writing numbers and names of months on a piece of paper is not a difficult task. Imagine if you were task switching between refactoring, debugging a production issue and checking Slack 🤯</p><h3>The second game: Proving you don’t have to write code to contribute</h3><p>Now we’re going to fold some origami. Yes, the art of paper folding. This is going to require your whole team. So gather everyone ‘round. You’ll probably need between 4 and 8 people.</p><p>Here are the instructions:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/495/1*FOaD9Qhen6MiZflb-dCjjQ.png" /></figure><p>We are going to make a production line to create paper boats. Divide the steps from the instructions among the team members like this (assuming 5 people):</p><p>The first person does step 1, the next person does steps 2 and 3, the next person does step 4, the next person does steps 5 and 6 and the last person does steps 7, 8 and 9. You can change these up if you have more people, but purposefully create a bottleneck by at least giving the last person more steps to do.</p><p>The objective: Make as many paper boats as possible in 3 minutes.</p><p><strong>Round 1:</strong></p><p>As soon as the clock starts, the first person does their fold, passes it on to the next person and immediately grabs a new piece of paper to fold. Everyone in the queue then continues to make their folds and pass it on to the next person as fast as they can.</p><p>After three minutes sound the airhorn and stop. Now, count three metrics: the number of finished paper boats, the number of unfinished boats (any paper with a fold still in the queue) and the number of finished boats of which the team is happy with the quality.</p><p><strong>Round 2:</strong></p><p>This time each person is only allowed to pass work to the next person if the next person is not currently busy with a fold. To signal this properly have each person sit with their hands in the air if they are not busy with a fold. When someone has their hands in the air, the person before them in the queue will know they can pass work on to them. Some people will find themselves with their hands in the air more often than not.</p><p>After three minutes sound the airhorn again and stop. Count the three metrics again: the number of finished boats, the number of unfinished boats and the number of quality boats.</p><p><strong>The result</strong></p><p>You should have the same number of finished boats after both rounds (or more after round 2).</p><p>However, round 2 will have significantly less unfinished boats and also the same number of quality boats (or more).</p><p>This is a nice practical example of the difference between a push system and a pull system. In round 1 everyone was pushing work into the system, regardless of capacity. In round 2 work is pulled through the system when there is capacity to perform the task.</p><h3>Combining the results of the two games</h3><p>Armed with the results of these two games we realised a couple of things:</p><ol><li>Writing more code and creating more pull requests were actually harming the amount of work we are capable of delivering.</li><li>Having more open pull requests than we have people on the team slows down our “production system”.</li><li>If we are constantly switching between writing code and reviewing code (without finishing either) we are increasing the time it takes to finish both tasks.</li></ol><h3>How we improved</h3><p>We have limited the number of “allowed” open pull requests to six. If we submit a pull request and hit the limit (or pass it) we don’t continue to our next coding task without reviewing some pull requests first.</p><p>We have seen a dramatic reduction in review time for our pull requests which results in a very welcome increase in output and all of us have more time to review other pull requests.</p><p>Not only do we have more time to review, but reviewing has become a shared responsibility within our team.</p><p>As a bonus we feel more like a team than we ever did, since everyone is involved, collaborating and sharing knowledge on our open pull requests.</p><h3>Try it out</h3><p>These games and insights have made a significant difference to our development process. I hope it can inspire and help you to build great products. Like <a href="https://madewithover.com">Over</a> 😃</p><h3>Further reading</h3><p><a href="https://www.amazon.com/Lean-Software-Development-Agile-Toolkit/dp/0321150783/ref=sr_1_1?ie=UTF8&amp;qid=1527759180&amp;sr=8-1&amp;keywords=lean+software+development"><strong>Lean Software Development: An Agile Toolkit</strong></a></p><p>A must-read about the concept of waste in a software production system — how to recognise it and how to avoid it.</p><p><a href="https://less.works/less/principles/queueing_theory.html"><strong>Queueing Theory</strong></a></p><p>The mathematical analysis of how work flows through a system with queues. The article explains how to avoid queues and how to implement work in progress limits (like we did) to avoid the pitfalls of unavoidable queues.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6e64e33c7e79" width="1" height="1" alt=""><hr><p><a href="https://medium.com/over-engineering/limit-your-pull-requests-and-see-what-happens-this-is-our-story-6e64e33c7e79">Limit your pull requests and see what happens. This is our story.</a> was originally published in <a href="https://medium.com/over-engineering">Over Engineering</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>