Building a Granular Synth in Swift, Part 5: Sharing Sounds

Now is a good time to do some testing and see if any bugs creep up. One issue is doing a modulo by grain length rather than source buffer length when updating grain position: fix position bug · thestrangeagency/GrainSwift@46157c9 · GitHub. Also in our WaveView it’s probably preferable not to wander off the end of the audio buffer: modulo for end of array · thestrangeagency/GrainSwift@7d0edc3 · GitHub.

We are still a bit of a walled garden, so let’s open the app up to accepting audio files from other apps. A quick way to do this is to register as an app that opens audio files. We can do that by editing the info.plist and adding a CFBundleDocumentTypes key: open audio files from other apps · thestrangeagency/GrainSwift@3e192b8 · GitHub. I confess this was a copy-paste from an earlier app, and we’ll want to revisit the icons in CFBundleTypeIconFiles at a later point. Also, the plist need not be edited directly, as Xcode has some UI for adding document types.

Edit: Quickly got some nice icons wrapped up in an assets folder from https://appicon.co. The CFBundleTypeIconFiles didn't seem to do much, so it was deleted: sort out the icon situation · thestrangeagency/GrainSwift@3bcab17 · GitHub.

This will let us use, for example, the Voice Memos app to record audio and use it in our own. The UX for this is a little fiddly so let’s have a look.

IMG 9583

Tapping the three dot action icon brings up an action sheet with a Share button. That brings up potentially a ton of sharing options, depending on what apps you have installed, and we’ll want to swipe right through all the available apps to More.

IMG 9580

Thereupon, we can scroll down, find our app, and use the plus icon to make it a favorite for future shares.

IMG 9581

Once it’s made a favorite as below, it will show up in the list of apps to share audio with.

IMG 9582

Of course we still need to somehow receive the share in our app. The OS will send us a URL from which to grab the shared audio. SwiftUI now makes handling this handoff most pleasant with an .opOpenURL handler that comes in quite handy.

var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(audio)
                .onOpenURL { url in
                    // do your thing
                }
        }
    }

We’ll have to do some refactoring in our AudioSource to deal with loading URLs, and while we’re in there, let’s save a copy of any opened file locally, so we can default to opening it next time the app runs. This took a few commits, but here is the result: GrainSwift/AudioSource.swift at de7019ff3aed633a5d45da49f26aa648dc2a790b · thestrangeagency/GrainSwift · GitHub.

As we’ve added a significant new feature, let’s go ahead and tag this build: GrainSwift/GrainSwift at v0.0.14 · thestrangeagency/GrainSwift · GitHub.