Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Export audiofiles via “open in:” from Voice Memos App

Tags:

I have the exact same issue as "Paul" posted here: Can not export audiofiles via "open in:" from Voice Memos App - no answers have yet been posted on this topic.

Essentially what I'm trying to do is simple: After having recorded a Voice Memo on iOS, I select "Open With" and from the popup that is shown I want to be able to select my app.

I've tried everything I can think of and experimented with LSItemContentTypes without success.

Unfortunately I don't have enough reputation to comment on the existing post above, and I'm getting quite desperate for a solution to this. Any help is hugely appreciated, even just to know whether it's doable or not.

Thanks!

like image 219
timrichardsn Avatar asked Apr 21 '16 08:04

timrichardsn


People also ask

How do I export audio from voice memo?

In the Voice Memos app, tap ••• below the Voice Memo you want to convert, then tap Save to Files and a destination (like Music). Otherwise, your Voice Memos save inside that app and it can be difficult to find this file when using the converting app.

What format does iPhone save voice memos?

The MPEG-4 codec used to record and play back audio files in the iPhone's Voice Memo app is compressed using the Advanced Audio Coding (AAC) codec or the Apple Lossless Audio Codec (ALAC). Because the Voice Memo app only records audio data, the ". M4A" file extension is used, rather than the ". MP4" file extension.


1 Answers

After some experimentation and much guidance from this blog post ( http://www.theappguruz.com/blog/share-extension-in-ios-8 ), it appears that it is possible to do this using a combination of app extensions (specifically an Action Extension) and app groups. I'll describe the first part which will enable you to get your recording from Voice Memos to your app extension. The second part -- getting the recording from the app extension to the containing app (your "main" app) -- can be done using app groups; please consult the blog post above for how to do this.

  1. Create a new target within your project for the app extension, by selecting File > New > Target... from Xcode's menu. In the dialog box that prompts you to "Choose a template for your new target:" choose the "Action Extension" and click "Next".

CAUTION: Do not choose the "Share Extension" as is done in the blog post example above. That approach is more appropriate for sharing with another user or posting to a website.

  1. Fill in the "Product Name:" for your Action Extension, e.g., MyActionExtension. Also, for "Action Type:" I selected "Presents User Interface" because this is the way Dropbox appears to do it. Selecting this option adds a view controller (ActionViewController) and storyboard (Maininterface.storyboard) to your app extension. The view controller is a good place to provide feedback to the user and to give the user an opportunity to rename the audio file before exporting it to your app.

  2. Click "Finish." You will be prompted to "Activate “MyActionExtension” scheme?". Click "Activate" and this new scheme will be made active. Building it will build both the action extension and the containing app.

  3. Click the disclosure triangle for the "MyActionExtension" folder in the Project Navigator (Cmd-0) to reveal the newly-created storyboard, ActionViewController source file(s), and Info.plist. You will need to customize these files for your needs. But for now ...

  4. Build and run the scheme you just created. You will be prompted to "Choose an app to run:". Select "Voice Memos" from the list and click "Run". (You will probably need a physical device for this; I don't think the simulator has Voice Memos on it.) This will build and deploy your action extension (and its containing app) to your device. and then proceed to launch "Voice Memos" on your device. If you now make a recording with "Voice Memos" and then attempt to share it, you should see your action extension (with a blank icon) in the bottom row. If you don't see it there, tap on the "More" button in that row and set the switch for your action extension to "On". Tapping on your action extension will just bring up an empty view with a "Done" button. The template code looks for an image file, and finding none does nothing. We'll fix this in the next step.

  5. Edit ActionViewController.swift to make the following changes:

6a. Add import statements for AVFoundation and AVKit near the top of the file:

// the next two imports are only necessary because (for our sample code)
// we have chosen to present and play the audio in our app extension.
// if all we are going to be doing is handing the audio file off to the
// containing app (the usual scenario), we won't need these two frameworks
// in our app extension.
import AVFoundation
import AVKit

6b. Replace the entirety of override func viewDidLoad() {...} with the following:

override func viewDidLoad() {
  super.viewDidLoad()

  // Get the item[s] we're handling from the extension context.

  // For example, look for an image and place it into an image view.
  // Replace this with something appropriate for the type[s] your extension supports.

  print("self.extensionContext!.inputItems = (self.extensionContext!.inputItems)")

  var audioFound :Bool = false
  for inputItem: AnyObject in self.extensionContext!.inputItems {
    let extensionItem = inputItem as! NSExtensionItem
    for attachment: AnyObject in extensionItem.attachments! {
      print("attachment = \(attachment)")
      let itemProvider = attachment as! NSItemProvider
      if itemProvider.hasItemConformingToTypeIdentifier(kUTTypeMPEG4Audio as String)
        //|| itemProvider.hasItemConformingToTypeIdentifier(kUTTypeMP3 as String)
        // the audio format(s) we expect to receive and that we can handle
      {
        itemProvider.loadItemForTypeIdentifier(kUTTypeMPEG4Audio as String,
            options: nil, completionHandler: { (audioURL, error) in
          NSOperationQueue.mainQueue().addOperationWithBlock {

            if let audioURL = audioURL as? NSURL {

              // in our sample code we just present and play the audio in our app extension
              let theAVPlayer :AVPlayer = AVPlayer(URL: audioURL)
              let theAVPlayerViewController :AVPlayerViewController = AVPlayerViewController()
              theAVPlayerViewController.player = theAVPlayer
              self.presentViewController(theAVPlayerViewController, animated: true) {
                theAVPlayerViewController.player!.play()
              }

            }
          }
        })

        audioFound = true
        break
      }
    }

    if (audioFound) {
      break  // we only handle one audio recording at a time, so stop looking for more
    }
  }
}

6c. Build and run as in the previous step. This time, tapping on your action extension will bring up the same view controller as before but now overlaid with the AVPlayerViewController instance containing and playing your audio recording. Also, the two print() statements I've inserted in the code should give output that looks something like the following:

self.extensionContext!.inputItems = [<NSExtensionItem: 0x127d54790> - userInfo: {
    NSExtensionItemAttachmentsKey =     (
        "<NSItemProvider: 0x127d533c0> {types = (\n    \"public.file-url\",\n    \"com.apple.m4a-audio\"\n)}"
    );
}]
attachment = <NSItemProvider: 0x127d533c0> {types = (
    "public.file-url",
    "com.apple.m4a-audio"
)}
  1. Make the following changes to the action extension's Info.plist file:

7a. The Bundle display name defaults to whatever name you gave your action extension (MyActionExtension in this example). You might wish to change this to Save to MyApp. (By way of comparison, Dropbox uses Save to Dropbox.)

7b. Insert a line for the key CFBundleIconFile and set it to Type String (2nd column), and set its value to MyActionIcon or some such. You will then need to provide the corresponding 5 icon files. In our example, these would be: MyActionIcon.png, [email protected], [email protected], MyActionIcon~ipad.png, and MyActionIcon@2x~ipad.png. (These icons should be 60x60 points for iphone and 76x76 points for ipad. Only the alpha channel is used to determine which pixels are gray, the RGB channels are ignored.) Add these icon files to your app extension's bundle, NOT the containing app's bundle.

7c. At some point you will need to set the value for the key NSExtension > NSExtensionAttributes > NSExtensionActivationRule to something other than TRUEPREDICATE. If you want your action extension to only be activated for audio files, and not for video files, pdf files, etc., this is where you would specify such a predicate.

The above takes care of getting the audio recording from Voice Memos to your app extension. Below is an outline of how to get the audio recording from the app extension to the containing app. (I'll flesh it out later, time permitting.) This blog post ( http://www.theappguruz.com/blog/ios8-app-groups ) might also be useful.

  1. Set up your app to use App Groups. Open the Project Navigator (Cmd-0) and click on the first line to show your project and targets. Select the target for your app, click on the "Capabilities" tab, look for the App Groups capability, and set its switch to "On". Once the various entitlements have been added, click on the "+" sign to add your App Group, giving it a name like group.com.mycompany.myapp.sharedcontainer. (It must begin with group. and should probably use some form of reverse-DNS naming.)

  2. Repeat the above for your app extension's target, giving it the same name as above (group.com.mycompany.myapp.sharedcontainer).

  3. Now you can write the url of the audio recording to the app group's shared container from the app extension side. In ActionViewController.swift, replace the code fragment that instantiates and presents the AVPlayerViewController with the following:

    let sharedContainerDefaults = NSUserDefaults.init(suiteName:
        "group.com.mycompany.myapp.sharedcontainer")  // must match the name chosen above
    sharedContainerDefaults?.setURL(audioURL, forKey: "SharedAudioURLKey")
    sharedContainerDefaults?.synchronize()
    
  4. Similarly, you can read the url of the audio recording from the containing app's side using something like this:

    let sharedContainerDefaults = NSUserDefaults.init(suiteName:
        "group.com.mycompany.myapp.sharedcontainer")  // must match the name chosen above
    let audioURL :NSURL? = sharedContainerDefaults?.URLForKey("SharedAudioURLKey")
    
  5. From here, you can copy the audio file into your app's sandbox, e.g., your app's Documents directory or your app's NSTemporaryDiretory(). Read this blog post ( http://www.atomicbird.com/blog/sharing-with-app-extensions ) for ideas on how to do this in a coordinated fashion using NSFileCoordinator.

References:

Creating an App Extension

Sharing Data with Your Containing App

like image 126
inwit Avatar answered Sep 28 '22 03:09

inwit