How do I share an Audio File in an App using Swift 3? - audio

Sharing an Audio File in Swift
How do I share an audio file which exists in my apps document directory to other apps?
To elaborate on this question, what I mean is when a user taps a share button in the app they should be able to email their recorded audio track to another person, or alternatively to be able to send it across to a range of other apps which can handle audio like perhaps soundcloud.
Researching the topic, I have found:
UIActivityViewController
UIDocumentInteractionController
Since my application makes an audio recording of a person's voice which they should be able to share, and despite searching through stack overflow, I have not been able to find a code example of how exactly this option can be implemented in a Swift app. Can I request suggestions and example code on how this may be accomplished. Many Thanks,

Swift 3.x:
let activityItem = URL.init(fileURLWithPath: Bundle.main.path(forResource: "fileName", ofType: "mp3")!)
let activityVC = UIActivityViewController(activityItems: [activityItem],applicationActivities: nil)
activityVC.popoverPresentationController?.sourceView = self.view
self.present(activityVC, animated: true, completion: nil)

My answer is using for doing this with UIDocumentInteractionController.
I begin by instantiating a UIDocumentInteractionController at the top of my class
var controller = UIDocumentInteractionController()
Then I link up an IBAction to a share button on my nib or Storyboard:
#IBAction func SHARE(_ sender: Any) {
let dirPath: String = NSSearchPathForDirectoriesInDomains(.documentDirectory,
.userDomainMask,
true)[0]
let recordingName = UserDefaults.standard.string(forKey: "recordingName")
let pathArray: [String] = [dirPath, recordingName!]
let filePathString: String = pathArray.joined(separator: "/")
controller = UIDocumentInteractionController(url: NSURL(fileURLWithPath: filePathString) as URL)
controller.presentOpenInMenu(from: CGRect.zero,
in: self.view,
animated: true)
}

Related

How do I remove subtitles from vlckit?

I've written an app in swift that uses the vlckit framework. I have managed to get subtitles working by using
let url = URL(string: "file://Users/lonespeaker/test-subitltle.srt")
mediaPlayer.addPlaybackSlave(url, type: .subtitle, enforce: true)
but if the user wants to disable the subtitles after selecting one, I am stuck.
I have tried the following but had no luck
let url = URL(string: "")
mediaPlayer.addPlaybackSlave(url, type: .subtitle, enforce: true)
I've googled for an answer to remove the PlaybackSlave but there doesn't seem to be an API for it.
Here is an extract of my code :
guard let url = URL(string: "file://Users/lonespeaker/test-file.mkv") else { return }
let media = VLCMedia(url: url)
mediaPlayer.media = media
mediaPlayer.delegate = self
mediaPlayer.drawable = self.movieView
mediaPlayer.play()
then in the IBAction for disabling subtitles
#IBAction func disableSubtitles(_ sender: Any) {
let url = URL(string: "")
mediaPlayer.addPlaybackSlave(url, type: .subtitle, enforce: true)
}
I would have expected the PlaybackSlave to be changed/overwritten by the new URL and therefore disabling subtitles.
I would also expect an API call to disable a Playback Slave, but there doesn't appear to be one.
Anyone help? thanks.
Your approach is too complex. You can add an infinite number of playback slaves which indeed cannot be cleared using VLCKit at this point.
However, VLC will expose a list of available subtitles tracks during playback regardless of their source (you cannot differentiate between subtitles internal to the video and those added through an input slave), which you can iterate on and display to the user. Additionally, there is the player option to set the subtitles track ID you want to show. Those IDs can be queried from the API, too (note that the IDs can be 0, 1, 2, ... but it is perfectly legal to be 201, 4022, 8444, ..., too). However, to just disable, set this option to -1.
It looks like it's not exposed by vlckit, but libvlc does offer libvlc_media_slaves_clear.
You should try adding support for it in a local vlckit fork, and if it works, open a pull request. Other people might be interested.

Exporting Core Data structure to .csv file attached to mail XCODE 8.1 Swift 3

I'm making an app that needs to take a managed object array from core data and export it to a csv file that I plan to attach to an email being sent out using the mfMailComposer system. I have the data properly stored in the core data systems and the mail composer functionality seems to be working. I'm reaching a snag when I try to find the correct process by which to export the data.
I have already taken a long look at both of these posts attempting to determine a solution:
from 2012, seems very outdated:
how to export Core Data to CSV
from 2016, more recent, but swift 3 and Xcode 8 have since been released and I worry this has become outdated as well: How to create a CSV file from Core Data (swift)
I have been attempting to try the solutions proposed in the second link, but much of the code gets marked as incorrect when typing it, so I believe it is now obsolete with the upgrade.
The code below is based off of the second post and therefore, likely outdated, but in order to provide a reference of the process I am trying to accomplish...
// Called by the press of xcode UI button
#IBAction func ExportToCSV(_ sender: AnyObject)
{
// Make our mail composer controller and fill it with the proper information
let mailComposeViewController = configuredMailComposeViewController()
// If the composer is functioning properly ...
if MFMailComposeViewController.canSendMail()
{
// ... Present the generated mail composer controller
self.present(mailComposeViewController, animated: true, completion: nil)
}
else
{
// ... Otherwise, show why it is not working properly
self.showSendMailErrorAlert()
}
}
// Used to set up the body of the outgoing email
func configuredMailComposeViewController() -> MFMailComposeViewController
{
// Establish the controller from scratch
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
// Set preset information included in the email
mailComposerVC.setSubject("Generic email subject")
mailComposerVC.setMessageBody("Generic email body", isHTML: false)
// Turn core data for responses into a .csv file
// Pull core data in
var CoreDataResultsList = [NSManagedObject]()
// Register the proper delegate and managed context
let appDelegate =
UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
// Pull the data from core data
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ItemResponses")
do {
let results =
try managedContext!.fetch(fetchRequest)
CoreDataResultsList = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
// Take the managed object array and turn it into a .csv sring to write in the file
let csvString = writeCoreObjectsToCSV(objects: CoreDataResultsList, named: "Generic name")
let data = csvString.dataUsingEncoding(NSUTF8StringEncoding)
mailComposerVC.addAttachmentData(data, mimeType: "text/csv", fileName: "GenericFilename.csv")
return mailComposerVC
}
// Takes a managed object and writes it to the .csv file ..?
func writeCoreObjectsToCSV(objects: [NSManagedObject], named: String) -> String
{
// Make sure we have some data to export
guard objects.count > 0 else
{
return ""
}
let firstObject = objects[0]
let attribs = Array(firstObject.entity.attributesByName.keys)
// The attires.reduce function is throwing an error about originally using combine as in the second post, used auto fix, but noteworthy.
//Now gives an error that says "No '+' candidates produce the expected contextual result type NSString"
let csvHeaderString = (attribs.reduce("", {($0 as String) + "," + $1 }) as NSString).substringFromIndex(1) + "\n"
// This function says that substring from index has been renamed as well as a few other lines within it
let csvArray = objects.map({object in
(attribs.map({((object.valueForKey($0) ?? "NIL") as AnyObject).description}).reduce("",combine: {$0 + "," + $1}) as NSString).substringFromIndex(1) + "\n"
})
// Again with the reduce issue
let csvString = csvArray.reduce("", combine: +)
return csvHeaderString + csvString
}
New the bottom of the code I have commented in the multiple errors with the suggested code from the second post and the issues pertaining after I use xCode's auto-fix feature.
I would like to thank you in advance for helping me with this issue. I am merely looking for the most up-to-date way to export core data as a .csv file and send it out. Thanks!
I ended up working around it. At first I didn't understand how .csv files were written until I saw the acronym stood for "comma-separated values". Then it all clicked for me and I wrote my own, I did a more manual route for the head entry but the data on the following lines is still auto mated.
Below is the relevant functions in their new working form:
// Called by the press of xcode UI button
#IBAction func ExportToCSV(_ sender: AnyObject)
{
// Make our mail composer controller and fill it with the proper information
let mailComposeViewController = configuredMailComposeViewController()
// If the composer is functioning properly ...
if MFMailComposeViewController.canSendMail()
{
// ... Present the generated mail composer controller
self.present(mailComposeViewController, animated: true, completion: nil)
}
else
{
// ... Otherwise, show why it is not working properly
self.showSendMailErrorAlert()
}
}
// Used to set up the body of the outgoing email
func configuredMailComposeViewController() -> MFMailComposeViewController
{
// Establish the controller from scratch
let mailComposerVC = MFMailComposeViewController()
mailComposerVC.mailComposeDelegate = self
// Set preset information included in the email
mailComposerVC.setSubject("Generic Subject")
mailComposerVC.setMessageBody("Generic Email Body", isHTML: false)
// Turn core data for responses into a .csv file
// Pull core data in
var CoreDataResultsList = [NSManagedObject]()
// Register the proper delegate and managed context
let appDelegate =
UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.managedObjectContext
// Pull the data from core data
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: "ItemResponses")
do {
let results =
try managedContext!.fetch(fetchRequest)
CoreDataResultsList = results as! [NSManagedObject]
} catch let error as NSError {
print("Could not fetch \(error), \(error.userInfo)")
}
// Take the managed object array and turn it into a .csv sring to write in the file
// In doing this, we are writing just like we would to any string
let csvString = writeCoreObjectsToCSV(objects: CoreDataResultsList)
let data = csvString.data(using: String.Encoding.utf8.rawValue, allowLossyConversion: false)
mailComposerVC.addAttachmentData(data!, mimeType: "text/csv", fileName: "GenericFilename.csv")
return mailComposerVC
}
// Takes a managed object and writes it to the .csv file ..?
func writeCoreObjectsToCSV(objects: [NSManagedObject]) -> NSMutableString
{
// Make sure we have some data to export
guard objects.count > 0 else
{
return ""
}
var mailString = NSMutableString()
mailString.append("Generic Header 1, Generic Header 2, Generic Header 3")
for object in objects
{
// Put "\n" at the beginning so you don't have an extra row at the end
mailString.append("\n\(object.value(forKey: "Generic Core Data Key 1")!),\(object.value(forKey: "Generic Core Data Key 2")!), \(object.value(forKey: "Generic Core Data Key 3")!)")
}
return mailString
}
I'm having an issue where one of my keys is a string containing commas and need a proper way to escape from them. I've heard double quotes is how to do it but inserting them gave me no success.
Regardless, this is one current way to take core data into an array and write it to a string, save it to a .csv file and mail it out. This answers my question, but my next task is reading the data back in. I have no idea how to access the file to do that on an iPad. If you come across this and know how to do that, please let me know! I will probably be making another post on that topic if I can't find a solution and will then drop a new question and a link to that in the replies below this answer.
Thank you!

Swift 3 Fetch Request Error (warning: could not load any Objective-C class information)

I recently received this error when fetching data from Core Data:
warning: could not load any Objective-C class information. This will significantly reduce the quality of type information available.
(lldb)
Here is my code:
// MARK: - Initialize Fetch Request
var fetchedResultsController = NSFetchedResultsController<Profile>()
func setFetchRequest() -> NSFetchRequest<Profile> {
let request = Profile.fetchRequest()
let sortDescriptor = SortDescriptor(key: "title", ascending: false)
do {
try moc?.fetch(request)
} catch {
print("Error With Request: \(error)")
}
request.sortDescriptors = [sortDescriptor]
return setFetchRequest()
}
// MARK: - Retrieve Fetch Request
func getFetchRequest() -> NSFetchedResultsController<Profile> {
fetchedResultsController = NSFetchedResultsController(fetchRequest: setFetchRequest(), managedObjectContext: moc!, sectionNameKeyPath: nil, cacheName: nil)
return fetchedResultsController
}
I crashed with this error where I have "try moc?.fetch(request)":
Thread 1 EXC_BAD_ACCESS (code=2, address=0x16fc07feo)
Are these errors connected or is this a bug in Swift 3 / Xcode 8?
You shouldn't take results from the ManagedObjectContext. If you want to use a NSFetchedResultsController class in your app? You'll need to access their methods. And all of the required or optional methods are comes from the NSFetchedResultsControllerDelegate protocol.
Try this
class YourTableViewController: UITableViewController, NSFetchedResultsControllerDelegate {
var fetchedResultsController:NSFetchedResultsController<Profile>!
}
And then create a custom helper function like this one:
`func frc() {
let request:NSFetchRequest<Profile> = Profile.fetchRequest()
let sorter = SortDescriptor(key: "title", ascending: true)
request.sortDescriptors = [sorter]
self.fetchedResultsController = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
// make sure the delegate is set to self
self.fetchedResultsController.delegate = self
do {
try self.fetchedResultsController.performFetch()
} catch {}
}
`
From this point you'll need a trigger to perform operations. So let's the system itself should be doing this when you call the viewDidLoad method or you can create a button instead. For example click the button to begin operations.
override func viewDidLoad() {
super.viewDidLoad()
self.frc()
self.tableView.reloadData()
}
It should be works.
Good luck!
Automatic Subclass Generation
Xcode 8 and Swift 3 comes with a new generation of subclassing called as Automatic Subclass Generation! How to create it? Well! So let's create a new Xcode 8 project, choose a Single View Application and then another window will appears called Choose options for your new project:. Give the name for your new project, make sure language is a Swift and Use Core Data check box is checked and then hit Create.
Go to the YourProjectName.xcdatamodeld file and click it. And then add an entity! So let's say your entity name is a Profile and create their Attributes respectively. It's important to know, because this is an Automatic Subclass Generation. Choose your entity and go to the Data Model Inspector ! Choose a Class Definition for the Codegen You can find a Codegen from here.
After selected the Class Definition, you can see Name text field automatically filled by your entity name like so. Again go to the your entity and click it. Click Command + S for save changes firstly and then click Command + B for rebuild, that's it. Automatic Subclass Generation is successfully created.
Remember
If you want to change your model? For example: If you want to add a new Attribute to your model? It's very easy, select a xcdatamodeld file and click your entity. Click the plus sign under Attributes and add your new Attribute. After your changes is completed? Don't forget to save changes. Again click Command + S and then click Command + B
Create A Managed Object
In the Swift 3 you can create a ManagedObject by using subclass initializer. It's very easy to implementing than ever before
let managedObject = Profile(context: self.managedObjectContext!)
You can see it's very easy! How to save values to the managedObject ? So let's say you have a title attribute of your model. Also title is a String.
managedObject.setValue("Well crafted API? Right?", forKey: "title")
or
managedObject.title = "Well crafted API? Right?"
Save values:
do {
try self.managedObjectContext.save()
print(managedObject)
} catch {}
It's works well on the Swift 3.0 beta and Xcode 8.0 beta.
Update
So, this is what I got working for Xcode 8 beta and Swift 3 beta Core Data
var fetchedResultsControler = NSFetchedResultsController<Profile>()
func frc() {
let request: NSFetchRequest<Profile> = Profile.fetchRequest()
let sortDescriptor = SortDescriptor(key: "title", ascending: true)
request.sortDescriptors = [sortDescriptor]
self.fetchedResultsControler = NSFetchedResultsController(fetchRequest: request, managedObjectContext: self.moc!, sectionNameKeyPath: nil, cacheName: nil)
self.fetchedResultsControler.delegate = self
do {
try self.fetchedResultsControler.performFetch()
} catch {
print("Error Fetching Data: \(error)")
}
}
and in viewDidLoad I have self.frc() at the top of the viewDidLoad.
So, in my Profile+CoreDataProperties.swift I copied a method Apple uses in their Master-Detail example when you create a new project:
extension Profile {
#nonobjc class func fetchRequest() -> NSFetchRequest<Profile> {
return NSFetchRequest<Profile>(entityName: "Profile");
}
#NSManaged var title: String?
#NSManaged var titleImage: Data
}
so that my fetch request is "native to my function." Pretty sure that's not the correct way to say that but it's helping me understand what is going on. The fetch request in Apple's example is green instead of blue. And it took me forever to notice that. I clicked on "Event" in Apple's example, and was conveniently taken to the created subclass, which was demonstrated in the Xcode 8 video at WWDC.
The files for e.g. Event.swift and Event+CoreDataProperties.swift are not exposed like they are in Xcode 7.x.x and earlier. You have to click on the entity in the code and you'll be taken to them. Maybe that was my problem? Anyway, I'm fetching data and images like a champ. Thanks a lot for your help #Mannopson!

How to avoid the percent encoding in a string?

I would like to export some stuff from my app using an UIActivityViewController.
I have a NSURL among other things (which works perfectly when I choose to export to my reading list), but when I opt to send an email, the link that is printed in the email's body is messed up by the encoding.
Part of the link says: "...&origem=TODOS&regiao=QUALQUER..." which is converted to: "...&origem=TODOS®iao=QUALQUER...". This action makes my link a non-clickable one, and I must guarantee this action to the user.
How can I avoid this? Is there a way to not print the NSURL in the email body ?
Also, when I print the link to the console this problem does not occur.
Thank you in advance!
EDIT:
#IBAction func exportWebButton(sender: AnyObject) {
// some code here > generating testData.pdf
let activityController = UIActivityViewController(activityItems: [testData,NSURL(string:self.url)!], applicationActivities: nil)
print(NSURL(string:self.url)!) //> "...&origem=TODOS&regiao=QUALQUER..."
let presentationController = activityController.popoverPresentationController
presentationController?.barButtonItem = self.exportWebButtonO
self.presentViewController(activityController, animated: true, completion: nil)
}

Hide Now Playling Track Informations

is it possible, from an spotify application, to hide or overwrite the "now playing" pannel (the cover and artist name/track name of the playing track) ?
Thx, for your help.
I try to modify the Album object or the Track object provided to the player but it don't work :
models.Album.fromURI("spotify:album:3ty039P7JO7bTcWtWi1AP6", function(a) {
var player = new v.Player();
var track = a.get(0);
//a.artist = "TOTO";
//a.cover = "sp://import/img/placeholders/300-album.png";
//track.image = 'sp://import/img/placeholders/300-album.png';
track.name = "Test";
player.play(track,a);
});
This is not possible in the current API, mainly because we don't want people overwriting now playing information with adverts and stuff.

Resources