This happens when i am saving a video, I am also trying to segue to another view controller. I am not sure why the app crashes here
func finalExportCompletion(_ session: AVAssetExportSession) {
PhotoManager().saveVideoToUserLibrary(fileUrl: session.outputURL!) { (success, error) in
if success {
ProgressHUD.showSuccess("Video Saved", interaction: true)
self.finalVideo = session.outputURL!
//FileManager.default.clearTmpDirectory()
self.clipsCollectionView.reloadData()
} else {
ProgressHUD.show(error?.localizedDescription)
}
self.performSegue(withIdentifier: "toPostVideoViewController", sender: nil)
}
}
Your crash is issued to modifications in UI while your are in background thread, as said your crash log, and #rmaddy in his comment, THREAD 8 is a background thread, your need execute all your UI actions in Thread 1 which is the "Main Thread" you have to modify your code like this
func finalExportCompletion(_ session: AVAssetExportSession) {
PhotoManager().saveVideoToUserLibrary(fileUrl: session.outputURL!) { (success, error) in
DispatchQueue.main.async{
if success {
ProgressHUD.showSuccess("Video Saved", interaction: true)
self.finalVideo = session.outputURL!
//FileManager.default.clearTmpDirectory()
self.clipsCollectionView.reloadData()
} else {
ProgressHUD.show(error?.localizedDescription)
}
//I don't know if you anyway want to go to "toPostVideoViewController" this you need to do it also in main thread
self.performSegue(withIdentifier: "toPostVideoViewController", sender: nil)
}
}
}
Related
I have a method with a completion that returns a bool. But I cant figure out how to get around this error.
Error
UISwitch.isOn must be used from main thread only
Button Action
#IBAction func notificationSwitch(_ sender: Any) {
LocalNotification().checkEnabled(completion: { (success) -> Void in
// When download completes,control flow goes here.
if success == false{
print("Cant Turn On")
self.notificationToggle.isOn = false
} else {
print("Can Turn On")
if self.notificationToggle.isOn == true {
self.notificationToggle.isOn = false
} else {
self.notificationToggle.isOn = true
}
}
})
}
also already tried wrapping the LocalNotifications().... in DispatchQueue.main.async but still get the same error
You're almost there. It is not the checkEnabled that needs to be wrapped in the call to get onto the main thread, but the stuff "inside" it:
LocalNotification().checkEnabled(completion: { (success) -> Void in
DispatchQueue.main.async {
if success == false {
I am developing an application that uses the Face/Touch ID at the opening.
I achieved this by adding this func to my MainViewController():
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: nil) {
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "Verifying") { (success, err) in
if success {
DispatchQueue.main.async {
self.loginSuccessfull()
self.button.removeFromSuperview()
}
} else {
if let err = err {
print(err)
}
}
}
}
This gets called both in the ViewDidLoad and by a button, as shown in this video.
As you can see tho, when I try to close my App it has a very weird behavior, and I am sure that it is caused by the FaceID.
Any suggestion to fix this?
Crash log:
Error Domain=com.apple.LocalAuthentication Code=-4 "Caller moved to background." UserInfo={NSLocalizedDescription=Caller moved to background.}
I believe I have found a solution for the issue, by delaying the evaluation.
I noticed that when I have any kind of delay in UI before evaluation (for example: animation that move the logo up before showing the face ID alert) the crash stops altogether.
So I did another test with delay like so:
override func viewDidAppear(_ animated: Bool) {
let context = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Biometric test") { success, error in
DispatchQueue.main.async {
if success {
doSome()
} else {
if let error = error { print(error) }
}
}
}
}
}
}
With that implementation I had zero crashes.
*Note: I also tried different delay times, from 0.1 to 2.0 seconds, all worked for me.
when click button I added face id recognition and if true, need to navigate another page. But not working. application crashed. After few minutes navigate to the next page and back button not working.
#IBAction func myProfile(_ sender: Any) {
// self.performSegue(withIdentifier: "MyProfile", sender: nil)
let context:LAContext = LAContext()
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil){
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Need to access with your finger print", reply: {(wasCorrect, error) in
if wasCorrect{
print("correct")
self.performSegue(withIdentifier: "MyProfile", sender: nil)
}else{
print("incorrect")
}
})
}else{
print("finger print doesn't support touch id")
}
}
Error message is:
[Animation] +[UIView setAnimationsEnabled:] being called from a
background thread. Performing any operation from a background thread
on UIView or a subclass is not supported and may result in unexpected
and insidious behavior.
Callback of evaluatePolicy runs in a background queue so do
DisptachQueue.main.async {
self.performSegue(withIdentifier: "MyProfile", sender: nil)
}
I have written the following code where after the user logs in with touch id or face id it should go to the next view controller.
#IBAction func logInButton(_ sender: Any) {
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Account") { (correct, error) in
if correct {
print("Logged In....")
self.performSegue(withIdentifier: "SecondView", sender: self)
} else {
print("Not Logged in....")
}
}
} else {
print("No Support for Biometrics")
}
}
I'm able to print the Logged in and Not Logged in message. But after user gets authenticated that is after Logged In message is printed I get the following message in console for "performSegue"
019-03-21 19:37:18.473350-0400 Login v1[7596:2446134] [Animation] +[UIView setAnimationsEnabled:] being called from a background thread. Performing any operation from a background thread on UIView or a subclass is not supported and may result in unexpected and insidious behavior. trace=(
0 UIKitCore 0x00000001d6ac7710 + 136
1 libdispatch.dylib 0x000000010159cdc8 _dispatch_client_callout + 16
2 libdispatch.dylib 0x000000010159ee28 _dispatch_once_callout + 84
and much more. I have connected my second view controller and made is display as "Present Modally". not sure where I am going wrong.
As the message states, any operations that affect the UI must be performed on the main thread.
The callback from context.evaluatePolicy is being invoked on some other thread.
All you need to do is dispatch the performSegue on thee main thread:
#IBAction func logInButton(_ sender: Any) {
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: nil) {
context.evaluatePolicy(LAPolicy.deviceOwnerAuthenticationWithBiometrics, localizedReason: "Account") { (correct, error) in
if correct {
print("Logged In....")
DispatchQueue.main.async {
self.performSegue(withIdentifier: "SecondView", sender: self)
}
} else {
print("Not Logged in....")
}
}
} else {
print("No Support for Biometrics")
}
}
I've been getting an EXC_BAD_ACCESS error/crash when I engage in a specific action within my application. Figuring that this was a memory management issue, I enabled NSZombies to help me decipher the issue. Upon the crash, my console gave me the following message:
heres my stack trace:
and the new error highlighting my app delegate line:
Now being the debugger is referring to a UIActivityIndicatorRelease, and the only line of code highlighted in my stack trace is the 1st line in my delegate, is there an issue with my Activity Indicator UI Element? Here is the logic within my login action ( which forces the crash every time ):
#IBAction func Login(sender: AnyObject) {
activityIND.hidden = false
activityIND.startAnimating()
var userName = usernameText.text
var passWord = passwordText.text
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> Void in
if user != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
} else {
self.activityIND.stopAnimating()
if let message: AnyObject = error!.userInfo!["error"] {
self.message.text = "\(message)"
}
}
}
}
is there an error within it?
All your code that manipulates UI objects absolutely, positively must be done from the main thread. (and so it should be in a call to dispatch_async(dispatch_get_main_queue()) as #JAL says in his comment.
That includes not just the self.activityIND.stopAnimating() line, but the code that sets label text as well (any code that manipulates a UIKit object like a UIView).
Your if...else clause should look something like this:
if user != nil
{
dispatch_async(dispatch_get_main_queue())
{
self.activityIND.stopAnimating()
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
}
else
{
dispatch_async(dispatch_get_main_queue())
{
self.activityIND.stopAnimating()
if let message: AnyObject = error!.userInfo!["error"]
{
self.message.text = "\(message)"
}
}
}
So it turns out, in my viewDidLoad() I had the following code in attempt to hide the indicator on the load:
UIActivityIndicator.appearance().hidden = true
UIActivityIndicatorView.appearance().hidesWhenStopped = true
not knowing this would deallocate the indicator for the remainder of the application so when i called the following in my login logic:
activityIND.hidden = false
activityIND.startAnimating()
i was sending a message to an instance that was no longer available, causing the crashes. So all i did was adjust my code in viewDidLoad()
to :
activityIND.hidden = true
activityIND.hidesWhenStopped = true
using the name of the specific outlet I created rather than the general UIActivityIndicatorView
All UI related operation should execute in Main Thread, i.e., within
dispatch_async(dispatch_get_main_queue(){}
block
#IBAction func Login(sender: AnyObject) {
activityIND.hidden = false
activityIND.startAnimating()
var userName = usernameText.text
var passWord = passwordText.text
PFUser.logInWithUsernameInBackground(userName, password: passWord) {
(user, error: NSError?) -> Void in
if user != nil {
dispatch_async(dispatch_get_main_queue()) {
self.performSegueWithIdentifier("loginSuccess", sender: self) //UI task
}
}
else {
dispatch_async(dispatch_get_main_queue())
{
self.activityIND.stopAnimating() //UI task
if let message: AnyObject = error!.userInfo!["error"]
{
self.message.text = "\(message)" //UI task
}
};
}
}
}
Refer some good articles on Concurrency here and here