Event to play mp3 file in specific time when app closed or in background - ios

I have an application for Muslims prayer alert in IOS ,
Iam already playing mp3 file by using when i click a button
and this is my code
super.viewDidLoad()
let tapSound = NSBundle.mainBundle().URLForResource("mp", withExtension: "mp3")
self.soundFileURLRef = tapSound
do {
player = try AVAudioPlayer(contentsOfURL: soundFileURLRef)
} catch _ {
player = nil
}
player?.delegate = self
player?.prepareToPlay()
}
#IBAction func play(sender: AnyObject) {
NSLog("started playing")
player?.play()
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
//
NSLog("finished playing")
}
and it's working perfeclty
Now I am looking for a way to play mp3 file in specific time like
when time is 12:00 AM Play the mp3 file
even the application is closed
any suggestion how to make that in IOS ?
Thanks for help

To schedule events like this, you can use Local Notifications. It's how all (that I know of) alarm clock apps alert you when the app isn't open. Local notifications, however, only allow you to play a 30 sec (max) sound clip that you have bundled with your application.
Currently there is no way to have your app play music as a background service unless it's currently open (or was open when you locked the screen if you opt out of multitasking... see the above link).
The built-in alarm clock app is allowed to do this, however, because it is using a private API.

Local Notifications work as suggested here but you should keep in mind that there is no 100% guarantee the notification will be fired precisely when you schedule them. Depending on the workload, the OS can delay it a little while (it could change from seconds to minutes). Just wanted to point that out since prayer times are very precise ;)

Related

Programmatically trigger the action that a headphone pause button would do

I am trying to find a way to pause any playing media on the device, so I was thinking of triggering the same logic that is fired when a user press the headphone "middle button"
I managed to prevent music from resuming (after I pause it within my app, which basically start an AVAudioSession for recording) by NOT setting the AVAudioSession active property to false and leave it hanging, but I am pretty sure thats a bad way to do it. If I deactivate it the music resumes. The other option I am thinking of is playing some kind of silent loop that would "imitate" the silence I need to do. But I think if what I am seeking is doable, it would be the best approach as I understood from this question it cannot be done using the normal means
func stopAudioSession() {
let audioSession = AVAudioSession.sharedInstance(
do {
if audioSession.secondaryAudioShouldBeSilencedHint{
print("someone is playing....")
}
try audioSession.setActive(false, options: .notifyOthersOnDeactivation)
isSessionActive = false
} catch let error as NSError {
print("Unable to deactivate audio session: \(error.localizedDescription)")
print("retying.......")
}
}
In this code snippet as the function name implies I set active to false, tried to find other options but I could not find another way of stopping my recording session and prevent resume of the other app that was already playing
If someone can guide me to which library I should look into, if for example I can tap into the H/W part and trigger it OR if I can find out which library is listening to this button press event and handling the pause/play functionality
A friend of mine who is more experienced in IOS development suggested the following workaround and it worked - I am posting it here as it might help someone trying to achieve a similar behaviour.
In order to stop/pause what is currently being played on a user device, you will need to add a music player into your app. then at the point where you need to pause/stop the current media, you just initiate the player, play and then pause/stop it - simple :)
like so:
let musicPlayer = MPMusicPlayerApplicationController.applicationQueuePlayer
func stopMedia(){
MPMediaLibrary.requestAuthorization({(newPermissionStatus: MPMediaLibraryAuthorizationStatus) in
self.musicPlayer.setQueue(with: .songs())
self.musicPlayer.play()
print("Stopping music player")
self.musicPlayer.pause()
print("Stopped music player")
})
}
the part with MPMediaLibrary.requestAuthorization is needed to avoid an authorisation error when accessing user's media library.
and of course you will need to add the Privacy - Media Library Usage Description
key into your Info.plist file

Process the text once voice input is stopped from SFSpeechRecognizer

I am developing a Voice to Text application using iOS SFSpeechRecognizer API.
Found a great tutorial here: and it worked fine.
I wanted to process the text and perform some action as soon as the voice input is stopped. So, was curious whether there is a delegate method available for SFSpeechRecognizer which can recognise when the voice input is stopped so that I can capture the input and process further?
So, was curious whether there is a delegate method available for SFSpeechRecognizer which can recognise when the voice input is stopped so that I can capture the input and process further?
Not built into the SFSpeechRecognizer API, no. On the contrary, that is exactly why you must provide interface that allows the user to tell the recognizer that the input is finished (e.g. a Done button of some sort). Your app will be rejected if you omit that interface.
A possible solution maybe to use a third party library like FDSoundActivatedRecorder which start recording when sound is detected and
stops recording when the user is done talking.
Then you can use the recorded audio as in this link to convert it to text in a go.
func transcribeAudio(url: URL) {
// create a new recognizer and point it at our audio
let recognizer = SFSpeechRecognizer()
let request = SFSpeechURLRecognitionRequest(url: url)
// start recognition!
recognizer?.recognitionTask(with: request) { [unowned self] (result, error) in
// abort if we didn't get any transcription back
guard let result = result else {
print("There was an error: \(error!)")
return
}
// if we got the final transcription back, print it
if result.isFinal {
// pull out the best transcription...
print(result.bestTranscription.formattedString)
}
}
}

Chromecast Sleep/Background issue in iOS app

I am facing a very big issue while using Chromecast in my application. I am using normal GCKUICastButton to connect to the Chromecast. The video plays well.
I am using the default Cast receiver for my application. When the device goes to sleep, sometimes, Chromecast stops and sometimes, after sleep mode, the device disconnects after some time. After going through lot of forums and Stack Overflow questions, I implemented the below code
extension GCKSessionManager {
static func ignoreAppBackgroundModeChange() {
let oldMethod = class_getInstanceMethod(GCKSessionManager.self, #selector(GCKSessionManager.suspendSession(with:)))
let newMethod = class_getInstanceMethod(GCKSessionManager.self, #selector(GCKSessionManager.suspendSessionIgnoringAppBackgrounded(with:)))
method_exchangeImplementations(oldMethod, newMethod)
}
func suspendSessionIgnoringAppBackgrounded(with reason: GCKConnectionSuspendReason) -> Bool {
guard reason != .appBackgrounded else { return false }
return suspendSession(with:reason)
}
}
Then in my code I wrote the below line
GCKSessionManager.ignoreAppBackgroundModeChange()
Now suddenly, the Chromecast does not disconnect however after few minutes of sleep, it disconnects as well as, kill the app as well. How can I retain the Chromecast play session even if the device goes to sleep or goes to background.
As I am using GCKUICastButton so I am not using the GCKDeviceManager so I am unable to use the ignoreAppStateNotification using GCKDeviceManager, can you advice if I can use that as well.
I have also added the GCKCastOption code as well in AppDelegate.

NotificationCenter stops working when the screen is locked

I'm having trouble with an app I'm building, the app objective is to play audio files, it works by requesting an audio file from a public API, playing it and wait until it ends, after it ends it requests another audio and starts over.
Here's a shortened version of the code that does this, I omitted the error checking part for simplicity
func requestEnded(audioSource: String) {
let url = URL(string: "https://example.com/audios/" + audioSource)
audio = AVPlayer(url: url!)
NotificationCenter.default.addObserver(self,selector: #selector(MediaItem.finishedPlaying(_:)), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: audio?.currentItem)
audio?.play()
}
#objc func finishedPlaying(_ notification: NSNotification) {
print("Audio ended")
callAPI()
}
func callAPI() {
// do all the http request stuff
requestEnded(audioSource: "x.m4a")
}
// callAPI() is called when the app is initialized
It works well when the screen is unlocked. When I lock the phone the current audio keeps playing but when it ends finishedPlaying() never gets called (the print statement is not shown on the console).
How can I make it so the app would know the audio ended and trigger another one all while locked?, In the android version I got around the screenlock problem by using a partial wakelock which made it run normally even with the screen off.
It has to be done this way because the API decides the audio on realtime and it's all done on the backend so no way to buffer more than one audio without breaking the requirements of the app
What are my options here?, any help is appreciated.

Web Audio API on iOS Safari do not play even after user interaction

I know that there is a limitation in iOS Safari where the audio is not playing until user triggers an interaction. So I have placed the code inside a touchstart event. But unfortunately, I have tried almost every combination, and I couldn't get it to play on iOS Safari.
Here are the things I have tried:
putting the audio load outside the touchstart callback
try adding a gain node
use 0.01 as the start time
and none of the above works in iOS Safari, but they can all play in desktop Chrome and Safari. Here is the link to the gist, you can see the versions where I made the changes (P.S. the click event is used for testing on desktop)
https://gist.github.com/angelathewebdev/32e0fbd817410db5dea1
Sounds play only when currentTime starts to run, but scheduling sounds exactly at currentTime doesn't seem to work. They need to be a little bit into the future (ex: 10ms). You can use the following createAudioContext function to wait until the context is ready to make noise. User action doesn't seem to be required on iPhone, but no such success on iPad just yet.
function createAudioContext(callback, errback) {
var ac = new webkitAudioContext();
ac.createGainNode(); // .. and discard it. This gets
// the clock running at some point.
var count = 0;
function wait() {
if (ac.currentTime === 0) {
// Not ready yet.
++count;
if (count > 600) {
errback('timeout');
} else {
setTimeout(wait, 100);
}
} else {
// Ready. Pass on the valid audio context.
callback(ac);
}
}
wait();
}
Subsequently, when playing a note, don't call .noteOn(ac.currentTime), but do .noteOn(ac.currentTime + 0.01) instead.

Resources