<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Drew McCormack on Medium]]></title>
        <description><![CDATA[Stories by Drew McCormack on Medium]]></description>
        <link>https://medium.com/@drewmccormack?source=rss-4082d9467366------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/2*iuEldkUKs2Zy2oINe9zCKA.jpeg</url>
            <title>Stories by Drew McCormack on Medium</title>
            <link>https://medium.com/@drewmccormack?source=rss-4082d9467366------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Mon, 13 Jul 2026 12:42:43 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@drewmccormack/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Here is what SwiftData will be (…and what it could have been)]]></title>
            <link>https://drewmccormack.medium.com/here-is-what-swiftdata-will-be-and-what-it-could-have-been-65b79cd11c6a?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/65b79cd11c6a</guid>
            <category><![CDATA[wwdc]]></category>
            <category><![CDATA[swiftui]]></category>
            <category><![CDATA[core-data]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Fri, 28 May 2021 08:47:54 GMT</pubDate>
            <atom:updated>2021-05-28T08:51:09.217Z</atom:updated>
            <content:encoded><![CDATA[<p>Twitter: <a href="https://twitter.com/drewmccormack">@drewmccormack</a></p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*7KbusXV-U_W67bCqElsPiA.jpeg" /><figcaption>Image by <a href="https://pixnio.com/author/milim84">https://pixnio.com/author/milim84</a></figcaption></figure><p>WWDC is just around the corner, and that means it is time to get in your predictions. For a few years now, there has been discussion of a Swift native data modeling framework, which will do for data what SwiftUI is doing for the user interface. This might be the year. The timing feels right, with SwiftUI reaching a usable level. SwiftData may finally make its appearance. But what will SwiftData look like?</p><p>Before I answer that, I should state first that I have no inside information at all. I don’t know if SwiftData even exists. But I do have a long history with data on Apple’s devices. I’ve written <a href="http://books.google.nl/books/about/Beginning_Mac_OS_X_Snow_Leopard_Programm.html">books</a> and <a href="http://objc.io/issues/10-syncing-data/data-synchronization/">articles</a> dealing with Core Data, and develop the only third party commercial <a href="https://ensembles.io">Core Data synchronization framework</a> currently available. I use Core Data <a href="https://agenda.com">daily</a>, and know it well. And lately, I have started to think about what it would mean for Apple’s data frameworks to follow the same route as SwiftUI, and — perhaps more interestingly — what some blue sky thinking could bring further into the future.</p><h4><em>Core Data is not going anywhere</em></h4><p>If you’re hoping for something completely new with SwiftData, I don’t think you are going to get it. Apple have invested many years into Core Data, and have a strong team working on it. The framework today is very stable, and much more powerful than most developers realize. You don’t really understand just how powerful Core Data is, until you start developing your own model layer from scratch. Then you start to hit the problems:</p><ul><li>How do I make sure that there is only one copy of a particular object in memory?</li><li>How do I access enormous datasets without pulling everything into memory?</li><li>How do I make transactional updates to a set of objects?</li><li>How do I keep the data in memory up-to-date with what is on disk?</li><li>How can I make long running updates in the background, while my UI continues to use the same data store?</li><li>How do I merge in changes from other devices?</li></ul><p>You may not realize it when you first start using Core Data, but you get all of this for free, and lots, lots more.</p><p>So Apple is not going to throw out the baby with the bathwater. Expect Core Data to be at the core of this thing.</p><h4><em>Learning from SwiftUI</em></h4><p>If Core Data is not going anywhere, what place is there for SwiftData? Well, the same place as for SwiftUI.</p><p>Many developers think of SwiftUI as something completely new, but it is actually built atop the work of Apple UI framework engineers going back to the days of NeXT and the Jobs’ takeover. SwiftUI is not as radically different as it first seems. What Apple did is replace the declarative XIB files, which contain a tree structure of XML describing the UI and how it is connected, with Swift source code…containing a tree structure describing the UI and how it is connected. This is oversimplifying a bit, but the shift is fundamentally from a data declaration of the UI to a code-al declaration of the UI.</p><p>If you thought that the list you see on screen in a SwiftUI app is something completely different to what you see in a UIKit app, think again. It’s the same control, with the same autolayout constraining it on the screen. The scrolling works the same, the buttons are the same, the text labels are the same — it is exactly the same. What is different is how the developer told the system where it wanted those controls, and how they should behave. In SwiftUI, you declare these things in code, where previously you might have used an XIB or Storyboard file.</p><h4><em>SwiftData at WWDC2021</em></h4><p>This is all well and good, but wasn’t this post about SwiftData? How did we get onto SwiftUI?</p><p>SwiftData will be designed in the same way. Apple want to leverage years of Core Data development, so the data storage will still use the existing framework, including mirroring via CloudKit. What will change is how you declare your model, and how you fetch and manipulate data in your app.</p><p>To begin with declaring the model, currently in Core Data you use a proprietary XML file via the data modeler in Xcode to declare your data model. Sound familiar? It should, because the data model file is analogous to an XIB file. We used to edit the XIB using Interface Builder (later Xcode), and our app would load the XML and layout the interface. When you use Core Data now, you edit your model using a the Core Data Modeler, a tool similar to Interface Builder, which spits out an XML file, and that is what your app loads to setup the data store.</p><p>Once you see this, it becomes much clearer where SwiftData will be positioned. You will be able to declare your data model entirely in code, using the same techniques you use in SwiftUI. The code will build up a tree of value types defining the entities and properties in the model, and even the migrations between model versions. This can all be done on top of Core Data, without requiring major changes to the framework.</p><p>The other piece of the puzzle is fetching and manipulating data from the store. My guess is that it will be possible to do this entirely with value types (<em>structs</em>). Presumably bindings will be available to integrate your data directly into SwiftUI, so that changes made to the store will propagate to the view, and changes made to the data values will be updated in Core Data.</p><p>Again, this sounds like a big change, but I don’t think you will have to dig very deep to find the <em>old</em> Core Data. You may be provided with <em>struct</em> to work with in your app, but internally, Apple will probably bridge that <em>struct</em> to an old-fashioned Core Data <em>NSManagedObject</em>. The bindings will just be syntactic sugar on top of Key-Value Observation and Key-Value Coding, and the <em>NSManagedObjectContext</em> will be just under the surface, keeping track of everything so it knows what you have changed when it comes time to save. Just like SwiftUI, it’s a shiny new Ferrari body on a tried and true Toyota Corolla chassis.</p><h4><em>What it could have been</em></h4><p>It sounds good, right? SwiftData will make working with your data model nicely integrate with the new SwiftUI framework you are using to layout your UI, and should be as robust as the latest releases of Core Data. It’s the most logical step for Apple to take, and that is why I think we will see it, if not this year, probably next.</p><p>And yet, it leaves me wondering what could have been, because the world of data is in serious flux. Ever heard of blockchains? If not, get out from under that rock and take a look. All the hype of Bitcoin and cryptocurrencies aside, there are some interesting developments afoot. Data storage is going global — its becoming decentralized — and the Toyota Corolla of data modeling frameworks is starting to show its age.</p><p>I’m not suggesting for a minute that Apple adopt the <a href="https://ethereum.org/en/">Ethereum blockchain</a> for Core Data 2.0, but there is plenty they could be doing to make their storage APIs more decentralized. And what’s more, it doesn’t have to be an exercise in altruism. Decentralized storage fits Apple’s narrative beautifully. Here’s why…</p><h4><em>Decentralized stores will rule the earth</em></h4><p>Remember when you got your first Mac or iPhone? If you have been around a while, you will probably remember that it was a fairly isolated device. My first Mac didn’t connect to anything, not even the web. I could write documents, but they stayed right on the hard disk (or floppy, if you are really old!). There was no watch or phone to exchange data with. In the rare case that an app could work with a peripheral, such as an iPod, it required a direct USB connection to sync up the data each day.</p><p>It was into this world that Core Data was born. A world in which everything is centralized on one device. There was no need to handle data synchronization or merge conflicting changes, because there weren’t any. And the Core Data of today is fundamentally the same as it was then: a centralized data storage framework for a single device.</p><p>Yes, now it can upload your data to CloudKit, but it won’t automatically deduplicate objects when you download the changes to another device. Why? Because it can’t. It has been designed for a single device. It has no concept of globally distributed objects. It can’t see that a <em>Contact</em> object on your phone is just another version of a <em>Contact</em> object on your Mac, separated by time and space. It sees those contacts as unrelated. Chalk and cheese.</p><p>What we need now is for someone to take decentralized storage seriously, and I don’t mean Bitcoin speculators. Blockchain and technologies like <a href="https://appdecentral.com/posts-in-the-replicating-types-crdts-in-swift-series/">Conflict-Free Replicated Data Types (CRDTs)</a> have shown how it can be done. In the server world, most services are already decentralized. They have to be; there is no way a single server could handle the load of a Twitter or Facebook.</p><p>Apple just needs to gather a team together, beginning with members of the Core Data team, but also including CloudKit engineers and APFS engineers. All of these people have faced — and solved — the problems of distributed data systems. Together they should design the first decentralized store for apps.</p><h4><em>Why are we still waiting?</em></h4><p>It’s a good question. Given the technologies needed to make a decentralized store are not really that new, why haven’t we seen someone make one yet? Why is there no easy to use decentralized store for app developers?</p><p>One reason is that it doesn’t align with the interests of many of the big players. Companies like Google and Facebook are built almost entirely on advertising, and to make their advertising effective, they need your data. The last thing in the world they are going to promote is a decentralized store, in which all the data is end-to-end encrypted, and it can only be read on the user’s own devices. They want to own the data — they <em>have to</em> own the data. Without it, they don’t have a business. So Google is not going to build our decentralized data store, and neither is Facebook — don’t hold your breath waiting for ReactDecentral.</p><p>But there is one player who has a different business model, one that doesn’t rely on mining user data. It’s a player who likes to talk about customer privacy, actively working to protect users from <a href="http://imore.com/tim-cook-says-response-app-tracking-transparency-has-been-tremendous">tracking</a>, and uploading of personal data like contacts.</p><p>Apple have long touted their stance on privacy. Even if you don’t buy into their motivations, it is difficult to deny that improving privacy works in Apple’s favor from a public relations point of view. It would make sense for Apple to develop a decentralized data storage framework with full end-to-end encryption for developers to use in their apps, so that users are no longer mandated to share data with a central service.</p><h4><em>WWDC2023</em></h4><p>This is the potential. This is what SwiftData could have been. It’s almost certainly not what it will be, but perhaps it can be the next step. Gather the top engineers from the Core Data, CloudKit, and APFS teams, and start now, Apple. And at WWDC2023 we can all be astonished at how easy it now is to develop a cross device app, with complete user privacy guaranteed. We can use the framework to more easily develop apps that run on all of Apple’s devices, and Apple can sell it to the public as a great step forward in taking control of their personal data. Win, win.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=65b79cd11c6a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[My Steve Story]]></title>
            <link>https://drewmccormack.medium.com/my-steve-story-eb7e0fe2ab11?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/eb7e0fe2ab11</guid>
            <category><![CDATA[steve-jobs]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Wed, 07 Oct 2020 08:18:13 GMT</pubDate>
            <atom:updated>2020-10-07T08:18:13.216Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/416/1*nymcUmxgwzNc4bvC5-92KA.jpeg" /></figure><p>Steve’s anniversary was a few days ago. I always pause, at least for a moment, to remember how great those times were after his return to Apple in 1996. A rollercoaster that convinced me to leave my secure job in the scientific community, and risk everything on something new they were calling “apps”.</p><p><a href="https://player.fm/series/debug-11576/77-james-thomson-of-pcalc">Unlike other developers of my era</a>, I never had an exchange with Steve Jobs, and I regret that I now will never get the opportunity. But I do have a “Steve Jobs story”, which I have never shared. I actually don’t know whether it was a hoax, and I also wanted to respect his privacy in the case that it wasn’t, so I have never spoken about it. I think enough water has flowed that it should not be sensitive any longer.</p><p>Back in 2009, I was developing a flashcard app in my spare time called Mental Case (now <a href="https://studiesapp.com">Studies</a>). I was selling it as shareware, but I still had a day job at the university. Apple was fast on its way to becoming the most valuable company on the planet, with the release of the iPhone in 2007, and the new App Store in 2008.</p><p>At the time, Steve was sick. It would remain a recurring theme of his last years. He would be at the reigns one minute, and then be away for a few months on “medical leave”. Outside of Apple, it wasn’t clear how serious this all was. They certainly played it down, as you would expect of a company discussing the health of its CEO.</p><p>That’s when I got the order. I would receive details of all Mental Case purchase orders in my email inbox. (Yes, there were so few I could read them all individually.) This one was different, because I recognized the name immediately.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*R734FwXQxd-tV477GePiog.png" /></figure><p>Could this be the real Steve? I knew the email address was his, but everyone under the Sun knew that email address, so it meant nothing.</p><p>What about the street address? Not a California one. Missouri? Didn’t ring any bells at the time, but a few months <a href="http://news.bbc.co.uk/2/hi/americas/8110625.stm">later it became known</a> that he had been treated for his liver problems in Missouri.</p><p>I’ve since searched for that address. The street seems to be fictional, but the <a href="https://www.google.com/maps/place/Westwood,+Missouri,+Verenigde+Staten/@38.6191712,-90.4776643,13z/data=!4m13!1m7!3m6!1s0x87d8cd7bb8b1f8eb:0xf80c8e4db67b9782!2sSaint+Louis,+Missouri+63131,+Verenigde+Staten!3b1!8m2!3d38.6206914!4d-90.4408037!3m4!1s0x87d8cd48f3715fa7:0xe3e0ef89a85f6bf3!8m2!3d38.6436654!4d-90.4398394">zip code is in St. Louis</a>, and does include a few medical centers, so I guess it is plausible that it was the real Steve. It was either the real Steve, or someone filling in false information, including a false email, and thereby donating their payment receipt to Steve Jobs. Both are plausible.</p><p>I have no idea if this was the real Steve Jobs on the line, or just a carefully crafted practical joke. (I don’t think the information about treatment in Missouri was even common knowledge at that time.) But I like to think it really was Steve, bored in his hospital bed, recovering from surgery, and just browsing through apps to see where the wind was blowing. Maybe he even wanted to learn a language, or memorize the names of presidents, or some other skill that Mental Case could help with.</p><p>I never met or spoke to Steve Jobs, but I like to think he at least used a piece of my software.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=eb7e0fe2ab11" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Cash Cow Revisited]]></title>
            <link>https://drewmccormack.medium.com/cash-cow-revisited-4c2185e2b3d8?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/4c2185e2b3d8</guid>
            <category><![CDATA[agenda-app]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Fri, 02 Oct 2020 13:30:28 GMT</pubDate>
            <atom:updated>2023-04-14T13:06:43.739Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*zLC6K3JHfRh9i1fKCP4IIg.jpeg" /></figure><p><strong><em>Update:</em></strong><em> When I wrote this piece, Agenda did not use subscriptions at all. Recently, we introduced subscriptions, while maintaining the same feature unlocking approach of “cash cow”, meaning customers keep the features they have paid for. If you want to try this in an app, just setup a standard subscription, and unlock any features released prior to the customer’s expiry date. Using this approach makes the burden of setting up the sales model much, much simpler.</em></p><p>For a while now, the other half of the Agenda team, <a href="https://twitter.com/mekentosj">Alex</a>, has been pushing me to write a follow up to <a href="https://medium.com/@drewmccormack/a-cash-cow-is-on-the-agenda-138a11995595">my post on our sales model</a>, which we jokingly refer to as “cash cow”. I wrote the <a href="https://medium.com/@drewmccormack/a-cash-cow-is-on-the-agenda-138a11995595">original post</a> back when <a href="https://agenda.com">Agenda</a> was just a baby note-taking app, with an experimental sales model that was largely untested. It seemed sound in theory — we had plenty of reason to think it would work — but it was early days, and we couldn’t provide definitive evidence.</p><p>Last week, an episode of John Gruber’s <a href="https://daringfireball.net/thetalkshow/">The Talk Show</a> encouraged me to take another look, now that we are almost 3 years into the adventure. The <a href="https://daringfireball.net/thetalkshow/2020/09/26/ep-296">episode in question</a> featured Michael Simmons — co-founder of <a href="https://flexibits.com">Flexibits</a>, developer of the fabulous <a href="https://flexibits.com/fantastical">Fantastical</a> — and covered the recent release of Panic’s exciting new <a href="https://nova.app">Nova</a> app. The talk revolved around the sales model Panic have adopted, which excludes the Mac App Store.</p><p>There are a number of purchase options for Nova, but the main one could be described as “subscribe for updates”, which is very similar to what <a href="https://www.sketch.com">Sketch</a> have pioneered. The nice thing about this model is that, when you pay for a year, you get to keep using the most recent download of the app when it expires, where a standard app subscription typically disables most features when the subscription ends. (As I’ll explain below, Agenda’s Cash Cow model takes this a step further, providing updates with bug fixes and free features to everyone, even after license expiry, and is fully compatible with the App Stores.)</p><p>With 3 years of Agenda behind us, it is time to revisit the Cash Cow sales model. Is it working out the way we anticipated? How does it compare to other models? We’ve gathered statistics and other evidence to present the case. The case for cow.</p><h4>The Cash Cow Model Explained</h4><p>When we first explained our sales model to Gus Mueller of <a href="https://flyingmeat.com/acorn/">Acorn</a> fame, he immediately christened it “feature gating”. Only Gus could come up with such a cool and geeky name so fast, but <em>feature gating</em> is exactly what Cash Cow is. It is quite similar to the Sketch/Nova approach, but the emphasis is on unlocking a set of premium features over time, rather than unlocking app updates.</p><p>With Agenda, the app is free to install and use forever. It even syncs across Macs and iOS/iPadOS devices for free. But there are some premium features scattered through the app which are locked for a new user.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*tE34dTkRKxhZW00Dx_pW5A.png" /><figcaption>Agenda includes locked premium features, which become permanently unlocked after a purchase.</figcaption></figure><p>We store an unlock expiry date for each customer on our server. When they begin, it is set way back in the past. We also have a launch date for each premium feature we build. If a feature was introduced prior to the unlock expiry date of a customer, the customer has access to that feature.</p><p>In the beginning, the unlock date is in the distant past, so none of the premium features unlock. When the customer pays, we set their unlock date a year into the future. The effect of this is that all existing premium features, plus any that arrive in the following year, are unlocked for that customer. (This is the “gating” which Gus had picked up on.)</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*q_CA10JwQYSqJYZbcCBFyQ.png" /><figcaption><em>In the Cash Cow sales model, each customer has an unlock expiry date. This moves forward a year from any purchase, and permanently unlocks all</em> <em>premium features introduced before that date.</em></figcaption></figure><p>The unlock date never regresses in time, and the feature introduction dates are fixed, so the customer has permanent access to any premium features they unlock. Of course, as the customer’s unlock expiry date passes by, it is possible that we introduce a new feature that the customer sees as locked. At that point, they can decide to purchase again, and thereby extend the unlock date another year into the future, or they can wait until the continual build-up of new features makes it attractive to pay again. In the meantime, they have full access to the app, including updates and bug fixes, and the premium features that they already paid for.</p><p>Unlike a freemium model, this is not an <em>à la carte</em> selection of features — it is all you can eat. When you purchase to move your unlock date forward, all features on or before that date get unlocked, forever. Customers appreciate this, because they keep what they have already paid for; and we like it, because we don’t have to support customers stuck on an old version who don’t want to pay to upgrade. And, as developers, we get to have our app in the App Stores, generating recurring income, without the negativity that often accompanies subscriptions.</p><h4>Happy Customers</h4><p>Time to assess. It should go without saying that we are happy. We haven’t considered abandoning the Cash Cow sales model. It’s bringing in the bucks, and we receive virtually no negative feedback about it. In fact, it’s mentioned in a significant number of App Store reviews as playing a factor in a customer’s decision to purchase. We have seen the Agenda ratings slowly rise in the App Store from about 4.1 in the beginning to around 4.6 now. So customers are happy, and we are happy.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*BmqpfY-ugqdIvSq-mHcCZQ.png" /><figcaption>App reviews and tweets tell us that customers appreciate the Agenda sales model.</figcaption></figure><p>From the point of view of day-to-day development, we are also content. The initial setup of the server infrastructure was a major project, but now that that is in place, it doesn’t take much to keep things working. And if a customer finds a bug in Agenda, we can fix it, and tell them to update. We don’t have to worry about people being stranded on an out-of-date version.</p><h4>The Cash in the Cow</h4><p>When considering the economics of the sales model, one of the main questions is whether it generates recurring purchases. In contrast to a subscription, the customer is not required to pay in order to keep using the app, so we have to compel them to do so with premium features that appeal. All carrot, and no stick.</p><p>Do we have any evidence that new premium features tempt users to pay? And do we have evidence that users will tag along using the free app until they are tempted to purchase?</p><p>Let’s analyze the first question using some graphs from the App Store Connect Analytics pages at the time of our big Agenda 10 release on May 25th, 2020. We’ll start with downloads.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*NOk2eEcBIwGuFR-BIa73rw.png" /><figcaption>Download numbers show only a small peak around the time of the release, and much larger numbers a few days later, when Agenda was featured.</figcaption></figure><p>What you can see from this is that we didn’t really get a big spike in downloads until the app was featured, about 4 days after the initial release. We had some marketing setup to run at that time too, and clearly, until it kicked in, there was no great boost in downloads.</p><p>But now let’s see what happened to sales.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xohgh-YiAab4izE9AXFuiQ.png" /><figcaption>Sales show a clear peak on the release date, and actually drop off somewhat when the app is featured. This points to out-of-license customers being tempted to pay by the new premium features.</figcaption></figure><p>What is going on here? This is pent up demand, <em>ie</em>, people using the app out-of-license, who get the new update, see the new features in the interface, and pay to unlock them. It’s a clear sign that things are working as we had hoped: people are staying on board even when they don’t have a current license, and can then be tempted into purchasing as we introduce new premium features.</p><p>A secondary question you might have is this: How many of those purchasers are new, and how many are renewing? 54% of the release peak sales above were due to first-time purchasers, and 46% were from those renewing. But what is perhaps even more interesting is that 35% of the first-time purchasers — about a third — had already been using the app more than 6 months prior to purchasing.</p><p>To drive home the point about pent-up demand, consider sales around a week before this release. At that time, only 15% of sales were due to renewals. Clearly, the percentage of renewals jumped substantially when the new release appeared, and you can also see a similar jump in the percentage of the first-time buyers who had been lurking longer than 6 months.</p><p>In short, the Cash Cow model keeps people engaged and using the app even when they are out-of-license, which provides us an opportunity to win their hearts, and wallets, with new feature releases. It’s a powerful aspect of the model — the app itself is your best marketing.</p><h4>Cash Cow vs Subscriptions</h4><p>I know a lot of you will say that this is all well and good, but how does it compare to a standard subscription? I wish I had the answer to that, but we don’t use subscriptions, so I can’t make any direct comparison.</p><p>If I had to guess, I would say that subscriptions probably have a higher yearly renewal rate than the 15% base rate that we have, simply because subscribers are required to pay immediately to keep using the app, where in our system, they are free to let it lapse and pay at a later time. But I also think that the number of subscribers an app has is likely lower than the number of active users you would have with a Cash Cow approach, because — as we saw above — there are many people who use Agenda having never purchased or having let their license lapse. That pool of users helps spread the word, and gives a potential sales boost when new features are released.</p><h4>Release Planning</h4><p>That brings us to how we organize the releases themselves. We actually use a pretty traditional model for that. We have 2–3 major feature releases per year. So we don’t trickle out features continually, we bundle them up into bigger releases. These give us a nice publicity boost, as web sites pick up the release, and Apple often throw us a featuring bone at these times.</p><p>It’s also important to time a bigger release for the OS bumps in September-October. Apple are always looking for apps to show off their new features, so start building them into your app after WWDC, and let Apple know you have them in the weeks prior to the release. With any luck, you will be featured, and that can give your sales and visibility a nice boost.</p><h4>A Few Elephants</h4><p>When we first introduced the Agenda sales model, there were some naysayers, as you would expect. Concerns were expressed about the toll of having feature releases an integral part of the sales model. These concerns fell into the following general flavors</p><ol><li>Cash Cow will require us to rush in poorly developed features in order to keep people paying.</li><li>It will be difficult to isolate features to make premium, without making the app unusable for customers in the free tier.</li><li>Continually coming up with new features to drive sales will be difficult.</li></ol><p>We weren’t really worried about these things ourselves, and it has played out largely as we expected. We don’t feel pressure to churn out new features any more than under another sales model. All app developers feel some compulsion to add new features, and we don’t feel it any more with Agenda than with other apps we have developed.</p><p>As for determining what should be a premium feature, it is rarely a stumbling block. We discuss it, and reach consensus very quickly. For this, we have developed the guideline that a premium feature should never be essential to the operation of the app. The app should be perfectly usable in the free incantation, and the premium features are usually nice-to-have conveniences, or add some icing (<em>eg</em> control over appearance).</p><p>We also haven’t been challenged finding ways to isolate premium features. Some features, such as the recently introduced note templates, are already well isolated from the rest of the app. Where that isn’t the case, we make the new feature free, and just make a small part of it premium. For example, we added the option to embed reminders linked to Apple’s Reminders app directly in your notes. This is available for free, but there is a premium feature where you can create a reminder in Agenda even without a note.</p><h4>Long Live the Cash Cow</h4><p>In conclusion, after three years of “the cow”, we are happy. Developing the app feels much like developing under any other sales model, and it generates recurring income probably on a similar level to a subscription model, without the animosity from customers.</p><p>Would we recommend it to someone starting out with a new app now? The only thing that would prevent us doing that is the upfront effort required to build a server for registering customers, and tracking their unlock expiry dates. If the choice is between a subscription with no server of your own, and Cash Cow, the ‘easy’ path is through subscriptions. If you are planning to build a server to track subscriptions anyway — perhaps because you have various sales channels — the effort required would be comparable to what you would need for the Agenda approach. The choice is yours. For us it has definitely paid off.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4c2185e2b3d8" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[It’s Time for Apple to Change the App Store Sales Model]]></title>
            <link>https://drewmccormack.medium.com/its-time-for-apple-to-change-the-app-store-sales-model-f9ff09dd314f?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/f9ff09dd314f</guid>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Sun, 16 Aug 2020 11:17:49 GMT</pubDate>
            <atom:updated>2022-05-25T15:40:08.427Z</atom:updated>
            <content:encoded><![CDATA[<p>You’ve been hearing about it for months now. Developers — and governments — are fed up with Apple’s App Store sales model. It started with Hey, and now bigger players like Epic and Facebook are joining in like hyenas sensing a wounded beast. It feels like we have passed a tipping point, and you will be hard pressed to find anyone outside Apple who still thinks the 30% tax is the right approach in 2020.</p><p>I see plenty of disgruntled tweets pass by, but most conclude with something like “I don’t know what Apple should do, but it can’t stay like this”. I was in the same boat, until now. Having given it some thought, I feel like I am starting to see why it doesn’t feel right anymore, and what Apple could change to mitigate it. And I am going to be so obnoxious as to present my thoughts here, if only to start the discussion about where we would like to go, rather than just where we have been.</p><p>Let’s start by going back to the 30% that Apple takes from every sale. Why does that now feel unfair? I called it a tax earlier, and that was not accidental — it really works like a tax. Apple wants to get 30% of any income you make as a result of your app being in the App Store.</p><p>This was straightforward when the App Store launched, because nearly all income arose from direct sales. But over the years, other sales models have arisen, from advertising to In-App Purchase and service subscriptions, and Apple have had to make ad hoc changes to the App Store guidelines to try to collect “the tax” from these many and varied sales models. This has led to bizarre rules, such as the exceptions made for so-called ‘reader apps’, all of which points to a system that is creaking, and has become resented by most.</p><p>Apple wants to collect taxes, like a government or overlord, and that is exactly how they have framed the deal from the very beginning. The deal in short: we will take care of the infrastructure, which you will benefit from, but in exchange we will levy an income tax of 30%.</p><p>And this all worked fine in the feudal app world of 2009, where Apple had complete control over every transaction in its realm. People valued living in Apple’s little fiefdom. Outside the city walls was just the World Wide Web — a scary place — and giving up 30% of your salary to live in civility felt like a fair deal. The city prospered, developers prospered, and Apple prospered.</p><p>But that is not the app world of 2020. It has become a huge, multifaceted market, with multiple platforms, crossover with the web, and a multitude ways to make money. Very few companies now live solely within Apple’s city walls; they are more like multinationals, with offices in many cities, trading across borders in a global marketplace. Apple’s simplistic feudal system, taxing income alone, feels dated.</p><p>Compare the App Store model to how things are sold in the real world today. If I have a great idea for a new restaurant, I can find a great location, pay rent determined by the desirability of said location, and get started. The rent paid is not based in any way on the success of my restaurant. If it is very successful, that is down to me, and I benefit from that, not the landlord. This feels fair: the landlord’s success is based on bringing value to the property itself, and my success derives from how well I execute on my business, the restaurant. It would feel wrong if the landlord demanded a cut of the restaurant profits, something they have had no part in.</p><p>Now think about the App Store in this light, and you start to realize why it feels unfair for Apple to take 30% of all income. In the beginning, it might have been true that Apple featuring your app could contribute to its success to the point where it was justified that they should take a cut of sales. It was more like a partnership, where you needed Apple to get the sales, and it was fair for them to take something for that ‘service’.</p><p>But the App Store has not been like that for many years. Getting featured is still most welcome, but nobody is building a business on that. No app can rely entirely on getting featured as a business model. To make it in today’s app world, you have to find your own users. A company like Netflix will have benefited very little from being featured by Apple, and that is no doubt why they feel miffed at Apple taking 30% of income they have worked hard for. It feels like the landlord turning up and demanding a cut of your profits.</p><p>So what is the way forward? Thinking of Apple as the landlord is helpful for this. Apple provide infrastructure, and that deserves to be rewarded with income. Developers should pay to use that infrastructure. At the same time, good tenants raise the desirability of the neighborhood, which also helps the landlord (in Apple’s case, it helps sell iPhones).</p><p>In my view, Apple should simply charge a fee to developers for every app they have in the store. Think of it as rent. The fee should be based on download tiers. Think of the number of downloads as the desirability of the neighborhood. A bigger company, with many downloads, will pay more than a small one, but this will not be directly tied to the income they generate.</p><p>Taking this approach immediately frees Apple from all the intricate rules needed to tap into the multitude of sales models in existence today. An app developer can use any model it likes to generate income, from direct sales to advertising. Developers can also charge users and process sales with any system they like. No more ad hoc “reader app” rules.</p><p>Apple wouldn’t need to get out of the payment processing business entirely. That should simply be an option that developers have in the App Store. Using Apple’s payments is an easy option for them, and the fee should be inline with other payment processors, something like 5–10%.</p><p>And I think there should still be room for apps that are completely free, and pay no fees to be in the App Store. Think of these apps as social housing in the Apple-as-landlord picture. My vision would be that an app can be registered as ‘monetized’ or ‘altruistic’, and this would be checked in app review. Altruistic apps would pay no fees, and monetized apps would pay a tiered fee based on download numbers.</p><p>The obvious objection to all of this is that Apple would never earn as much posing as a landlord collecting rent, as it does as an overload collecting tax. Maybe, maybe not. Apple would presumably still collect a lot of income from payment processing, and it would also start to collect income from apps that previously worked around the rules. It would no longer be a case of Netflix, Amazon, and Facebook — companies with deep pockets — working around the rules to avoid paying, while small enterprises end up footing the bill. Like an insurance scheme, the fact that everybody pays keeps premiums down.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=f9ff09dd314f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Apple’s New CloudKit-Based Core Data Sync]]></title>
            <link>https://drewmccormack.medium.com/apples-new-cloudkit-based-core-data-sync-4c1f334044bc?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/4c1f334044bc</guid>
            <category><![CDATA[wwdc19]]></category>
            <category><![CDATA[cloudkit]]></category>
            <category><![CDATA[ios]]></category>
            <category><![CDATA[core-data]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Mon, 17 Jun 2019 08:29:13 GMT</pubDate>
            <atom:updated>2019-06-19T05:34:13.256Z</atom:updated>
            <content:encoded><![CDATA[<h4>At WWDC19, Apple entered into another chapter in their struggles with getting Core Data to sync reliably. Is it a case of “third times a charm”, or more like “fool me twice”?</h4><p>Full disclosure: I am the developer of <a href="http://ensembles.io">Ensembles</a>, a Core Data sync framework that works with many different services, including CloudKit, Dropbox, WebDAV, and even peer-to-peer. So I am certainly not unbiased when it comes to discussing sync frameworks. But it may come as a surprise to hear that I am rooting for Apple to get this right.</p><p>Ensembles has been a great success, in that (a) It works!, and (b) I managed to sell some licenses. An indie developer selling licenses for a source code framework is as rare as hen’s teeth, so the 6 years I have been developing the framework have been very rewarding. Now, my development effort is much more firmly focused on <a href="https://agenda.com">Agenda</a>, and I would be happy for Apple to take over what really should have been in their court from day one.</p><p>Have no fear, I have no plans to stop developing Ensembles, even if Apple is successful. I will keep using it in my existing products, and keep maintaining it. I don’t anticipate any big new features, but I am invested in keeping it working, so Ensembles is not going anywhere.</p><p>Let’s go back to the Monday of WWDC when for the first time I saw a session in the schedule mentioning Core Data and CloudKit in the same text. At first, I confess to thinking it was going to be some tips for setting up your own sync engine using CloudKit, something which many, many developers have had to do. But it soon became clear via the Twitters that it was actually the third incantation of Core Data sync.</p><p>You might think that I would be shocked and worried by this, but I really wasn’t. For a start, Apple had clued me in to the fact they might be working on such a solution a few years back. They gave me no details, of course, but if I were shocked about anything, it was that it only arrived this year, rather than two years ago. I had actually given up on ever seeing it, figuring they probably decided not to ship it in the end. I guess the product planning at Apple spans many, many years (…as we are also hearing regarding SwiftUI).</p><p>Given I have a lot of experience working in Core Data sync, you are probably wondering what my impressions of the new approach are. Will it work? What are the potential issues? I haven’t taken a close look at it, but have watched the session, and discussed it on Twitter, and I will now offer my opinion on what they are doing. Again, given I have no direct experience with the framework, you should take some of this with a grain of salt, but at least it might point to potential pitfalls. (I wasted many months trying to get the original Core Data sync to work reliably, before giving up and reinventing the wheel. Let’s hope Apple have their bases covered this time.)</p><p>The general approach Apple are taking seems sound enough to me. They are using their new generational storage (with history) to track changes, and update the cloud from that. Effectively having “versions” in your store is very powerful, because you can bring in new sync changes while the UI of the app continues on oblivious. (If you are interested in pushing this approach to the extreme, check out my experimental <a href="https://gitlab.com/llvs/llvs">LLVS</a> project.) Later you merge in the changes. This is preferable to the nightmare we used to have with concurrent changes, where one context would be forced to merge in change notifications, and failing to do so would lead to exceptions.</p><p>The new generational approach to storage in Core Data may in fact be what they were waiting for the whole time. Apple probably realized the old system of storing transaction logs for every change was not ideal, and so planned first to introduce solid generational support, before adding direct CloudKit sync. I haven’t used the generational support in any of my projects yet — they are mostly legacy apps — but I have not heard anything bad about it, and it seems the “right” approach, so I think this aspect is on the money.</p><p>Assuming it works as advertised, why would you not immediately jump on Core Data CloudKit integration? Well, there are a bunch of “policy” related issues you probably want to consider first, which may sway your decision, at least in the short term.</p><p>To begin with, as with any new Apple technology, it will only work on new operating systems. If you are developing a new app now, it is probably fine; if you have an existing app, it will probably be a year or two before you can consider adding it (…unless you are prepared to tell customers to upgrade to get sync).</p><p>Another drawback, essentially with any Apple solution, is that you are locked in to a black box. If you adopt this approach, you are placing all of your bets on it working. You can’t fix it if there is a bug; you can’t easily migrate to another service; and you can’t support more than one service (<em>eg</em> Dropbox, Google Drive etc.) without adding a whole new sync engine. Note that CloudKit does give you access to your data via a JavaScript API, so you could add an Android or Web app in theory, but I’m not sure if many cross platform apps would want all their eggs in the one Apple basket.</p><p>Now to the nitty gritty. The nature of Apple’s sync, where you effectively have distributed stores, means you can’t globally validate data like you can with a single central store. Apple have chosen to work around this by disallowing validation on relationships. It’s something to keep in mind. For example, if you have a one-to-one relationship that was previously non-optional, once you add sync, you will have to make it optional, and concurrent changes could lead to an orphaned object where the relationship is nil. This is not really avoidable in a decentralized syncing system, though how it is handled can vary. In Ensembles, the same problem can arise, and a delegate method is called to allow the app code to correct the issue (<em>eg</em> delete an object); Apple have opted to just disallow validation of relationships, which means you will probably need to add your own checks to “correct” the data.</p><p>The last problem I see with Apple’s approach is perhaps the most important. Like the original Core Data sync, the new CloudKit sync does not introduce any concept of global identity for your objects. Why does this matter? Not being able to match objects on one device to those on another means you have to do unnecessary, and often complex, deduplication of your data. It seems Apple didn’t really learn from their mistakes here. If you take a look at the sample code from WWDC19, you will find a relatively simple app which is littered with complex deduplication logic. Hardly an easy sync solution.</p><p>The sad part of this is that Apple could help a lot here by simply exposing the CloudKit record names to the developer (Submitted as Feedback Issue: FB6120135). Allowing the developer to choose the record names would introduce a configurable global identity, and the deduplication could be eliminated.</p><p>Ensembles has always had the notion of global identity. You can add sync to a Core Data app with Ensembles in about 20 lines of code, and that is because the framework itself is able to map objects on one device, to those on another. As a practical example, if you have already been syncing using some other mechanism, and move to Ensembles, you can provide the global identifiers you have already setup, and Ensembles will merge the data automatically, regardless of how many devices initially have a copy of the data. And if you have a singleton-like object, such as something for app settings, you can assign a predetermined identifier (eg “Settings”), so that all devices share the same set of options. Having control over global identity makes things much simpler, and I hope Apple realize this, and open up control over the record names in CloudKit.</p><p>Conclusions? I think there is promise here. It is certainly the best approach we have seen to Core Data sync from Apple. They seem to have the foundation in place (<em>ie</em> generational versioning) to do it properly. But even if they get it completely right, you will probably want to weigh the negatives: Can you be content with just supporting the latest operating systems? Are you happy being locked-in to CloudKit? Is it OK to drop relationship validation in your model, and adopt a lot of deduplication complexity? On the plus side, CloudKit offers free and fast sync, so there is plenty going for it. The ball is in your court.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=4c1f334044bc" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[LLVS: Git for App Data]]></title>
            <link>https://drewmccormack.medium.com/llvs-git-for-app-data-d0cc70e25f5a?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/d0cc70e25f5a</guid>
            <category><![CDATA[sync]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[llv]]></category>
            <category><![CDATA[ios]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Sun, 02 Jun 2019 13:20:14 GMT</pubDate>
            <atom:updated>2019-06-02T13:20:49.954Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*JKt4l6rpbrSZsKOZWTC3fw.jpeg" /></figure><p>If you have heard of me at all, chances are that it is through my work on the app <a href="https://agenda.com">Agenda</a>. But I have worked on many projects through the years, and one of the more successful is <a href="https://ensembles.io">Ensembles</a>, a framework for syncing Core Data stores via iCloud and other services.</p><p>When I first developed Ensembles, I took a deep dive into decentralized data synchronization. The plan with Ensembles was to support different cloud storage services, and pure peer-to-peer sync. With no central “truth” or programmable server, this moves the equation firmly in the direction of decentralized algorithms.</p><p>After Ensembles was on the market a few years, I started to wonder about the other half of the abstraction. What Ensembles achieves is to allow any app built on Core Data to sync via virtually any data transport mechanism. It provides an escape from lock in: if you discover Dropbox is not working so well, or Firebase is charging too much, you can simply move elsewhere with a bare minimum of code-level change.</p><p>The other half — the half that I had been pondering — is the Core Data bit. The storage API, I guess you would call it. It’s great that Ensembles can free an app from lock in on the backend, but you are still stuck using just the one storage API. And with Swift going strong, there is definitely a push to investigate other possibilities, beginning with all things Codable.</p><p>This all happened a few years ago now, and culminated in a talk I delivered at <a href="https://cocoaheads.nl">CocoaHeads</a> in Amsterdam. I proposed a framework which would handle moving app data around, and which was abstracted from both the backend data transport, and the front end storage API. Imagine, you could setup your app to use Core Data and sync via Firebase, or just as easily use Realm and sync via iCloud.</p><p>For a while, that was that, but then I started working on <a href="https://agenda.com">Agenda</a>, and I was called upon to once again make something sync. We didn’t want to use Core Data, so I spent many months with my co-founder <a href="https://twitter.com/mekentosj">Alex Griekspoor</a>, designing and building our sync system. There were many aspects that overlapped with things I had learned from Ensembles, but other things were handled differently. For example, we really needed to think carefully about merging. With a text editor, you can’t just pick the most recently edited version of a document; you really have to merge individual edits together as best you can. This process gave me new perspective on the sync problem, and led me to where we are today.</p><p>And where is that? Today I am finally delivering on that CocoaHeads talk. At least, I am releasing an open source framework which abstracts away the front end storage API, and the backend transport API. The <a href="https://gitlab.com/llvs/llvs"><em>Low-Level Versioned Store (LLVS)</em></a><em> </em>is a Swift framework which can be used across devices, tracking the evolution of your decentralized data set. In effect, it is Git for your app’s data.</p><p>“That sounds a bit vague” I hear you say, so let me elaborate. LLVS works like a traditional key-value store, in that you can store data blobs with an associated unique key. What makes it different is that each time you store a set of key-value pairs, you create a new version. This version is based on the previous version, which is based on the previous version, and so forth, forming a complete history of changes.</p><p>What you store as data is entirely up to you. You could put in some plist or JSON data, and use LLVS as a global UserDefaults; or make your Swift model objects Codable, and serialize those into the store to sync. You could even setup LLVS as a sync engine for Core Data, <a href="https://realm.io">Realm</a>, or any other Object-Relational Modeling (ORM) framework.</p><p>The version history in LLVS is very much like Git’s. A version can be based on a single predecessor, or can merge two different predecessors into one. It forms a full Directed, Acyclic Graph (DAG), just like in Git. You can create new branches, and merge them together again as needed. And LLVS gives you full 3-way merging, which allows you to determine exactly what has changed when resolving conflicts.</p><p>The last part of the equation is distribution of data. LLVS has the concept of an <em>Exchange</em>, which transports data to other copies of the store, across a network, or locally. You <em>send/retrieve</em> data with an exchange, just like you <em>push/pull</em> from other repositories with Git. A store can have as many exchanges as you like, so data can pass through any number of paths to get between two devices.</p><p>At this point, there are only two types of exchange that have been implemented: one works via the file system, which is mostly for testing purposes, and the other uses CloudKit. If you only need to share data between devices in the Apple universe, the CloudKit exchange is already an enormous advantage. Transporting your data between devices just got a lot easier with LLVS, and you aren’t locked in if you decide in future you would rather use a different service, or offer a choice of services to your users.</p><p>That’s LLVS in brief. There is much more to tell, but if you are interested, you are best to start walking through the project’s README on the <a href="https://gitlab.com/llvs/llvs">Gitlab</a> page. And if there is a frontend or backend you want to support, why not take a shot at adding it? It’s often only a few hundred lines of code, and you get bonus points for donating your code back to the open source community so everyone can benefit.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d0cc70e25f5a" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The S Word]]></title>
            <link>https://drewmccormack.medium.com/the-s-word-35f232215478?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/35f232215478</guid>
            <category><![CDATA[sync]]></category>
            <category><![CDATA[firebase]]></category>
            <category><![CDATA[mongodb]]></category>
            <category><![CDATA[realm]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Wed, 08 May 2019 19:01:44 GMT</pubDate>
            <atom:updated>2019-05-08T19:01:44.811Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*dEsmXmG9Sqil74bN4hA7XQ.png" /></figure><p>TLDR; Sync is dead. Long live sync.</p><p>I am hopeless when it comes to keeping up with the latest tech news these days. This morning I was going back through my feeds from the past week, and came across a story that you undoubtedly already read about days ago: <a href="http://realm.io">Realm</a> has been acquired by <a href="http://mongodb.com">MongoDB</a>.</p><p>Today I posted my belated take in a <a href="https://twitter.com/drewmccormack/status/1126020391254863872">Tweet-rade</a>, and got plenty of interesting reactions. The general consensus was that Realm lost out to <a href="http://firebase.google.com">Firebase</a> in the end. Probably true — it’s difficult to go up against the Google behemoth.</p><p>My personal take is that Realm also took their eyes off the ball to some extent, concentrating so much on their marketing and other ventures (<em>eg</em> <a href="https://academy.realm.io">Realm Academy</a>), that they neglected their core product. They added sync very late in the game, when it should have been their big selling point from day one. But hindsight is a wonderful thing.</p><p>So are we now looking at an era of Firebase dominance? For a while maybe, but I’m thinking it may be the end of an era, not the beginning. See, sync is dying. Firebase is not the conqueror, it’s the last of a generation.</p><p>When I say “sync is dying”, I mean sync in the way developers currently use it. A developer can spend months writing and testing code to communicate with a web service, generally reinventing the wheel time and again. Most apps contain very similar code for this, all designed to deliver effectively the same outcome.</p><p>Frameworks like Firebase make this less painful by sparing you from the low-level networking, and removing the need to write a custom server, but the mechanics remain much the same: your app communicates over a network with a single central service, which stores the data, and coordinates with other devices.</p><p>The approach of using a server or cloud service as a central store is slowly coming under pressure. Growing privacy concerns; the desire to own your data, and store it where you like; blockchains; and mesh networking (including Bluetooth devices and app extensions) are just some of the trends that point to a more decentralized, distributed future for data.</p><p>And for our customers, sync has been dead for a while. Few still think about devices syncing; data is just expected to be everywhere it is needed. Where that data is stored is inconsequential — it feels like one big data store. In fact, if a customer notices your app “syncing”, it is usually a bad sign.</p><p>And just as customers think of it all as one big heap of data, it’s becoming less and less useful for developers to think of individual devices “syncing up”, and better to think about data globally, as a decentralized repository. Changes you have made on your Watch, and which migrate to your iPhone through Bluetooth, should be treated the same as data arriving via a cloud service like iCloud, or peer-to-peer over WiFi from your iPad. One big decentralized store, with data flowing every which way, and through any route available.</p><p>In startup lore, one way to disrupt an industry is to commodize the existing offerings, making them worth less, and adding your own value proposition in a completely different area. Microsoft famously did this to IBM in the early digital age. Nobody cared if they had an expensive IBM PC, or a cheaper clone from Compaq, because all that was important was that it ran Windows. The hardware became less and less relevant — it was commoditized — and Microsoft got rich. IBM moved on.</p><p>I bring this up, because it feels like there is room for disruption in the world of data. Up to this point, you made your money by selling online storage and resources. And you locked developers in to your product by making them use a proprietary API. In the case of Firebase, you have to infuse your whole code base with the Firebase modeling API. If your app should become very popular, and you start to find Firebase performance is no longer cutting it, or it is getting too expensive, you don’t have many options. An extensive code rewrite is in order if you want to move elsewhere.</p><p>If it isn’t clear yet, Firebase is IBM in this picture, and we need a Microsoft to come along and break it all wide open. Such a challenger would not compete directly with Firebase at all. They would build on top of it, and other similar services, just like Microsoft did back in the day.</p><p>The next generation of data startups could disrupt the current generation by making storage and transport largely irrelevant. There is room there for a new abstraction, one that works with any type of storage (<em>eg</em> SQLite, PostreSQL, MongoDB, Firebase, S3, Dropbox, CloudKit); on any platform (<em>eg</em> iOS, Android, Windows, macOS); with any transport mechanism (<em>eg </em>file system, HTTP, Bluetooth, peer-to-peer); and with any modeling framework (<em>eg </em>raw JSON, Swift Codable, Core Data).</p><p>That is where I expect the next breakthrough to come, whether it be via the open source community or some startup. And when it comes, we can all stop worrying about being locked in to particular storage providers. Storage will become a commodity. We will be able to pick and choose, spreading out our app data across as many services and communication channels as desired. And never give “sync” another thought.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=35f232215478" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Value Semantics, Copy-on-Write, and Multi-Threading went into a Bar…]]></title>
            <link>https://drewmccormack.medium.com/value-semantics-copy-on-write-and-multi-threading-went-into-a-bar-562a8d0b4923?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/562a8d0b4923</guid>
            <category><![CDATA[struct]]></category>
            <category><![CDATA[threading]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[copy-on-write]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Sat, 02 Feb 2019 16:53:43 GMT</pubDate>
            <atom:updated>2019-02-02T17:00:58.982Z</atom:updated>
            <content:encoded><![CDATA[<h4>TL;DR When you pass data between threads, whether you are using value types or not, you can very easily run into undefined behavior with unexpected results.</h4><p>When we first heard about Swift structs, and Apple was busy convincing us to adopt value types wherever possible, one of the main selling points was thread safety. When you work with a value type, it is like your own personal copy of the value, and you don’t have to worry that some other part of the code will mutate it. We have all heard this argument many times, and I personally never really questioned the wisdom.</p><p>After an extensive<a href="https://twitter.com/drewmccormack/status/1091458855089905664"> discussion</a> on Twitter, my understanding has changed. Although it is no doubt unlikely that you will be bitten by threading issues when working with value types, you can be, and when that happens, very odd things can happen.</p><p>Basically, any time you try to access a mutable value from multiple threads, you can already start to run into issues. Assume, for example, one thread is updating a var, while another thread attempts to copy it into a new let constant.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7fda497e13274ed94dc58d9ab1a58b5e/href">https://medium.com/media/7fda497e13274ed94dc58d9ab1a58b5e/href</a></iframe><p>Here we begin on the main thread, initialize the variable s, and then shoot off a block to a background queue, where we attempt to copy the value of s into a new let constant (s2). In the meantime, back on the main thread, s is being set to a different value.</p><p>It should be clear there is a race condition here, and the result of the print cannot be determined. In one run, s2 may be initialized before s is mutated, so that the console shows i = 1, j = 2. In another run, perhaps the mutation takes place first, and s2 ends up holding i = 3, j = 4.</p><p>For anyone with experience working in multithreaded programming environments, this will come as no surprise. This type of race condition is well understood. But, it can get creepier…</p><p>In theory, s2 may not end up with either of the values above. It could, for example, end up being i = 1, j = 4. It all depends on how far the main thread gets before the copy is made. If main thread execution is half way through setting the properties when the copy grabs the memory. This would lead to a “mixed” value like i = 1, j = 4.</p><p>When you add Copy-on-Write, things can get even stranger. CoW is used extensively in Swift, primarily as a way to avoid expensive copying of value types like Array and Dictionary. And if you are using your own deeply nested or large structs, you are generally advised to look at implementing CoW yourself.</p><p>Here’s an example of a CoW type, and code to induce a race condition. You can run it just by dropping it into a Playground.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/b8e2e5b0ccc4d1c7ad8dc9ef306df9f5/href">https://medium.com/media/b8e2e5b0ccc4d1c7ad8dc9ef306df9f5/href</a></iframe><p>When you run this, the saneOperator constant will actually change right underneath you. The first print will issue “We are safe”, but printing exactly the same constant a bit later will write out “We are in danger”. Remember — that’s a let constant with value semantics that is spontaneously changing.[1]</p><p>When I first saw this, I thought the sky was falling. If we can’t trust a value type to exhibit value semantics — and a let value type should not be able to change value — then what can we trust?</p><p>It’s not that bad, of course. I’m assured that this is all due to doing something illegal in the first place, namely accessing the same mutable value type across threads. Once you do that, all bets are off — behavior is “undefined”. Value constants can mutate…mixed values can arise which never previously existed…the sky can fall.</p><p>So what’s the lesson here? First, it is not true that value types are immune to threading issues. If you had that impression — and you would be forgiven for believing it — you need to tone down your expectations. Because the data in value types gets copied around a lot more than for classes, there is less of a risk that two threads will contend it, but it is no guarantee. To guard against it altogether, try to make immutable copies of your data types, whether they are structs or classes, before you fire them across a thread boundary. Avoid sharing mutable data between threads.</p><p>[1] I am <a href="https://twitter.com/jckarter/status/1091710423898804224">told by the experts </a>that this behavior — a let constant spontaneously mutating — can also happen for large non-CoW structs. Apparently it is all about how memory reads and writes get scheduled between threads. So this is not just a CoW problem, but we can at least easily simulate the issue using CoW.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=562a8d0b4923" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Free Trials from Apple’s Perspective]]></title>
            <link>https://drewmccormack.medium.com/free-trials-from-apples-perspective-3d925486bf2?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/3d925486bf2</guid>
            <category><![CDATA[iap]]></category>
            <category><![CDATA[app-store]]></category>
            <category><![CDATA[apple]]></category>
            <category><![CDATA[startup]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Wed, 13 Jun 2018 11:08:19 GMT</pubDate>
            <atom:updated>2018-06-13T11:08:19.923Z</atom:updated>
            <content:encoded><![CDATA[<p>There has been a lot of criticism over the years of Apple’s stance on free trials in the App Store. This has come to a head in the last few months, with a <a href="https://www.thedevelopersunion.org">“Developers Union”</a> formed with the initial objective of putting free trials on Apple’s agenda, and an <a href="https://bitsplitting.org/2018/06/06/ersatz-free-trials/">extensive post</a> by Daniel Jalkut about how recent changes to In-App Purchase (IAP) guidelines do not constitute real free trials.</p><p>I don’t want to get into a point-by-point debate on the topic; instead, I want to do something that I haven’t seen anyone do: try to understand why Apple <em>don’t</em> want the sort of free trials that are being demanded.</p><p>Apple currently allows free trials in two forms: if you sell subscriptions, you can give customers a free month to try the app; and, you can give your app away free, and offer a free In-App Purchase (IAP) to unlock all features for a fixed period of time.</p><p>So why does Apple allow these forms, but not offer a more formal version of free trials? Most developers seem to assume they are deliberately ignoring their protests, for no good reason, or that they simply are not willing to dedicate the resources to solve the problem. I doubt both of these assumptions. I think Apple have probably thought long and hard about it, and concluded that the options they have introduced are actually <em>better</em> than the free trials developer’s are requesting.</p><p>Think for a moment about how a ‘formal’ free trial system would work. What would you see in the App Store? Probably something along the lines of a button with the text “$50 with Free Trial”. Now take your average iOS customer, who has never heard of free trials as they exist outside the App Stores. I suspect many will already be confused by this. Here are some question they might have:</p><ul><li>If I click the button, will I be charged $50 now?</li><li>What happens when my trial is up: will I be charged automatically then?</li><li>How do I cancel my free trial?</li><li>What if I want to pay for the app right now, and not use a free trial?</li><li>What if half way through my trial I want to purchase the app?</li></ul><p>That button actually throws up a lot more questions than it would seem. I personally do not know the answers to some of these questions, and I sell software for a living, so how can Joe Normal ever know? Either they will be too scared to download the app, or they will download it and be a state of uncertainly, perhaps even fear, that they will be charged.</p><p>Even if they don’t fear being charged, they may wonder how they make the purchase when the trial is finished. Is that just an IAP? Can I pay somehow halfway through? Do I have to go back to the App Store and purchase again?</p><p>And what about people who absent-mindedly download by clicking the button. They may well miss the free trial information, and think they already paid. A month later, they are going to get angry, because they “already bought it!”. Or they realize it is a free trial, but quickly forget that, because they never formally opted-in to a free trial, later to find their data is being held for ransom unless they pay up. (Such a confusion could arise from overloading the purpose of the “Get” button, to mean download <em>and</em> get a free trial.)</p><p>Apple don’t want this. Apple want customers to make a clear decision about their purchase, with clear implications. They certainly don’t want refund requests to rain in, or — even worse — barrages of customers having their data held hostage because they didn’t realize, or forgot, they were in a free trial. And they don’t want any ambiguity, which is what a “$50 with free trial” button introduces — in bucket loads.</p><p>So why are the existing options any better? Let’s take the free IAP system. Firstly, there is no fear about downloading an app — it is free to download. There is a nice big “Get” button to indicate that. Second, once you have the app, you are told there is a free trial, and you are given a clear choice to opt-in. Because it is an IAP, and not a subscription, you know there can be no charge at the end of the trial. There is a second IAP to purchase the app; it is equally clear that you don’t pay until you activate that IAP, and that you can do that any time. Everything is driven by the customer, and all opt-in. No uncertainty.</p><p>The subscription option is also clear. People know what a subscription entails, and thus expect to be charged regularly. It is clear that you get a free month, and obvious that after that time the subscription will continue unless you cancel it. Again, fully opt-in, and no uncertainty.</p><p>For me personally — and not a reflection of the opinions of others in my company — Apple are doing this right. There are perhaps a few rough edges — for example, they could word the free In-App Purchase option better — but their philosophy of making it completely clear to customers what they are getting, and when they pay, is on the money. It is not a case of Apple being vindictive. I guarantee they have thought about this problem deeply.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3d925486bf2" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[A ‘Cash Cow’ is on the Agenda]]></title>
            <link>https://drewmccormack.medium.com/a-cash-cow-is-on-the-agenda-138a11995595?source=rss-4082d9467366------2</link>
            <guid isPermaLink="false">https://medium.com/p/138a11995595</guid>
            <category><![CDATA[agendas]]></category>
            <category><![CDATA[apps]]></category>
            <category><![CDATA[swift]]></category>
            <category><![CDATA[sales-model]]></category>
            <category><![CDATA[mac]]></category>
            <dc:creator><![CDATA[Drew McCormack]]></dc:creator>
            <pubDate>Mon, 15 Jan 2018 14:37:53 GMT</pubDate>
            <atom:updated>2020-10-03T10:03:22.981Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*jh26Ni8-c6cwAaZk6Lt4jA.jpeg" /></figure><p><strong>Update:</strong> There is a <a href="https://medium.com/@drewmccormack/cash-cow-revisited-4c2185e2b3d8">follow up post</a> about this sales model, told from the perspective of 3 years on the market.</p><p>TLDR; The note taking app Agenda is launching with a new sales model for Mac apps.</p><p>On January 22nd, I am launching a new app for macOS: <a href="http://agenda.com">Agenda</a>. When I say ‘I’, I really mean ‘We’, because Agenda was developed with <a href="https://nucleobytes.com">Alexander Griekspoor</a> and <a href="http://wrinklypea.com">Marcello Luppi</a>. Alexander is best known as the developer behind the Apple Design Award winning app <em>Papers</em>, and Marcello is a great designer who we have been working with on just about everything for the past 10 years.</p><p>I’ll no doubt have more to tell about <a href="http://agenda.com">Agenda</a> itself after the launch, but right now, I wanted to introduce the sales model we have settled on, because it is quite unique. We don’t really have a name for the model, although internally we jokingly started referring to it as ‘cash cow’, because it was our ticket to fame and fortune. Grandiosity aside, a more accurate description may be something like a ‘non-renewing, premium feature subscription’, but that is quite wordy, and ‘subscription’ is a dirty word in our team, so we’ve stuck with<em> cash cow</em>.</p><h3>The Great Mac App Sales Crisis of 2017</h3><p>For the last few years, Mac developers have been struggling in the race-to-the-bottom mentality that was ushered in by the mobile app stores. Customers no longer want to pay large sums for software, and traditional approaches such as selling major version upgrades are often met with a strong backlash. Great apps like <a href="https://www.ulyssesapp.com">Ulysses</a> get a barrage of 1-star reviews when they <a href="https://medium.com/building-ulysses/why-were-switching-ulysses-to-subscription-47f80b07a9cd">migrate to more sustainable sales models</a> like subscriptions.</p><p>The situation is getting pretty dire, pitting customers against developers; there doesn’t seem to be a model that is working well for both parties. Paid upgrades trigger an outcry; subscriptions trigger an outcry; and the lack of trial versions in the Mac App Store means selling at a sustainable price is a challenge.</p><h3>The ‘Cash Cow’ Model</h3><p>We thought about this long and hard when developing Agenda, and decided to try something new. It’s required a lot more work than simply uploading the app for sale on the Mac App Store — more on that below — but we think the result feels fairer to customers, and will encourage them to pay for the app with none of the usual resentment. Early reactions, during our beta testing program, and in <a href="https://www.ifun.de/agenda-hat-nachgedacht-mac-app-probiert-neues-geschaeftsmodell-aus-117504/">online forums</a>, have been positive.</p><p>So what are we actually talking about here? Agenda’s sales model lies somewhere between freemium and subscription. The app itself is free, with no time limits, but there are extra premium features that require an In App Purchase (IAP) to unlock. When an upgrade pack is purchased, all current features are permanently unlocked across all of the user’s Macs (…and iOS devices, when the iOS app becomes available). In addition, any features added to Agenda in the twelve months following the purchase are included, and permanently unlocked as well.</p><p>Only after the twelve month pack has expired can new premium features begin to appear that require a new purchase. The user can choose to buy a new pack, unlocking twelve more months of new features, or be content with what they already have until features are added which tempt them to purchase again.</p><h3>Inspiration Courtesy of <em>Sketch</em></h3><p>Cash cow is inspired by a sales model used for the popular vector graphics app <a href="https://sketchapp.com">Sketch</a>. (The model was actually first adopted by <a href="https://framer.com">Framer</a>, but Sketch is probably the most high profile app using it.) When you purchase Sketch, you get twelve months of updates included. So you get new features and bug fixes for one year, but after that you are on your own again until you make a new purchase. You won’t get any new features, or even bug fixes, but you can keep using the last copy of the app that you downloaded.</p><p>The Sketch sales model allows the development team to avoid the dreaded major version update cycle, which is hated by customers and developers alike. Customers hate it because they feel forced into making a new purchase, even if they don’t think it has much to offer. Developers are also not too keen, because they have to go into hiatus for 6 months or more to come up with enough new features to justify the version bump. And because many customers perceive the update as just a money grab, a major upgrade can lead to a heap of negative feedback and 1-star reviews.</p><p>Agenda’s sales model is inspired by Framer and Sketch, but there is a fundamental difference — we never leave a customer behind. We felt it was important to always be able to offer customers the latest build of Agenda, so that they get all of the bug fixes, even if they haven’t paid or their year of features is up. For this reason, cash cow is about unlocking features, rather than unlocking updates.</p><h3>Aren’t Subscriptions the Way Forward?</h3><p>The changes Apple have introduced to <a href="https://developer.apple.com/app-store/subscriptions/">subscriptions in the app stores</a> are a pretty clear indication that it is aware that the traditional ways of selling apps are not working as well as they used to. So are subscriptions going to save the day?</p><p>You only have to take a look at the <a href="https://hackernoon.com/subscription-software-paradox-d4a1aef4d88a">fate of Ulysses</a> to know the answer to that. No, not the Greek hero, the well-respected Mac app that moved to a subscription model in 2017. Despite <a href="https://www.ulyssesapp.com">Ulysses</a> being a great app, which has won an Apple Design Award, customers were not shy in <a href="https://hackernoon.com/subscription-software-paradox-d4a1aef4d88a">‘punishing’</a> the change with 1-star reviews, pushing their stellar rating much lower than the app alone deserves.</p><p>People clearly don’t like subscriptions when it comes to apps. There seems to be <em>subscription overload</em>. It sometimes feels like every company wants you to sign up for a subscription, and people have become wary of them. It is an extra barrier to purchase; a feeling that your monthly outlays are getting too high, or that you will forget to cancel the subscription when you no longer need it.</p><p>You hear these complaints from customers regularly, but what really ruled out a subscription model for Agenda was this: we felt exactly the same way! We may be developers, but we are also consumers, and we feel the same repulsion to subscriptions — they are a reason to look elsewhere.</p><p>We didn’t want that for Agenda. We wanted a sales model that would encourage a customer to purchase, but not force them to do so. And we didn’t want customers to feel locked-in, or avoid a purchase because they were worried about feeling locked in.</p><h3>How Is Agenda’s Model Better?</h3><p>Let’s tally up the advantages that we see with the new sales model, and why we think it could be an attractive option for other Mac apps.</p><p>From the customer’s perspective, we see these pros:</p><ul><li>Not only can customers trial the app for free, they can keep using the base features as long as they like (à la freemium).</li><li>The latest version of the app is available to everyone, including all bug fixes. Nobody need be ‘stuck’ on an older version.</li><li>The customer is not locked into a subscription to keep using the app. The features they have already purchased are unlocked forever.</li><li>There are no major upgrades, forcing customers to pay to keep using the app. Customers can decide whether new features warrant a new purchase, and keep using the latest version even if they decide not to upgrade.</li><li>When a customer makes a purchase, it is clear what they are purchasing: access to a concrete set of premium features.</li><li>Agenda can be downloaded from either the Mac App Store, or the <a href="http://agenda.com">Agenda website</a>, and the customer can switch between the two variants while maintaining their existing license. For example, you can purchase via a download from the Agenda web site, and later install via the Mac App Store.</li></ul><p>And from the developer’s perspective:</p><ul><li>As with subscriptions and paid upgrades, a single customer can generate recurring income for the developer.</li><li>The development team is not forced to disappear for 6 months to develop a major new release. The app is continually evolving, with new features and bug fixes.</li><li>With no major updates, there are no opportunities for large scale customer disappointment being reflected in a wave of poor reviews and negative feedback. Updates are spread out, as are customer purchases.</li><li>Customers who do not immediately purchase are not necessarily a lost opportunity. They can continue to use the app for free, and may be tempted to purchase in future by newly introduced features.</li><li>It is quite easy to incentivize customers to help spread the word. For example, we could introduce bonus time for referrals. If a customer referred someone to the app, resulting in a new purchase, the referrer could receive an extra 3 months of premium features. You can conceive of many variations on this theme.</li></ul><h3>The Nitty Gritties</h3><p>That concludes the high level discussion, but if you are a developer you might want to keep reading, because I’ll now go into how we implemented <em>cash cow</em>. (Others will probably want to skip this bit.)</p><p>To make cash cow work in the Mac App Store, and via direct web downloads, we had to develop our own licensing server. Because we are not server-side developers, we opted for a server built with the same programming language used to construct Agenda itself: Swift. We added in the <a href="http://www.kitura.io">Kitura</a> server framework from IBM, and a few bits and pieces of <a href="https://vapor.codes">Vapor</a>, and host the whole thing on <a href="http://heroku.com">Heroku</a>.</p><p>The server is fairly standard: it tracks user accounts, and the In-App Purchases (IAPs) made. The user accounts are also used for our in-app Agenda Community. (The community is another Agenda innovation that we will blog about in future.) The IAPs can take place via any vendor; we currently support the Mac App Store and <a href="https://paddle.com">Paddle</a>.</p><p>A lot of the complexity of the server is just validating purchases. When a purchase is made on the Mac App Store, the Agenda client app uploads a receipt which must be validated with Apple’s servers. With Paddle, a webhook is called when a purchase goes through. Our server validates the purchase, and then adds twelve months to the user’s license.</p><p>On the client side, from the Agenda app’s perspective, there are a number of premium features, each with a date of introduction. The client app queries the server for the license status. If no purchase has been made, all premium features are locked; if there has been a purchase, a feature is considered unlocked if the license expiry date falls after the introduction date.</p><p>In principle, it’s all pretty straightforward. Of course, the devil’s in the details. I was quite surprised how much work was involved in getting it all working. Probably 3–4 months all up.</p><h3>Concluding</h3><p>And that is the one downside of the model at this juncture. It is much easier to just upload a binary to the Mac App Store with an up-front purchase fee than it is to build a cash cow server, and the accompanying client app integration.</p><p>But cash cow feels like the ‘right’ model; the way a Mac app really should be sold, assuming you were prepared to overlook the implementation cost. We decided the cost was worth it for Agenda, and hopefully, if other Mac apps start to move in this direction, frameworks will become available to make it all much easier.</p><p>And whether the sales model actually bears fruit will — naturally — only be something we can answer after the Agenda launch on January 22nd.</p><h3>Agenda Links</h3><ul><li>There is a Press Kit <a href="http://downloads.agenda.com/presskit.zip">here</a>.</li><li>You can preorder Agenda on the <a href="https://itunes.apple.com/us/app/agenda-a-new-take-on-notes/id1287445660">Mac App Store</a>.</li><li>Agenda’s web site is <a href="http://agenda.com">agenda.com</a>.</li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=138a11995595" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>