Status

If Nintendo made an N64 Classic, what would be your top 10 games? In no particular order:

  1. Donkey Kong 64
  2. Mario Kart 64
  3. Super Smash Bros.
  4. Mario Tennis
  5. Beetle Adventure Racing
  6. Diddy Kong Racing
  7. Mario Party
  8. Pokémon Snap
  9. Super Mario 64
  10. Ocarina of Time
Status

My next task for Snapthread is to add support for landscape videos. I think what I’ll do is have the app “smartly” pick the best aspect ratio for your final video based on the clips you’ve chosen, but also give you the ability to manually change it (including 1:1). ?

My Last Mac

‘Tis the season for End of the Year blog posts! Much like Charles Dickens’ three Christmas ghosts, the blogosphere is frequented by holiday posts, some reflecting on the past, some making sense of the present, and some speculating about or making resolutions for the future. With a fresh update to Snapthread packaged up and sent to the review queue, I thought I’d sit down and write a little something about the Mac: specifically, why my current Mac may be my last.

Quite an incendiary statement, isn’t it? If you’re heavily involved in the Mac community, it can evoke strong emotions: defensiveness, sadness, nostalgia, or perhaps hearty agreement. If you’ve read my blog for awhile, it might also elicit confusion. “Wait, Becky. I thought you loved your new MacBook Pro?” I do love it! It’s a great little machine. I love the Touch Bar, and the big trackpad, and the beautiful screen. It’s fast, and the few keyboard issues I’ve had have quickly resolved themselves. I love the Mac, and I hope it has a long, prosperous future ahead of it.

Here’s the thing, though. I replace my computer every five years. Five years is an incredibly long upgrade cycle in the world of technology. This post isn’t about the quality of the Mac declining, but rather the quality of iOS improving. I’m making a bet that by the time I’m ready to buy a new computer, the iPad Pro is going to be able to do everything that I need it to do, including run Xcode. And if there’s a few little things that it still can’t do, I’m betting this 2016 MacBook Pro will still be chugging along in 2021.

I mean, think of the changes that have come to iPads between iOS 6 and iOS 11. Now imagine what changes could occur between iOS 11 and iOS 16 (or whatever it’ll be called at that point). Multitasking will continue to improve, and maybe there will be some kind of panel/windowing system. Hopefully it will be easier to shuffle files between apps. There will likely be hardware keyboard improvements and perhaps even an extra port or two. There will be even more pro software from great folks like Affinity.

In short, I’m feeling really optimistic about the future of iOS, despite the many hiccups I and others have experienced with iOS 11. And I never thought I’d say this, but I’m looking forward to living the iPad lifestyle. Just not yet.

Why switch at all? Well, first, it would be a great relief for my bank account. Replacing a Mac, Apple Watch, iPad, and iPhone on a semi-regular basis gets really expensive (as those of you shelling out for an iMac Pro today can painfully attest). Taking one of those devices out of the mix can help a lot. Aside from that, there are four main reasons why I’d like to move to an iPad someday: 1) cellular connectivity, 2) detachable keyboard, 3) Apple Pencil support, and 4) overall portability.

Maybe next I’ll write up my grand wishlist for my “ultimate” iPad Pro. ?

Status

Did anyone at Apple even try to use Control Center on the iPhone X while carrying a coffee, or a briefcase, or, I don’t know, a baby? I think I’m gonna have to yell about this until they change it.

Status

How do I add multiple volume changes to an AVMutableAudioMixInputParameters instance? Using setVolume:atTime twice in a row is causing the first instruction to be ignored.

Control Center on the iPhone X

I ended up enabling AssistiveTouch, and under Custom Actions set Single Tap to Control Center. I placed the virtual button at the very bottom of the screen and set it so that a triple-click of the side button activates/deactivates AssistiveTouch. It’s not an ideal solution, but I need one-handed access to Control Center and this does the trick.

Staying Focused with an App Mission Statement

One important part of marketing an app is developing an elevator pitch (for more info on that, see Aleen’s great post at App Launch Map). An elevator pitch helps you tell others what your app does and why it should exist without going into too much detail about its entire feature set.

A mission statement (also called a vision statement/statement of purpose) is slightly different. It can also be used for external marketing; however, it’s primary purpose is to provide internal guidance. A company’s mission statement should ideally be consulted before making any product decisions, codifying any policies, or beginning any strategic initiatives. It describes the company’s “core” and helps prevent a loss of focus.

I think we can all think of at least one app or tech company that seems to have lost its focus lately (?? Dropbox). iTunes used to be about music. VSCO used to be a great photo editor. Everything Facebook owns now has Stories inside of Stories. Indeed, feature creep and a general misunderstanding of user wants/needs has ruined many a good app/service.

That’s why I decided to come up with a mission statement for my app. As I’ve been working to improve Snapthread (yes, I decided to make the “T” lowercase; it’ll be reflected in the next update), I’ve found myself getting lots of ideas for new features. I want to make sure I don’t stray away from the app’s true mission.

So here goes: Snapthread’s mission is to provide the fastest, most intuitive way for people to merge Live Photos and videos for the purpose of compiling and sharing their memories.

I like that this statement has a human component. If I’m going to be returning to this over and over, I want to be reminded that my primary goal is to improve people’s lives (if only in a small way). It also brings accessibility to mind. From this, you can see that my goals are to be fast, intuitive, and to focus on video merging.

How is this useful in practice? Like this: if ever I get an idea for a feature that seems cool, but would greatly increase video export times, I’ll toss the idea because my goal is to be fast. If I ever find myself adding a lot of complexity to the interface, I’ll have to take a step back and ask myself, “Does this slow people down? Does it make the app less intuitive? How does this affect the user’s workflow?” Another example: I’m planning to make Snapthread a universal app. I’ll probably do a lot with drag and drop, because dragging and dropping things on an iPad is both fast and intuitive.

My mission statement also reminds me of how my app is different from others, lest I be tempted to copy them. For instance, Clips also lets users merge videos. However, it doesn’t support Live Photos and is focused more on all of the fun effects that you can add to your movies. It also allows project saving, which adds a data persistence layer and a lot of added complexity. Snapthread doesn’t save anything, because it’s meant for quick creations without a lot of “tinkering.”

An app mission statement doesn’t have to be super formal. It doesn’t even have to be a statement…it could just be a few bullet points (I’d say no more than five). For me, it’s just one more thing to help me focus, especially when I’m trying to make to-do lists and wondering which feature I should tackle next!

Link

Interview: App Camp Fireside Chats

Interview: App Camp Fireside Chats

App Camp for Girls is interviewing one member of the Apple community each day for the duration of its current Indiegogo campaign. They were kind enough to ask me to participate, so you’ll find my entry above!

I admit I haven’t donated to the campaign yet, but plan to: I just need to decide on a rewards tier. As I’ve said in the past, I would have loved App Camp as a young girl. I hope you’ll donate too! I also want to encourage everyone to check out the other fireside chat interviews. They’ve been really fun and encouraging to read.

SnapThread 1.1 with Live Photo Support

I have a confession to make: I released SnapThread too early. I thought it was a good MVP (minimum viable product), but I was wrong. It was a little too buggy, and didn’t have a real “killer” feature.

The good news is, SnapThread 1.1 is now available, and it’s what I should have waited to release in the first place. I’m hoping that with the addition of Live Photo support, SnapThread can now be people’s go-to app for quick, easy portrait video compilations.

New features:

  • Live Photo support! SnapThread will strip the 3 second videos from your Live Photos and allows you to stitch them together.
  • The app now presents the native iOS share sheet upon successful export.
  • New duration limits. Filter your photo library by videos under 10, 15, 30, or 60 seconds.

Improvements/Bug fixes:

  • Faster exporting, and fixed a bug where the exporting would fail when trying to merge over 16 videos at once.
  • Better progress reporting. Downloading lots of videos from iCloud can take awhile, and now the app more accurately reflects the download’s current progress.
  • Smart aspect ratio: since Live Photos are 3:4 and regular videos are 9:16, SnapThread chooses the final video’s aspect ratio based on what you have more of. So if you have 45 Live Photos and 1 video you want to merge, the final video will be 3:4. Likewise, if you have lots of widescreen videos and only a few Live Photos, the final video will be 9:16. (you should see the tangled if-else statements that determine the video clips’ scaling and translation values…it’s terrifying).

Since “the proof of the pudding is in the eating,” here’s a video of Charlie’s day at the pumpkin patch that I stitched together from Live Photos (and one video):

Future plans/ideas for the app:

  • Photo library album picker (so you can find clips more easily)
  • Scaling improvements and fixes (I’m sure there’s some edge cases I missed!!
  • Ability to mute individual clips?
  • Ability to add a very simple title to beginning of video?
  • New localizations
  • Accessibility improvements
  • iPad version??

SnapThread Now Available

SnapThread icon

My new app, SnapThread, is now available on the App Store! SnapThread is a simple, no-frills utility for merging portrait videos from apps like Snapchat and Instagram Stories.

I am hoping to add support for Live Photos in the next version. In the future, I may also add the ability to include a title for a few seconds at the beginning of the video, or add a single background music track. However, I don’t want to complicate the app too much, so those features aren’t guaranteed to make the cut.

Let me know if you run into any trouble (errors and such) using the app, and if you like it, please consider leaving a rating or review. Thanks!

Please, Don’t Write About My App

Please, don’t write about my app. It’s not that good. In fact, it probably crashes sometimes. Also, I don’t really know what I’m doing.

Please, don’t tell your friends about my app. They probably won’t like it. I mean, the art assets aren’t good enough. It’s too simple. And it only really appeals to a tiny niche anyway.

Thus goes my inner monologue every time I prepare to ship an app. It’s not because I’m humble: trust me, I’m not. It’s just…fear of failure, I guess?

For indie developers, marketing is especially important. You gotta get the word out about your stuff. You gotta build your audience, refine your #brand, hack all dat growth, and so on and so forth. It feels gross. It isn’t, though—at least not for the most part. I struggle with it though, as I’m sure many of you do as well.

Look, my app isn’t special. It’s not Apple Design Award material. Does that mean it shouldn’t exist? No, it doesn’t mean that at all. I created something that’s useful to me, and now I’m going to share it with others. If they don’t find it useful, that’s fine. But if I want to give it its best chance at success, it’s still my job to tell its story.

But if you do…

Look, if you’re going to write about my app, say this: it’s a simple utility for merging short, portrait videos. It’s called SnapThread. It’s currently waiting for review.

It’s for parents who have a bajillion Snapchat videos of their kids with Marilyn Monroe hair or with a hot dog dancing on their head or what-have-you and all they want to do is create a sweet supercut of them all. No fancy filters or overlays or stickers or cropping. No dumb letter-boxing that forces it into landscape. Just stitch ’em all together and get on with your day.

It’s for travelers who have 30 Instagram Stories videos from their trip to Disney World spread over several days, and want to mash them all into one movie.

It was created by a mom who wanted to visualize how her son has grown.

SnapThread does what it says on the tin: it let’s you select portrait videos from your photo library that are 15 seconds or less, re-arrange them to your liking, merge them together, and save them to your photo library. Sometimes it takes a long time. Sometimes the videos have to be downloaded from iCloud. Sometimes their rotation has to be fixed before the merge can finish.

This isn’t an app for your home screen. This is an app you throw in your “Photo/Video Editing” folder and use once in awhile when you need it. It’s like “Clips,” but simpler.

SnapThread will be out soon. In the meantime, you can check out this page about it (the App Store link doesn’t work yet obviously).

Tell your friends! Or don’t, maybe. I don’t know.

Too Many AVPlayers?

Wow, I can’t believe it’s nearly September! For me that means I’m 1) popping allergy pills like a maniac because UGH RAGWEED, 2) getting really excited for the September Apple event, and 3) scrambling to finish up a random side-project app before iOS 11 hits the mainstream.

A couple nights ago I ran into a strange bug with my app, which uses AVFoundation to merge videos. Sometimes I would be able to export the final video with AVAssetExportSession and save it to my photo library, and sometimes it would randomly fail with the following error:

AVFoundationErrorDomain Code=-11839 "Cannot Decode" and NSLocalizedFailureReason=The decoder required for this media is busy., NSLocalizedRecoverySuggestion=Stop any other actions that decode media and try again., NSLocalizedDescription=Cannot Decode

In my app, every time a new video clip is added to the list of videos to be merged, I re-generate a preview of the final merged video. After merging the video, I set up an AVPlayer with the AVComposition like so:

func setupPlayerView(playerItem: AVPlayerItem?) {
        guard self.player == nil && playerItem != nil else { return }
        
        // Create an AVPlayer and AVPlayerLayer with the AVPlayerItem.
        self.player = AVPlayer(playerItem: playerItem)
        let playerLayer = AVPlayerLayer(player: player)
        
        // Configure the AVPlayerLayer and add it to the view.
        playerLayer.videoGravity = AVLayerVideoGravity.resizeAspect
        playerLayer.frame = (self.videoPreviewView?.bounds)!
        self.videoPreviewView?.layer.addSublayer(playerLayer)
        
        ...
}

I always made sure to set the AVPlayer to nil before re-generating the preview, so I couldn’t figure out why there would be any other “actions that decode media.” A trip to Stack Overflow revealed a possible platform limitation on the number of video “render pipelines” shared between apps on the device. It turns out that setting the AVPlayer to nil does not free up a playback pipeline and that it is actually the association of a playerItem with a player that creates the pipeline in the first place. Since developers don’t seem to have any control over when these resources are released, I knew I’d have to figure out another solution.

In the end, I decided to initialize the view controller’s AVPlayer right off the bat with its playerItem set to nil. Then I changed my setup function like so:

        // Create an AVPlayer and AVPlayerLayer with the AVPlayerItem.
        self.player?.replaceCurrentItem(with: playerItem)
        let playerLayer = AVPlayerLayer(player: player)

Replacing the player’s playerItem instead of initializing it with a new playerItem each time (even though the player was previously set to nil), seems to prevent that weird “cannot decode” error (so far, at least). I’d like to know more about this error and why exactly it occurs, just out of nerdy curiosity.

Anyway, I hope this helps somebody out!