Journey to 2.0: Container View Controllers

One of the major structural changes I need to accomplish for Snapthread 2.0 is switching to the use of container view controllers. For those who don’t know, container view controllers allow you to embed one or more view controllers inside a parent view controller, which can then manage transitions between its children. UINavigationController and UITabBarController are examples of container view controllers in UIKit, but you can also create your own.

I’ve never used custom container view controllers before, so of course I hit Google to see what I could find. John Sundell has a great introduction to the topic and I really liked the series by Mike Woelmer as well. (Edit: I also meant to include this fantastic article by Ben Sandofsky) The first thing I learned was that if you want to be able to switch between child view controllers, you should probably set them up in code instead of Interface Builder, which only allows a single embed segue between parent and child. I wasn’t ready for that though, so I decided to take a baby step and find a situation where a single parent-child relationship made sense.

Snapthread’s main view controller is…well, massive. I’m too embarrassed to tell you how many lines of code it is, but after combing through it, I realized at least 400 lines were devoted to setting up the AVPlayer, handling play/pause events, observing the status of the current AVPlayerItem, scrubbing, adding the watermark, etc. Clearly, the video player was a good candidate for having its own view controller.

So, I created a VideoPlayerViewController class and began copy and pasting everything relevant to setting up the AVPlayerLayer, displaying time elapsed/remaining, scrubbing, etc. In Interface Builder, I added a new view controller and copied over the video preview view, which is a custom UIView class that resizes its own AVPlayerLayer, and the player controls.

I deleted the video player and controls from my main view controller and replaced it with a container view from the Object Library. I hooked it up to my new view controller using an embed segue.

Next, I had to figure out how to communicate between my main view controller and the video player. Communicating between the player and its parent was easy; I just set up a delegate with methods for responding to changes in AVPlayerItem status and duration (if it exceeds a certain duration, the UI displays a warning that the user needs to purchase Premium in order to export). I set the delegate using prepare(for segue:), which is called when the child VC is embedded.

There were times when I needed to tell the player to do something from the main view controller, however, such as hide its controls or clear its watermark. I wasn’t quite sure how to handle that. Using notifications was one option, but it just didn’t feel right for some reason. I ended up storing a reference to my VideoPlayerViewController and referencing it directly. That’s probably bad practice, but I’m pretty sure it’ll be okay, as I don’t plan on using that particular video player with any other view controller.

Overall, I feel slightly more comfortable using container views now, and I think I’m ready to tackle the next step: transitioning between child view controllers. I plan to post more about new things I’m learning; each of those posts will be prefaced by “Journey to 2.0” if you’re interested (or want to ignore them, lol).

2.0 Design Goals

Happy New Year! 🎊 I hope 2019 is off to a good start for you. I’ve been slowly getting back into coding since my daughter was born, sneaking in 30 minutes here and there in the evenings after the kids are asleep. I’m hoping to get Snapthread 1.9 submitted soon; it contains bug fixes, rudimentary support for still photos, the ability to loop clips, and makes the app compatible with the new iPad Pro screen sizes.

That said, I’m already thinking about Snapthread’s big 2.0 update. I don’t usually do “major releases” because I prefer to get new features out quickly, but now feels like the right time to slow down and really do a lot of work and polishing. A big update also invites more press coverage, is an opportunity for marketing, and can get Apple’s attention…gotta work toward that Apple Design Award, you know. 😜

My design goals for Snapthread 2.0 are simple and twofold:

  1. Improve accessibility. This will involve making my buttons even more button-y and using text labels in addition to icons for added clarity.
  2. Consolidate functions. Currently, every tool for editing a clip opens up its own modal view, with its own video preview. My goal is to make the clip editing interface more modular, swapping out tools below the same preview so that all editing (muting, cropping, trimming, looping, etc.) happens in the same view controller.

There are many more additional improvements that I’m hoping to cram into 2.0…but we’ll see. Some of them include: video transitions, the ability to add text to clips, Ken Burns effect for still photos, non-buggy support for time-lapse and slow motion videos, and auto-saving of recent projects to guard against losing work. I also want to continue improving the title card selections.

In order to finish 2.0, I’m going to need a good task management app. After watching the beta evolve over the past year, I believe Capsicum by Illuminated Bits may be the best app for helping me accomplish my goals this year. My next post will be all about how I plan to use it!

Link

The Sweet Setup Review

The Sweet Setup Reviews Snapthread

I love this review of Snapthread by Josh Ginter, especially this paragraph:

These backdrops and Creative Commons music options are, again, very plain and simple. However, the point of the app is to be a quick and easy way to share Live Photos with non-iPhone users and with a little extra pizazz. Snapthread is not designed to replace iMovie, so feel free to export your video after the Live Photo conversion and do more major edits inside iMovie.

Josh really nails what Snapthread is all about: it’s meant to be simple and fast, with a few little extras for those who really want them. I’d absolutely advise people who want more control over music, titles, and other effects to continue editing in a more fully-featured editor like iMovie. I’ll continue to add more editing functions as time goes on, but my intention has always been to compete with super casual apps like Clips rather than bigger apps like iMovie.

I Forgot to Take Videos

I forgot to take videos for the first few days after my son Charlie was born. I made sure to pack a couple nice cameras in my hospital bag, and recall struggling against the harsh lighting in my recovery room (not to mention the constant stinging of my incision) to snap some semi-decent photos of my new little bundle. It felt like enough at the time, though now I wish I had done a little more.

Maybe I should have shelled out for the professional photographer. I definitely should have insisted my husband take more pictures of me holding Charlie. And I should have taken some videos, too. Over the past two years countless friends have had babies, and many of them took videos of some of their earliest moments. I find myself wishing I had done the same.

In a way though, I did. I was using an iPhone 6s when Charlie was born. I took a few photos of him at the hospital to easily send to family and friends, and of course took many [thousands] once we finally got home. I’ve always had Live Photos enabled, so each one of those snapshots recorded a tiny 3-second clip.

The clips are shaky, low-quality, and mostly lack sound (because the little guy was sleeping), but there’s just something about them. I’m glad they’re there. They add some “concreteness” to a time of my life that seems like a blur, in a way that a photo alone couldn’t quite accomplish.

When I’m considering how to record a moment, I almost always favor photographs over videos. After all, you can’t really hang a video on your wall. Live Photos make that choice even easier, and with an app like Snapthread, I can still salvage a great moment from a sub-par photo.

My hope for the future of Live Photos is that we won’t have to choose between taking the highest quality photo and capturing those precious little videos. Having portrait mode and adjustable depth data is amazing, but hearing my little boy’s laugh years later is perhaps even more so.

If all goes according to plan, I’ll be going in for a scheduled cesarean section two weeks from tomorrow and we’ll finally get to meet our little girl. You can bet I’ll be taking even more Live Photos (and longer videos too) this time around.

If you use Snapthread to share some of your favorite moments publicly, I’d love it if you’d use the hashtag #snapthread or tag @snapthread (either on Twitter or Instagram) in your post so I can find them. And if you’ve written an article or blog post about how Live Photos in general have affected your life, I’d love to read that too!

Moving from Paid Upfront to Freemium: Logistics

If you’re thinking about switching your app’s business model from paid upfront to freemium and, like me, have no experience working with servers, I’m here to tell you that local receipt validation isn’t as horrible as it seems.

If you’re unconcerned about piracy and simply want to check to see which version of your app was originally purchased, I highly recommend following this tutorial by Andrew Bancroft: Local Receipt Validation for iOS in Swift From Start to Finish. What I did was skim through each step of the tutorial first to see what was involved. Then, I grabbed Andrew’s code from GitHub and read more carefully through the tutorial, copying files from his project to mine as needed.

Andrew’s tutorial doesn’t go into detail about how to check for original app version, which is why I’m writing this. Hopefully someone will find it useful!

Every time Snapthread’s main view controller loads, it runs a function called checkIAPStatus(). Here’s what that function does, in pseudocode:

if the “purchasedPremium” UserDefault has been set, don’t do anything because everything is already unlocked

else if a UserDefault that I set in the previous release of Snapthread called “originallyPurchasedPaidVersion” is true, unlock everything and set the “purchasedPremium” default (this covers users who paid upfront and have used Snapthread recently enough to have had the default set)

else retrieve and validate the receipt using the ReceiptValidator class created in the tutorial (it returns a ParsedReceipt struct if successful) and examine it for originally purchased app version

NOTE: One of the most important things to remember is that the receipt doesn’t list the original App Store version number that was purchased (such as 1.0, 1.1, etc.). Instead it lists the original build number. So you’ll have to note the final build number of your paid upfront version and check against that.

When you ask your ReceiptValidator to validate a receipt, it returns an enum that may or may not have an associated value (either .success, with a ParsedReceipt struct, or .error). So you can do a switch statement on the result, and do something like case .success(let receipt): to grab a reference to the associated ParsedReceipt so you can look at it.

The originalAppVersion property of the ParsedReceipt struct is a String, so you’ll want to convert it to an Int in order to do a less-than comparison.

The only real downside, in my opinion, to doing local receipt validation using Andrew’s method is that it uses OpenSSL, which requires you to disable Bitcode in your project because it doesn’t support it. Disabling Bitcode is easy, but can cause you to get a weird e-mail from the App Store after uploading a build telling you you’ve got extra symbol files, or something like that. It’s just a warning and doesn’t prevent your build from going through or anything, but I was confused by it.

So far I haven’t received any complaints from previous purchasers who can’t export long videos or are seeing a watermark, so I’m guessing I must have done something right!

Snapthread is Now Free!

Snapthread 1.8 is now available with a brand new business model and a gorgeous new icon designed by the incredibly talented Michael Flarup. The update includes mostly minor improvements, including the ability to select entire “Moments” of photos in one fell swoop as well as a button for quickly generating a video from your most recent photos.

There’s something about having a truly great icon that’s made me feel more dedicated to Snapthread than ever. There are so many improvements I still want to make as well—enough to keep me busy for a long, long time. In a way it’s become a sort of playground for me to practice and develop my programming skills, and I love the endless challenge of improving it.

Anyway, I hope you’ll consider giving Snapthread a try. You can use all of the app’s features for free with only two limitations: a watermark in the lower left corner and a 30-second limit for video exports.

Here are three things you can do quickly and easily with Snapthread:

  • Stitch together Live Photos of your kids, pets, latest vacation, etc.
  • Combine portrait videos for IGTV
  • Mix Live Photos and regular videos together

Thanks for all of your support, and extra special thanks to my awesome beta testers and translators. I couldn’t do this without you!

Looking Ahead

It’s been quite a summer. This pregnancy has been pretty miserable compared to my first; a combination of loosening ligaments and chronic lower back/hip issues has made walking extremely painful, and it’s been hard for Charlie to understand that I can’t play with him exactly like I used to. On the other hand, baby is healthy and the end is in sight with only 8 weeks to go!

Snapthread’s revenue dropped off considerably about two weeks ago, so I’m particularly eager to transition to a freemium model to see if that helps. In the free version you will be able to export videos up to 30 seconds long, with a watermark. You can create a longer movie and preview it, but if you try to share it, you’ll get a prompt to upgrade (before that there will be a noticeable warning in the UI when the duration limit is exceeded). This approach will give people a good sense of what the app can do, and while I realize I could probably get a higher conversion rate by being a little more limiting, I’d rather attract and retain a large user base. With all of the feature ideas I have, it’s very possible that more things will wind up behind the paywall in the future.

Speaking of features, I thought I’d list some of the things I’d really like to add to Snapthread as time goes on. Some of these I’ve mentioned before and others are new ideas.

  • The ability to add text and stickers to clips
  • The ability to select a specific section of a song
  • The ability to adjust the crop rectangle of clips for any aspect ratio, not just square
  • Support for still photographs (ideally with a Ken Burns effect)
  • Basic video adjustments: contrast, brightness, saturation, color balance, etc.
  • An option for at least one type of transition (fade, dissolve, etc)
  • A simpler way to loop a video multiple times (this is currently possible by just adding the same clip more than once)
  • The app should cache the most recent project so it can be restored in case of a crash
  • A way to “intelligently” auto-generate a video from a recent Moment

Honestly, that’s probably a couple year’s worth of features right there, at the rate that I’m able to work on it. 

I once stated that Snapthread’s mission was “to provide the fastest, most intuitive way for people to merge Live Photos and videos for the purpose of compiling and sharing their memories,” and to that end I want to make sure that any features I add don’t clutter up the interface. It will be a careful balancing act but I already have plans to streamline the clip-editing UI so that it’s easier to perform multiple edits quickly.

Anyway, I feel like I’m just rambling now, so: look for Snapthread 1.8 to arrive around the time iOS 12 is publicly released (with a pretty new icon, too!). After that, it’s anybody’s guess as to when I’ll be able to get another version out (besides bug fixes) because Baby Girl Hansmeyer will be here October 15th! 

Snapthread and the Terrible, Horrible, No Good, Very Bad Data Model

I may not have been a complete novice when I sat down to create Snapthread, but it’s obvious I didn’t put much thought into its data model.

Basically, I created a class called “Video” which had properties like “asset” (for its AVAsset), “orientation,” whether or not it should be muted, its start time, duration, etc.

The flow from selecting videos to seeing a preview of them threaded together goes like this:

  1. User taps on one or more videos and taps the “done” button
  2. The PHAssets for the selected videos are passed to the main view controller by means of a delegate and stored in an array called “videoAssets.”
  3. The videoAssets array is used for two things: 1) generating thumbnails for the video timeline and 2) fetching the actual AVAssets for all of the selected videos and wrapping them in the Video class. The Video objects are stored in a new array called “clips.”

You can already see a big problem here. I have two arrays to maintain: an array of PHAssets and an array of Video (containing the AVAssets). If a user re-orders videos in the timeline, I have to re-order both arrays. It’s mind-numbingly stupid.

It gets even dumber. I decided to add titles to Snapthread, so I added an enum called “VideoType” and just shoe-horned all the title stuff into the Video class. Now my “clips” array could have Video objects that were of type “.video” or “.title.” But wait, what about the array of PHAssets? I can’t create a PHAsset for a title card.

So, I shrugged and decided users could only add a single title at the beginning of the movie so that I could do stuff like “clips.remove[indexPath.item + 1],” taking into account the title card. It’s so, so gross.

Going Forward

My proposal for a new data model is to create a protocol called “Threadable,” which will cover any component of a user’s movie that can be added to the timeline. Whether it’s a video, title card, or still photo (which I may add support for in the future), it needs to give me two pieces of data:

  1. a thumbnail representation of itself
  2. an AVAsset to insert into the composition (for titles, this is a short blank video that’s included in Snapthread’s bundle)

All of the rest of the media-specific details will go in Video, Title, and (maybe) Photo classes. Then, I’ll have a single array of type “Threadable” which will be a million times easier to deal with.

I never really understood the benefit of protocols until this idea hit me the other night. Now I totally get it!

February Whirlwind

I realize February is the shortest month of the year, that it’s not even over yet, and that saying things like this is super cliché, but: holy cow this month went by fast.

Snapthread was featured on the App Store on the 12th and it still doesn’t seem real. It’s definitely been a fun couple of weeks. Every morning my husband waits anxiously for me to wake up so he can report Snapthread’s current ranking on the charts. At its peak, it hit #14 in the Photo & Video category and #51 on Top Paid Apps (it’s now around #104 in Photo & Video).

It’s not like I made tons of money in the last two weeks but I did make some money, which is more than I can really say for the past four years of learning iOS development. According to App Annie, in the last 30 days Snapthread was downloaded 1,424 (!) times. As soon as it’s no longer featured, I expect those numbers to fall right off a cliff; still, I’m super happy with how well it’s doing and am enjoying these feelings of success, however fleeting they may be.

As I type this, Application Loader is churning away on the first archived build of version 1.6, which adds iPad support. It’ll probably be a few days before it gets through beta review, but if you’re interested in testing Snapthread on iPad, please get in touch!

After iPad support, I hope to finish a revamped title card system before taking a break for a little while. Thanks to everyone who has shared Snapthread, left ratings/reviews, helped me out with programming questions, or just sent kind words and encouragement. This is such an awesome community and I’m proud to be a part of it!

Status

I know, I know. I said I was going to take a break from programming in February. But then Snapthread got featured and I got all jazzed about it and well…here I am. Here’s a peek at my work-in-progress:

Early version of Snapthread for iPad

Numbers

It’s been six days since I launched Snapthread 1.5, so I thought I’d share how it went in terms of downloads and revenue.

What I did differently

When I first launched Snapthread back in September, I did so with a blog post and a couple of tweets. Unsurprisingly, it did pretty poorly, with only a download or two per day. Right before Thanksgiving I decided to put it on sale for 99 cents which caused a modest bump in downloads for 4-5 days, but nothing spectacular.

Of course, Snapthread itself wasn’t particularly compelling at that point, since it only supported portrait photos and videos.

For version 1.5, I upped my game a little bit. I put together a new website, created new, more professional-looking screenshots, put together a press kit, set up a Search Ads Advanced campaign, tweeted about it, published a blog post, and e-mailed six media contacts. I also submitted the App Store Editorial form.

How it went

On Monday and Tuesday, several of my awesome Twitter acquaintances tweeted about Snapthread (some of whom have > 10,000 followers). On Wednesday, MacStories published a review. I was confused and excited when it continued to climb the charts in the Photo/Video category on Thursday, not realizing that another article had been published on iMore that morning. Snapthread’s ranking peaked at #108 before falling rapidly off the chart.

Snapthread was also posted to Product Hunt, but didn’t receive enough upvotes to trend.

Honestly, I still feel like a “nobody” in the iOS development world, so getting as much attention as I did for something I created was great fun. But did any of it translate into big downloads and revenue? Not really.

On the day the MacStories article was published, I made about $140. The next day, $103. Yesterday, $36.

What went wrong?

My gut feeling is that it’s just really, really hard to get noticed. Here are a few other guesses:

  • The app just isn’t good enough.
  • People don’t want to spend $2, or it doesn’t provide $2 worth of value.
  • Some folks don’t like Live Photos, or don’t see any use for them.
  • I didn’t e-mail enough people.
  • I didn’t have any videos showing how the app works.

I also didn’t advertise on Facebook or anywhere else because I honestly can’t afford it. I threw a few bucks into a Search Ads campaign which has so far resulted in 1,319 impressions, 22 taps and 1 conversion.

What’s next?

Next, I keep working to make Snapthread so darn good that it can’t be ignored. I chose this life for fun, not profit, so I’m going to keep on having fun and hope that someday I’ll make a profit.

Snapthread 1.5

Snapthread 1.5 is now available! This update brings support for videos and Live Photos taken in landscape orientation. It also features a new icon and a number of small improvements designed to make the movie-creating process as fast, flexible, and simple as possible:

  • You can now square-crop your videos and adjust the cropping area of individual clips.
  • Your final video can be exported in any of the following aspect ratios: 3:4, 4:3, 9:16, 16:9, 1:1.
  • Video clips in the opposite orientation of the one you selected will be letterboxed or pillarboxed by default. You can change this behavior on the new Settings screen.
  • There is now an option to automatically save your video to your Photo Library when you tap the Share button.
  • You can now 3D Touch a photo or video on the video selection screen to preview it before selecting it.
  • You can now mute all of the clips at once from the music selection screen.

With this update, Snapthread becomes the best app for combining and sharing Live Photos. Personally, it has completely changed the way I think about and take Live Photos. I’m much more likely to keep a subpar photo now if the underlying video is good. And since Live Photos also capture the moment before I take the picture, I find myself mashing the shutter button while Charlie is saying something cute, knowing that I likely captured most, if not all of it.

Anyway, I hope you like the update and I’m looking forward to getting started on the iPad version in March!

Snapthread Roadmap

Landscape support is coming to Snapthread on Monday. It seems like such an obvious thing, and I’m sure anyone who hasn’t been following Snapthread’s development is wondering why on earth I didn’t support landscape videos to begin with. The truth is, this app seems to have taken on a life of its own, and has whisked me far away from the path of my original intentions. What started as an app to merge Snapchat videos is now on its way to becoming a complete video studio for Live Photos.

I didn’t anticipate that, but I’m excited. Really excited. To me, Snapthread 1.5 is really Snapthread 1.0. The beginning of a new app.

I have so many ideas for the future, but for the next point release I’m waffling between these two priorities: revamping the title card system, and making the app universal.

Currently, title cards are extremely boring and limited. However, they’re also not an essential feature, and the app still does something really useful without them. So, I’m leaning towards focusing on the iPad version for the time being. I’m also really hoping that I’ll generate enough revenue over the next few months to hire a designer to help me with some fun backgrounds for titles. Perhaps making the app universal would also boost sales and help fund that.

Anyway, here’s a list of features I think might be cool additions to Snapthread:

Planned before 2.0

  • iPad support
  • New and improved title cards (multiple title cards that can be rearranged, have fun backgrounds and fonts, etc.)
  • For the initial view for video selection to be similar to Photos’ “Moments” tab, to make it easier to find what you’re looking for

Wishlist for the future

  • Video filters
  • Stickers maybe?
  • Localization
  • Ability to select a particular time range of background music
  • A way to either manually auto-generate or perhaps have the app surprise you with a video based on a “moment” in your photo library. Similar to Photos’ “Memories” feature.

I’m committed to improving Snapthread as it’s become one of my own most frequently-used apps. I love making compilations of Live Photos and videos of Charlie to post on Instagram or send to my parents, who only get to see him in person a handful of times per year. I hope others will get as much enjoyment from it as I do, and will consider it $2 well spent.

I think I’m going to take the month of February off from programming, and focus on blogging, taking care of myself, and spending more quality time with my family. I’m looking forward to relaxing, writing, and chatting with you all via Twitter, e-mail, or Micro.blog.

Snapthread 1.5 Beta Test

I’m going to do something I’ve never done before: ask people to beta test my app! If you’re interested in being a part of my very first beta test, please either send an email to feedback@beckyhansmeyer.com or DM me on Twitter (@bhansmeyer). All I need to know is what email address you’d like me to send a TestFlight invite to.

I’m hoping to have a build ready for testing sometime this weekend. In the meantime, here’s some more info about this version of Snapthread!

What’s New in Snapthread 1.5 (Build 26…probably):

  • New icon and slightly tweaked design
  • Support for landscape Live Photos and videos
  • Now you can change your final video’s aspect ratio  to any one of the following: 4:3, 3:4, 16:9, 9:16, 1:1 (and you can switch between them and preview them on the fly)
  • A new settings screen allows you to turn on auto-saving (which saves your video automatically when you hit “Export”) and letterboxing
  • Trim videos

What Still Needs Work

  • If you’ve already trimmed a video and go back to change it, the trimming handles don’t reflect your previous trimmed portion.
  • You can’t adjust the crop rectangle of a video yet. So if you select a 1:1 ratio, important things will probably be cut off.

I’m really looking forward to getting version 1.5 out in the world, but I want to make sure I get some of the kinks worked out first. Thanks in advance to all my beta testers!