The layout of project architecture:
record audio -> trim audio -> play trimmed audio -> upload to server.
I'm having troubles with playback of audio file that is created as a result of AVAssetExportSession trimming. I had doubts about integrity of trimmed file and I've uploaded it to server and there it plays fine, but iOS refuses to play it. I init AVAudioPlayer with URL to trimmed file, then play() and nothing happens, not even errors are thrown.
Please see code below, what can cause the problem?
static func outputFileURL() -> URL {
let outputFileURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.path.appending("/audioRecord-trimmed.m4a")
return URL(fileURLWithPath: outputFileURL)
}
#IBAction func trimRecording(_ sender: RoundCornerButton) {
//Delete existing recording
deleteEditedRecording()
//Check duration
let duration = CMTimeGetSeconds(recordingToTrim.duration)
if (duration < 5.0) {
let alertController = UIAlertController(title: "Warning", message: "Sound is too short", preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alertController.addAction(action)
self.present(alertController, animated: true, completion: nil)
return
} else {
let exporter = AVAssetExportSession(asset: recordingToTrim, presetName: AVAssetExportPresetAppleM4A)
exporter?.outputFileType = AVFileTypeAppleM4A
exporter?.outputURL = EditorVC.outputFileURL()
exporter?.timeRange = durationToTrim!
exporter?.exportAsynchronously(completionHandler: {
if exporter?.status == .completed {
let alertController = UIAlertController(title: "Success", message: nil, preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alertController.addAction(action)
self.present(alertController, animated: true, completion: nil)
} else {
let alertController = UIAlertController(title: "Error", message: exporter?.error?.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
let action = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alertController.addAction(action)
self.present(alertController, animated: true, completion: nil)
print(exporter?.error?.localizedDescription)
print("Export failed")
return
}
})
}
}
#IBAction func playTrimmedAudio(_ sender: RoundCornerButton) {
print("\nPlay tap\n")
let player = try! AVAudioPlayer(contentsOf: EditorVC.outputFileURL())
player.play()
}
declare audio player globally like this :
var player:AVAudioPlayer!
in your playTrimmedAudio function add this two line of code
player = try! AVAudioPlayer(contentsOf: EditorVC.outputFileURL())
player.play()
Related
When downloading completed I'm showing the alert that download has been completed after that when user click on dismiss button in alert popup self.quickLook(url: url) func will call.
But not showing the file in webView. When removing the alert code everything working fine and file opening in webView.
func showAlerts(){
let alertController = UIAlertController(title: "Download", message: "Download Completed", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "dismiss", style: .default, handler: { _ in
self.dismiss(animated: true, completion: nil)
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
#IBAction func openDoc(_ sender: UIButton) {
if let url = URL(string: "https://ikddata.ilmkidunya.com/images/books/12th-class-chemistry-chapter-10.pdf") {
self.loadFileAsync(url: url) { response, error in
if error == nil {
self.showAlerts()
self.quickLook(url: url)
}
}
}
}
Check console screenshot
Console screenshot see msg
How's your quickLook implemented? I guess it also calls present. You cannot present two things at the same time on iOS.
An option would be to quickLook after the alert is dismissed. Something like:
func showAlerts(and onDismiss: (() -> Void)? = nil) {
let alertController = UIAlertController(title: "Download", message: "Download Completed", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "dismiss", style: .default, handler: { _ in
self.dismiss(animated: true) { onDismiss?() }
}))
alertController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
present(alertController, animated: true, completion: nil)
}
#IBAction func openDoc(_ sender: UIButton) {
if let url = URL(string: "https://ikddata.ilmkidunya.com/images/books/12th-class-chemistry-chapter-10.pdf") {
loadFileAsync(url: url) { response, error in
if error == nil {
self.showAlerts { self.quickLook(url: url) }
}
}
}
}
Judging from your words it's exactly what you expect.
I have a app that can download files from a webview. It was working fine in ios 12 but isnt working. I'm getting the error
Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.
and
This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
This is my view.controller code:
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebView.NavigationType) -> Bool {
print(request.url as Any)
if request.url!.absoluteString.range(of: "/download/") != nil {
let extention = request.url!.absoluteString.slice(from: "&fileextension=", to: "&")?.lowercased()
var name = request.url!.absoluteString.slice(from: "&name=", to: "&")?.lowercased()
name = name?.replacingOccurrences(of: "+", with: " ")
DownlondFromUrl(request.url! as URL,name!, extention!)
return false
}
return true
}
func DownlondFromUrl(_ url: URL,_ name:String,_ extensionfile:String){
// Create destination URL
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
let destinationUrl = documentsUrl!.appendingPathComponent(name + ".\(extensionfile)")
//Create URL to the source file you want to download
let fileURL = url
let sessionConfig = URLSessionConfiguration.default
let session = URLSession(configuration: sessionConfig)
let request = URLRequest(url:fileURL)
let task = session.downloadTask(with: request) { (tempLocalUrl, response, error) in
if let tempLocalUrl = tempLocalUrl, error == nil {
let dataFromURL = NSData(contentsOf: tempLocalUrl)
dataFromURL?.write(to: destinationUrl, atomically: true)
let alert = UIAlertController.init(title: "Download", message: "File download Successful. Do you want open file ", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Open", style: .default , handler:{ (UIAlertAction)in
let fileBrowser = FileBrowser()
self.present(fileBrowser, animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Share", style: .default , handler:{ (UIAlertAction)in
let activityViewController = UIActivityViewController(activityItems: [destinationUrl], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel, handler:{ (UIAlertAction)in
}))
self.present(alert, animated: true, completion: {
print("completion block")
})
} else {
print("Error took place while downloading a file. Error description: %#", error?.localizedDescription as Any);
}
}
task.resume()
}
override func viewWillAppear(_ animated: Bool) {
self.navigationController?.isNavigationBarHidden = true;
}
}
I have found this post (Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread) but I'm very much a beginner and not sure how to implement this.
You have to run any code which accesses the UI on the main thread.
As URLSession tasks run on a background thread you have to add a DispatchQueue block
DispatchQueue.main.async {
let alert = UIAlertController.init(title: "Download", message: "File download Successful. Do you want open file ", preferredStyle: .actionSheet)
alert.addAction(UIAlertAction(title: "Open", style: .default , handler:{ action in
let fileBrowser = FileBrowser()
self.present(fileBrowser, animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Share", style: .default , handler:{ action in
let activityViewController = UIActivityViewController(activityItems: [destinationUrl], applicationActivities: nil)
activityViewController.popoverPresentationController?.sourceView = self.view
self.present(activityViewController, animated: true, completion: nil)
}))
alert.addAction(UIAlertAction(title: "Dismiss", style: .cancel)
self.present(alert, animated: true, completion: {
print("completion block")
})
}
Note:
The parameter in the UIAlertAction closure must be an instance not a type. If the parameter is not used you can replace it with an underscore character (for example handler:{ _ in)
My app crashes and quit when user try to register a new account but that occurs on any device except devices deployed the app with Xcode.
All the devices are registered in the developer account and running iOS 11.4.1
Here is the register button function:
#IBAction func regButton(_ sender: Any) {
usernameText = usernameTextField.text
mobileText = mobileTextField.text
emailText = emailTextField.text
passwordText = passwordTextField.text
fieldText = categoryTextField.text
print(usernameText ?? "damn")
print(mobileText ?? "damn")
print(emailText ?? "damn")
print(passwordText ?? "damn")
print(fieldText ?? "damn")
if(type=="Seeker")
{
let url1 = "http://app.alosboiya.com.sa/hourjob.asmx/insert_jobseeker?name="+usernameText!+"&phone="+mobileText!
let url2 = "&email="+emailText!+"&password="+passwordText!+"&workex="+"companyDescText!"
let url3 = "&category="+fieldText!+"&image="+"downloadURLGlobal!"
let url4 = "&unpaidhour="+"string"+"&hourpaidlast30="+"string"+"&totalhourworked="+"string"+"&balance="+"string"+"&username="+usernameText!
stringURL = url1 + url2 + url3 + url4
}else
{
let url1 = "http://app.alosboiya.com.sa/hourjob.asmx/insert_company?name="+usernameText!+"&field="+fieldText!
let url2 = "&phone="+mobileText!+"&email="+emailText!+"&password="+passwordText!+"&workex="+"companyDescText!"+"&crcopy="+"downloadURLGlobal!"+"&logo="+"string"+"&username="+usernameText!
stringURL = url1 + url2
}
if Reachability.isConnectedToNetwork()
{
if(checkbox.on==true)
{
let url = URL(string: stringURL!)
Alamofire.request(url!).responseString {
(response) in
let result = response.result.value
do {
if(result=="True")
{
let alert = UIAlertController(title: "Registration Successfully", message: "Registration Done Successfully Congratulations",
preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style:
UIAlertActionStyle.default, handler: self.doSomething))
self.present(alert, animated: true, completion: nil)
}else
{
let alert = UIAlertController(title: "Registration Failed", message: "Registration Failed Please Try Again", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
}
}else
{
let alert = UIAlertController(title: "License Agreement", message: "Check to Agree Licence Agreement", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}else
{
let alert = UIAlertController(title: "No Network Connection", message: "Connection Error Please Try Again", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
If a user will tap the button but one or both text fields are empty then your app will crash due to forced unwraping (! mark) which you use in your code.
Example: if mobileText field will be empty then the your app will crash:
let url1 = "http://app.alosboiya.com.sa/hourjob.asmx/insert_jobseeker?name="+usernameText!+"&phone="+mobileText!
the solution is to use guard statement
guard let usernameText = usernameTextField.text,
mobileText = mobileTextField.text,
emailText = emailTextField.text,
passwordText = passwordTextField.text,
fieldText = categoryTextField.text else {
return
}
How to access the view controller which is evoked by the stopRecording() method of ReplayKit framework. And how to save the video in the camera roll?
Try this if you haven't gotten to work yet:
func stopRecording() {
let sharedRecorder = RPScreenRecorder.sharedRecorder()
sharedRecorder.stopRecordingWithHandler { (previewViewController: RPPreviewViewController?, error: NSError?) in
if previewViewController != nil {
print("stopped recording")
previewViewController!.previewControllerDelegate = self
let alertController = UIAlertController(title: "Recording", message: "Tap view to watch, edit, share, or save your screen recording!", preferredStyle: .Alert)
let viewAction = UIAlertAction(title: "View", style: .Default, handler: { (action: UIAlertAction) -> Void in
self.view?.window?.rootViewController?.presentViewController(previewViewController!, animated: true, completion: nil)
})
alertController.addAction(viewAction)
self.previewViewController = previewViewController!
self.previewViewController.modalPresentationStyle = UIModalPresentationStyle.FullScreen
self.view?.window?.rootViewController!.presentViewController(alertController, animated: true, completion: nil)
} else {
print("recording stopped working")
//create the alert
let alert = UIAlertController(title: "Alert", message: "Sorry, there was an error recording your screen. Please Try Again!", preferredStyle: UIAlertControllerStyle.Alert)
// show the alert
self.view!.window?.rootViewController!.presentViewController(alert, animated: true, completion: nil)
alert.addAction(UIAlertAction(title: "Try Again!", style: UIAlertActionStyle.Destructive, handler: { action in
// add action
}))
}
}
}
internal func previewControllerDidFinish(previewController: RPPreviewViewController) {
self.previewViewController.dismissViewControllerAnimated(true, completion: nil)
print("cancel and save button pressed")
}
Here is my code:
let library = ALAssetsLibrary()
library.writeVideoAtPathToSavedPhotosAlbum(NSURL(string: path), completionBlock: { (url, error) -> Void in
if error != nil {
let error = UIAlertController(title: "Error!", message:
"Your video is NOT saved!", preferredStyle: UIAlertControllerStyle.Alert)
error.addAction(UIAlertAction(title: "Shit!", style: UIAlertActionStyle.Default,handler:
{ action in navigationController!.popViewControllerAnimated(true) }))
self.presentViewController(error, animated: true, completion: nil)
} else {
let alertController = UIAlertController(title: "Done!", message:
"Your video is saved!", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Nice!", style: UIAlertActionStyle.Default,handler:
{ action in navigationController!.popViewControllerAnimated(true) }))
self.presentViewController(alertController, animated: true, completion: nil)
}
})
And the AlertController that says that video is saved appears twice if I press "OK" really fast (If I wait a few seconds everything is fine). What's the problem and how can I solve it ?