Audio file not playing for iOS 8 swift - audio

This is the code i'm using:
var aSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("abc", ofType: "mp3"))
var audioPlayer = AVAudioPlayer()
println(aSound)
audioPlayer = AVAudioPlayer(contentsOfURL: aSound, error: nil)
audioPlayer.prepareToPlay()
audioPlayer.play()
I'm trying to print the path of the file and this is what I see in the console.
file:///Users/<username>/Library/Developer/CoreSimulator/Devices/<some GUID>/data/Containers/Bundle/Application/<some different GUID>/HelloWorld.app/abc.mp3
I have added the mp3 to my project by doing an "Add Files to project" from the menu.
Does it get added to the location above automatically when I run the simulator?

import UIKit
import AVFoundation
class ViewController: UIViewController {
var audioPlayer = AVAudioPlayer()
override func viewDidLoad() {
super.viewDidLoad()
var alertSound = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("fileName", ofType: "extension"))
println(alertSound)
AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback, error: nil)
AVAudioSession.sharedInstance().setActive(true, error: nil)
var error:NSError?
audioPlayer = AVAudioPlayer(contentsOfURL: alertSound, error: &error)
audioPlayer.prepareToPlay()
audioPlayer.play()
}
}

var audio: AVAudioPlayer = AVAudioPlayer()
#IBAction func playaudio(sender: AnyObject) //method for playing audio {
var audioPath = NSString(string: NSBundle.mainBundle().pathForResource("bus", ofType: "mp3")!)
var error: NSError? = nil
//instantiate the player
audio = AVAudioPlayer(contentsOfURL: NSURL(string: audioPath as String), error: &error)
audio.prepareToPlay()
audio.play()
}
override func viewDidLoad()
{
super.viewDidLoad()
// Do any additional setup after loading the view.
}
And don't forget to import AVFoundation Framework.

Use This Function to make sound in Swift (You can use this function where you want to make sound.)
First Add SpriteKit Framework.
import SpriteKit
func playEffectSound(filename: String){
runAction(SKAction.playSoundFileNamed("\(filename)", waitForCompletion: false))
}
// use this function to play sound
playEffectSound("Sound File Name With Extension")
// Example :- playEffectSound("BS_SpiderWeb_CollectEgg_SFX.mp3")

Related

Using AVAudioPlayer in background

In my app, I've added the capabilities of background audio and background processing.
My code presently uses AVAudioPlayer to play audio. While playback is good when the app in the foreground, with a locked screen, the audio is has some static jitteriness to it.
My app is written using SwiftUI and Combine. Has anyone encountered this issue and what would you suggest as a workaround?
Here is the play method:
/// Play an `AudioFile`
/// - Parameters:
/// - audioFile: an `AudioFile` struct
/// - completion: optional completion, default is `nil`
func play(_ audioFile: AudioFile,
completion: (() -> Void)? = nil) {
if audioFile != currentAudioFile {
resetPublishedValues()
}
currentAudioFile = audioFile
setupCurrentAudioFilePublisher()
guard let path = Bundle.main.path(forResource: audioFile.filename, ofType: "mp3") else {
return
}
let url = URL(fileURLWithPath: path)
// everybody STFU
stop()
do {
// make sure the sound is one
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
// instantiate instance of AVAudioPlayer
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
// play the sound
let queue = DispatchQueue(label: "audioPlayer", qos: .userInitiated, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
queue.async {
self.audioPlayer.play()
}
audioPlayer.delegate = self
} catch {
// Not much to go wrong, so leaving alone for now, but need to make `throws` if we handle errors
print(String(format: "play() error: %#", error.localizedDescription))
}
}
This is the class definition:
import AVFoundation
import Combine
import Foundation
/// A `Combine`-friendly wrapper for `AVAudioPlayer` which utilizes `Combine` `Publishers` instead of `AVAudioPlayerDelegate`
class CombineAudioPlayer: NSObject, AVAudioPlayerDelegate, ObservableObject {
static let sharedInstance = CombineAudioPlayer()
private var audioPlayer = AVAudioPlayer()
/*
FIXME: For now, gonna leave this timer on all the time, but need to refine
down the road because it's going to generate a fuckload of data on the
current interval.
*/
// MARK: - Publishers
private var timer = Timer.publish(every: 0.1,
on: RunLoop.main,
in: RunLoop.Mode.default).autoconnect()
#Published public var currentAudioFile: AudioFile?
public var isPlaying = CurrentValueSubject<Bool, Never>(false)
public var currentTime = PassthroughSubject<TimeInterval, Never>()
public var didFinishPlayingCurrentAudioFile = PassthroughSubject<AudioFile, Never>()
private var cancellables: Set<AnyCancellable> = []
// MARK: - Initializer
private override init() {
super.init()
// set it up with a blank audio file
setupPublishers()
audioPlayer.setVolume(1.0, fadeDuration: 0)
}
// MARK: - Publisher Methods
private func setupPublishers() {
timer.sink(receiveCompletion: { completion in
// TODO: figure out if I need anything here
// Don't think so, as this will always be initialized
},
receiveValue: { value in
self.isPlaying.send(self.audioPlayer.isPlaying)
self.currentTime.send(self.currentTimeValue)
})
.store(in: &cancellables)
didFinishPlayingCurrentAudioFile.sink(receiveCompletion: { _ in
},
receiveValue: { audioFile in
self.resetPublishedValues()
})
.store(in: &cancellables)
}
private func setupCurrentAudioFilePublisher() {
self.isPlaying.send(false)
self.currentTime.send(0.0)
}
// MARK: - Playback Methods
/// Play an `AudioFile`
/// - Parameters:
/// - audioFile: an `AudioFile` struct
/// - completion: optional completion, default is `nil`
func play(_ audioFile: AudioFile,
completion: (() -> Void)? = nil) {
if audioFile != currentAudioFile {
resetPublishedValues()
}
currentAudioFile = audioFile
setupCurrentAudioFilePublisher()
guard let path = Bundle.main.path(forResource: audioFile.filename, ofType: "mp3") else {
return
}
let url = URL(fileURLWithPath: path)
// everybody STFU
stop()
do {
// make sure the sound is one
try AVAudioSession.sharedInstance().setCategory(.playback, mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
// instantiate instance of AVAudioPlayer
audioPlayer = try AVAudioPlayer(contentsOf: url)
audioPlayer.prepareToPlay()
// play the sound
let queue = DispatchQueue(label: "audioPlayer", qos: .userInitiated, attributes: .concurrent, autoreleaseFrequency: .inherit, target: nil)
queue.async {
self.audioPlayer.play()
}
audioPlayer.delegate = self
} catch {
// Need to make `throws` if we handle errors
print(String(format: "play error: %#", error.localizedDescription))
}
}
func stop() {
audioPlayer.stop()
resetPublishedValues()
}
private func resetPublishedValues() {
isPlaying.send(false)
currentTime.send(0.0)
}
private var currentTimeValue: TimeInterval {
audioPlayer.currentTime
}
/// Use the `Publisher` to determine when a sound is done playing.
/// - Parameters:
/// - player: an `AVAudioPlayer` instance
/// - flag: a `Bool` indicating whether the sound was successfully played
func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
if let currentAudioFile = currentAudioFile {
didFinishPlayingCurrentAudioFile.send(currentAudioFile)
}
resetPublishedValues()
}
}
So I got it figured out. I had a few issues to contend with. Basically, I needed to play audio files at a specific time when the app was in the background. While this works fine if the sound is playing when the app is active, AVAudioPlayer won't let me start something after the app is in the background if audio playback is not already in progress.
I won't go into the nitty gritty details, but I ended up making use of AVQueuePlayer, which I initialized as part of my CombineAudioPlayer class.
Update AppDelegate.swift
I added the following lines to AppDelegate's didFinishLaunchingWithOptions method.
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
do {
try AVAudioSession.sharedInstance().setCategory(.playback,
mode: .default)
try AVAudioSession.sharedInstance().setActive(true)
} catch {
print(String(format: "didFinishLaunchingWithOptions error: %#", error.localizedDescription))
}
return true
}
In my AudioPlayer class, I declared an AVQueuePlayer. It is critical this be initialized with the AudioPlayer class, not inside of a method.
My ViewModel subscribes to a notification that listens for the app about to exit the foreground, it quickly generates a playlist and fires it just before the app exits.
NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification).sink { _ in
self.playBackground()
}
.store(in: &cancellables)
private var bgAudioPlayer = AVQueuePlayer()
Then, I created a method to generate a playlist for the AVQueuePlayer that looks something like this:
func backgroundPlaylist(from audioFiles: [AudioFile]) -> [AVPlayerItem] {
guard let firstFile = audioFiles.first else {
// return empty array, don't wanna unwrap optionals
return []
}
// declare a silence file
let silence = AudioFile(displayName: "Silence",
filename: "1sec-silence")
// start at zero
var currentSeconds: TimeInterval = 0
var playlist: [AVPlayerItem] = []
// while currentSeconds is less than firstFile's fire time...
while currentSeconds < firstFile.secondsInFuture {
// add 1 second of silence to the playlist
playlist.append(AVPlayerItem(url: silence.url!))
// increment currentSeconds and we loop over again, adding more silence
currentSeconds += 1
}
// once we're done, add the file we want to play
playlist.append(AVPlayerItem(url: audioFiles.first!.url!))
return playlist
}
Lastly, the sound is played as follows:
func playInBackground() {
do {
// make sure the sound is one
try AVAudioSession.sharedInstance().setCategory(.playback,
mode: .default,
policy: .longFormAudio,
options: [])
try AVAudioSession.sharedInstance().setActive(true)
let playlist = backgroundPlaylist(from: backgroundPlaylist)
bgAudioPlayer = AVQueuePlayer(items: playlist)
bgAudioPlayer.play()
} catch {
// Not much to mess up, so leaving alone for now, but need to make
// `throws` if we handle errors
print(String(format: "playInBackground error: %#",
error.localizedDescription))
}
}

Multithreading in Swift 3 with GCD

I'm working on a webservice based app and I've come to a crashing hault due to sending requests too quickly to the webservice. I simply can not getting GCD working in Swift 3 and I'm scratching my head. I've decided to dumb it down and just try loading 4 web images to a web view in order. Based on everything I'm seeing online the following code should work, but it is still freezing the UI until all four images load. What am I doing wrong?
import UIKit
let imageURLs = ["http://www.planetware.com/photos-large/F/france-paris-eiffel-tower.jpg", "http://adriatic-lines.com/wp-content/uploads/2015/04/canal-of-Venice.jpg", "http://hd-wall-papers.com/images/wallpapers/hi-resolution-pictures/hi-resolution-pictures-5.jpg", "http://hd-wall-papers.com/images/wallpapers/hi-resolution-pictures/hi-resolution-pictures-1.jpg"]
class Downloader {
class func downloadImageWithURL(_ url:String) -> UIImage! {
let data = try? Data(contentsOf: URL(string: url)!)
return UIImage(data: data!)
}
}
class ViewController: UIViewController {
#IBOutlet weak var imageView1: UIImageView!
#IBOutlet weak var imageView2: UIImageView!
#IBOutlet weak var imageView3: UIImageView!
#IBOutlet weak var imageView4: UIImageView!
#IBOutlet weak var sliderValueLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func didClickOnStart(_ sender: AnyObject) {
let serialQueue = DispatchQueue(label: "syncQueue")
serialQueue.sync{
let img1 = Downloader.downloadImageWithURL(imageURLs[0])
DispatchQueue.main.async(execute: {
self.imageView1.image = img1
})
}
serialQueue.sync{
let img2 = Downloader.downloadImageWithURL(imageURLs[1])
DispatchQueue.main.async(execute: {
self.imageView2.image = img2
})
}
serialQueue.sync{
let img3 = Downloader.downloadImageWithURL(imageURLs[2])
DispatchQueue.main.async(execute: {
self.imageView3.image = img3
})
}
serialQueue.sync{
let img4 = Downloader.downloadImageWithURL(imageURLs[3])
DispatchQueue.main.async(execute: {
self.imageView4.image = img4
})
}
}
#IBAction func sliderValueChanged(_ sender: UISlider) {
self.sliderValueLabel.text = "\(sender.value * 100.0)"
}
}
freezing the UI
Because you are calling serialQueue.sync. You almost never want to call sync, and in this case you certainly don't. Use async instead.

Thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)

I have built a fully functioning flappy bird game but when I try to add a sound I can't even run the project because an error pops up before I can play the game. the error is:
thread 1 exc_bad_instruction (code=exc_i386_invop subcode=0x0)
import SpriteKit
import AVFoundation
class GameScene: SKScene {
var bird = SKSpriteNode()
var pipeUpTexture = SKTexture()
var pipeDownTexture = SKTexture()
var PipesMoveAndRemove = SKAction()
var sound = try? AVAudioPlayer(contentsOfURL: NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("wingsSound", ofType: "waw")!))
let pipeGap = 150.0
override func didMoveToView(view: SKView) {
/* Setup your scene here */
sound!.play()

Core Data in Swift

Trying to work with Core Data in Swift. Found this one example:
http://www.sep.com/sep-blog/2014/06/23/a-c-developer-learns-swift-part-1-core-data/
Created Entity "Person" with two string fields - lastname and firstname. Created UITableViewController (MainTableViewController) to display records on the screen. Created UIViewController (DetailViewController) to add new records. Created my own class (AddrBook) for entity data.
Does not work display the records contained in the entity in main class - MainTableViewController.
My class AddrBook.swift:
import UIKit
import CoreData
#objc(AddrBook)
class AddrBook: NSManagedObject {
#NSManaged var lastname:String
#NSManaged var firstname:String
}
UIViewController to add new records. DetailViewController.swift:
import UIKit
import CoreData
class DetailViewController: UIViewController {
#IBOutlet var lastNameField : UITextField = nil
#IBOutlet var firstNameField : UITextField = nil
init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) {
super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func saveButtonPressed(sender : AnyObject) {
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
let projectEntity = NSEntityDescription.entityForName("Person", inManagedObjectContext: context)
var newPerson = AddrBook(entity: projectEntity, insertIntoManagedObjectContext: context)
newPerson.lastname = lastNameField.text
newPerson.firstname = firstNameField.text
context.save(nil)
self.dismissViewControllerAnimated(true, completion: nil)
}
}
Theoretically, in this class all goes well. Entry must be added.
The main class MainTableViewController.swift. To display the records. Trying to get them through the NSLog:
import UIKit
import CoreData
class MainTableViewController: UITableViewController {
init(style: UITableViewStyle) {
super.init(style: style)
}
init(coder aDecoder: NSCoder!) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
var results:NSArray = context.executeFetchRequest(request, error: nil)
for currentPerson in results as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
return 1
}
override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
return 0
}
}
Shows that there is an error in the expression
for currentPerson in results as AddrBook[] {
Error:
Cannot convert the expression's type 'AddrBook[]' to type 'AddrBook[]'
What am I doing wrong?
for LombaX:
override func viewDidLoad() {
super.viewDidLoad()
let request = NSFetchRequest(entityName: "Person")
request.returnsObjectsAsFaults = false
let appDelegate:AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
let context:NSManagedObjectContext = appDelegate.managedObjectContext
var results : AddrBook[]? = context.executeFetchRequest(request, error: nil) as? AddrBook[]
NSLog("\(results)")
if let array = results // check for nil and unwrap
{
for currentPerson in array as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}
}
// var results:NSArray = context.executeFetchRequest(request, error: nil)
/*for currentPerson in results as AddrBook[] {
NSLog("\(currentPerson.lastname)")
NSLog("\(currentPerson.firstname)")
}*/
}
Output NSLog - 2014-06-24 21:25:41.243 lesson-12-swift[1651:136375] nil
Variable results is nil :-(
Link to project in Dropbox: https://www.dropbox.com/s/q42rw5fw470timi/lesson-12-swift.zip
First, check that you filled the class in the data model:
As ProjectName.AddrBook (for swift classes you have to specify even the project name). (NOTE: this is needed only if you haven't used the prefix #objc(AddrBook) before the class, but I see that you used it, so this is not the problem).
or
as AddrBook as in this image in the Class section, top right
Moreover, change your cast like these:
// since executeFetchRequest can return nil, cast it as an optional array of [AddrBook]
// note: the first [AddrBook]? Can be omitted
var results : [AddrBook]? = context.executeFetchRequest(request, error: nil) as? [AddrBook]
if let array = results // check for nil and unwrap
{
for currentPerson in array as [AddrBook] {
// print
}
}
Or, less explicit and no check for nil
var results = context.executeFetchRequest(request, error: nil)
for currentPerson in results as [AddrBook] {
// print
}
let arrayresult = context!.executeFetchRequest(request, error: &error)
var arrayvalues=NSArray(array: arrayresult!)
for obj in arrayvalues as [AddrBook]
{
}

Can't Unwrap Optional.None when using CoreData in Swift

Ok so the problem actually occurs once the code bit var context: NSManagedObjectContext = appDel.managedObjectContext is run I commented it out to confirm that it was that line and it was please note this is my first time learning iOS programming so please try to be as specific as possible in your answer thank you :)
import UIKit
import CoreData
class SecondViewController: UIViewController, UITextFieldDelegate {
#IBOutlet var txtName : UITextField
#IBOutlet var txtDesc : UITextField
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func touchesBegan(touches: NSSet!, withEvent event: UIEvent!) {
self.view.endEditing(true)
}
#IBAction func hitAdd(sender : UIButton) {
glTask.newTask(txtName.text, desc: txtDesc.text)
txtName.text = ""
txtDesc.text = ""
self.view.endEditing(true)
self.tabBarController.selectedIndex = 0
var appDel: AppDelegate = (UIApplication.sharedApplication().delegate as AppDelegate)
Right here V
var context: NSManagedObjectContext = appDel.managedObjectContext
This crashes the app once button is pressed ^
The code error message is fatal error Cant unwrap Optional.None
var newTask = NSEntityDescription.insertNewObjectForEntityForName("Tasks", inManagedObjectContext: context) as NSManagedObject
newTask.setValue("test task", forKey: "myTask")
newTask.setValue("test Description", forKey: "myDesc")
context.save(nil)
//println(newTask)
println("Task was saved.")
}
// UITextField Delegate
func textFieldShouldReturn(textField: UITextField!) -> Bool {
textField.resignFirstResponder()
return true
}
}
Looking at the Core Data stack in Swift, managedObjectContext is implemented like this:
var managedObjectContext: NSManagedObjectContext {
if !_managedObjectContext {
let coordinator = self.persistentStoreCoordinator
if coordinator != nil {
_managedObjectContext = NSManagedObjectContext()
_managedObjectContext!.persistentStoreCoordinator = coordinator
}
}
return _managedObjectContext!
}
var _managedObjectContext: NSManagedObjectContext? = nil
As you can see it is backed by an Optional.
The place where this can go wrong is here:
_managedObjectContext = NSManagedObjectContext()
_managedObjectContext!.persistentStoreCoordinator = coordinator
if NSManagedObjectContext() returns a nil, then the backing _managedObjectContext will be nil and you will get this crash at the line where you unwrap it return _managedObjectContext!
To debug this, dig deeper down the stack, its most likely failing to initialize the object model or persistant store, and thus returning nil to you.
Edit:
In the definiton of the getter for var persistentStoreCoordinator: NSPersistentStoreCoordinator
They provide a location (the wall of comments) where you should debug this exact type of issue.
Not sure if OP ever figured this out, but I had a similar issue and realized that the code I copied from another app's AppDelegate was using the project name of that app and that I had forgot to change this line: let modelURL = NSBundle.mainBundle().URLForResource("CoreData", withExtension: "momd") to use "CoreData" instead of the "test" it had from another project.

Resources