Completion closure in swift doesn't appear to be firing - ios

I am following the "Learning Swift" book from O'reilly, and I have some code that looks like:
func deleteDocumentAtURL(url: NSURL) {
NSLog("Got to top of deleteDocumentAtURL, url: \(url)")
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
fileCoordinator.coordinateWritingItemAtURL(url, options: .ForDeleting, error: nil, byAccessor: { (urlForModifying) -> Void in
NSLog("Here I am")
do {
NSLog("Got inside deleteDocumentBlock")
try NSFileManager.defaultManager().removeItemAtURL(urlForModifying)
// Remove the URL from the list
self.availableFiles = self.availableFiles.filter {
$0 != url
}
// Update the collection
self.collectionView?.reloadData()
} catch let error as NSError {
let alert = UIAlertController(title: "Error deleting", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
}
When I run this code, which is triggered by clicking a button in the interface, the application hangs. Also, the log inside the do{} is not firing, which makes me think there's a problem with the whole block? Help appreciated.

Because
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
is local in the function. The variable is already destroyed, if you finished the function.
So, the async block can't be executed.
Put fileCoordinator as an instance variable to the class.

You are not handling error and probably have an error
Documentation for coordinateReadingItemAtURL:
outError: On input, a pointer to a pointer for an error object. If a file presenter encounters an error while preparing for this read operation, that error is returned in this parameter and the block in the reader parameter is not executed. If you cancel this operation before the reader block is executed, this parameter contains an error object on output.
add error handler and see if you get an error
func deleteDocumentAtURL(url: NSURL) {
NSLog("Got to top of deleteDocumentAtURL, url: \(url)")
let fileCoordinator = NSFileCoordinator(filePresenter: nil)
var error: NSError?
fileCoordinator.coordinateWritingItemAtURL(url, options: .ForDeleting, error: &error, byAccessor: { (urlForModifying) -> Void in
NSLog("Here I am")
do {
NSLog("Got inside deleteDocumentBlock")
try NSFileManager.defaultManager().removeItemAtURL(urlForModifying)
// Remove the URL from the list
self.availableFiles = self.availableFiles.filter {
$0 != url
}
// Update the collection
self.collectionView?.reloadData()
} catch let error as NSError {
let alert = UIAlertController(title: "Error deleting", message: error.localizedDescription, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Done", style: .Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
})
//Error:
if(error != nil){
print(error)
}
}

Related

App crashing when it gets closed because of biometrics

I have followed this tutorial to set up authentication on my app.
It works fine, but when try to terminate the app, I get the following:
* Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the
layout engine must not be performed from a background thread after it
has been accessed from the main thread.'
* First throw call stack:
(0x18bf22a48 0x18bc49fa4 0x18c3f8f08 0x18c1fa03c 0x19035664c 0x190357a00 0x18f604c5c 0x18f6004c8 0x18f600734 0x18f600a54
0x18f6054dc 0x18f605328 0x18f5e7004 0x18f97b134 0x18f97b838
0x18f990f70 0x18f989d7c 0x18f98b790 0x18f98dc6c 0x18f98e168
0x18f98dbbc 0x18f98de24 0x100c0f4ec 0x100c01780 0x1017917fc
0x101792bd8 0x1017952d4 0x1017a4160 0x1017a4a88 0x18bc3eb48
0x18bc41760)
libc++abi.dylib: terminating with uncaught exception of type NSException
(lldb)
I do not understand the error.
Here's my code:
func authenticateUser() {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] success, authError in
DispatchQueue.main.async {
if success {
self?.loginSuccessfull()
} else {
DispatchQueue.main.async {
let ac = UIAlertController(title: "Errore", message: "Riprova", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self?.authenticateUser()
return
}))
self?.present(ac, animated: true, completion: nil)
}
}
}
}
} else {
let ac = UIAlertController(title: "Errore", message: "Il tuo device non รจ configurato per l'autenticazione", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default))
self.present(ac, animated: true, completion: nil)
}
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
let reason = "Identify yourself!"
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) {
[unowned self] success, authenticationError in
DispatchQueue.global().async {
if success {
DispatchQueue.main.async {
self.loginSuccessfull()
}
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Sorry!", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
self.present(ac, animated: true)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
}
}
As you can see, I just evaluate the biometrics. Again: this works perfectly during the whole "life" of the application, but the problem comes out when I terminate the app. It just crashes and gets me to the home screen.
There are no crash logs into Settings > Privacy...
please use for authentication function its a global function
//enum for response handler
enum AuthenticatinsError: String {
case userEnrolled = "User is not enrolled"
case passCodeNotSet = "user not set passcode"
case biometricNotAvelabel = "Biometric authentication not available"
case faild = "faild to authenticat"
case noIssue = ""
}
func authenticationUser(compleation: #escaping (_ status: Bool, _ msgg: AuthenticatinsError) -> Void) {
context = LAContext()
//check inside app if biometric on then in UserDefault set true other wise false
//let isBiometricOn = WUserDefault.getBiometric()
//if isBiometricOn == false {
//context.invalidate()
//}
// First check if we have the needed hardware support.
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
let reason = "Log in to your account"
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason ) { success, error in
if success {
compleation(true, .noIssue)
} else {
print(error?.localizedDescription ?? "Failed to authenticate")
compleation(false, .faild )
}
}
} else {
if let err = error {
if #available(iOS 11.0, *) {
switch err.code {
case LAError.Code.biometryNotEnrolled.rawValue:
notifyUser("User is not enrolled",
err: err.localizedDescription)
compleation(false, .userEnrolled)
case LAError.Code.passcodeNotSet.rawValue:
compleation(false, .passCodeNotSet)
case LAError.Code.biometryNotAvailable.rawValue:
notifyUser("Biometric authentication not available",
err: err.localizedDescription)
compleation(false, .biometricNotAvelabel)
default:
compleation (false, .passCodeNotSet)
}
} else {
// Fallback on earlier versions
}
}
}
}
user code where you call
self.authenticationUser { (statu, msg) in
if statu == true {
DispatchQueue.main.async {
self?.loginSuccessfull()
}
} else {
DispatchQueue.main.async {
let ac = UIAlertController(title: "Errore", message: "Riprova", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "Ok", style: .default, handler: { (action) in
self?.authenticateUser()
return
}))
self?.present(ac, animated: true, completion: nil)
}
}
}

DynamoDB load nil Error

I'm trying to load my data from dynamoBD and print them out with the labels.
Following the sample: https://github.com/awslabs/aws-sdk-ios-samples/tree/master/DynamoDBObjectMapper-Sample
I already created a table on the account and I can see their attributes using web browser.
Using this code to load data:
var tableRow: DDBTableRow?
func getTableRow() {
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
dynamoDBObjectMapper .load(DDBTableRow.self, hashKey: (tableRow?.PhotoId)!, rangeKey: tableRow?.UserId) .continueWith(executor: AWSExecutor.mainThread(), block: { (task:AWSTask!) -> AnyObject! in
if let error = task.error as? NSError {
print("Error: \(error)")
let alertController = UIAlertController(title: "Failed to get item from table.", message: error.description, preferredStyle: UIAlertControllerStyle.alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.cancel, handler: nil)
alertController.addAction(okAction)
self.present(alertController, animated: true, completion: nil)
} else if let tableRow = task.result as? DDBTableRow {
self.photoIdLabel.text = tableRow.PhotoId
self.userIdLabel.text = tableRow.UserId
self.photoDateLabel.text = tableRow.PhotoDate
self.photoURLLabel.text = tableRow.PhotoURL
self.photoCategoryLabel.text = tableRow.PhotoCategory
}
return nil
})
}
In the log: tableRows [MyProject.DDBTableRow]? nil none.
The sample code is working fine, I don't know what wrong. Do I miss something before calling load? I feel very close to solve it. Please help!

Optimizing a CloudKit sign-in on app launch with error handling. How can I better handle the optional chaining? Swift 3.0/Xcode 8.1

Is there a cleaner, swiftier solution to handle the optional chaining happening in my code below? I'm setting up the user for CloudKit access in my custom function runCKsetup():
func runCKsetup() {
container.requestApplicationPermission(.userDiscoverability) { (status, error) in
guard error == nil else {
if let error = error as? NSError {
if let errorDictionary: AnyObject = error.userInfo as? Dictionary<String, AnyObject> as AnyObject? {
let localizedDescription = errorDictionary["NSLocalizedDescription"]! as! String!
if localizedDescription! == "This operation has been rate limited" {
// Create an alert view
let alert = UIAlertController(title: "Network Error", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Test for Connection", style: UIAlertActionStyle.default) { (action) in
self.runCKsetup()
})
self.present(alert, animated: true, completion: nil)
} else {
// Create an alert view
let alert = UIAlertController(title: "Sign in to iCloud", message: localizedDescription!, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.default) { (action) in
})
self.present(alert, animated: true, completion: nil)
}
}
}
return
}
if status == CKApplicationPermissionStatus.granted {
self.container.fetchUserRecordID { (recordID, error) in
guard error == nil else {
self.presentMessageAlert((error?.localizedDescription)!, title: "Error", buttonTitle: "Ok")
return }
guard let recordID = recordID else { return }
self.container.discoverUserIdentity(withUserRecordID: recordID, completionHandler: { (info, fetchError) in
//do something with the users names: e.g. print("\(info?.nameComponents?.givenName) \(info?.nameComponents?.familyName)")
})
}
}
}
}

Can't Use Nil for keys or values of PFObject

So I have this code.
#IBAction func updateRatelabel(sender: AnyObject) {
let rateValue = Int(rateSlider.value)
if rateValue == 0 {
rateLabel.text = "Budget (min.)"
}
else {
rateLabel.text = "$ \(rateValue)"
}
}
func createdoctor()
{
let doctors = PFObject(className: "doctors")
if doctorName.text?.characters.count > 5 {
doctorName.textColor = UIColor.redColor()
}
else {
let currentUser = String( PFUser.currentUser())
let rateValue = rateLabel.text
let rateValueInt = String(rateValue?.characters.dropFirst(2))
print(rateValueInt)
doctors["doctorName"] = doctorName.text
doctors["Rate"] = Int(rateValueInt)
doctors["doctorContent"] = doctorDetails.text
doctors["createdby"] = currentUser
doctors.saveInBackgroundWithBlock ({(success: Bool, error: NSError?) -> Void in
if error == nil {
print("created doctor")
}
else {
print("image \(error)")
}
let alert = UIAlertController(title: "Success", message: "New doctor Created" , preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
})
}
}
I am getting error when this code kicks in, this is the error that is happening
> 016-01-31 01:00:07.921 idoctortwo[3294:151254] Attempting to load the
> view of a view controller while it is deallocating is not allowed and
> may result in undefined behavior (<UIAlertController: 0x7f8258c8af80>)
> Optional(Swift.String.CharacterView(_core:
> Swift._StringCore(_baseAddress: 0x00007f8258c9b9c2, _countAndFlags: 4,
> _owner: Optional(Swift._HeapBufferStorage<Swift._StringBufferIVars, Swift.UInt16>)))) 2016-01-31 01:00:12.771 idoctortwo[3294:151254] ***
> Terminating app due to uncaught exception
> 'NSInvalidArgumentException', reason: 'Can't use nil for keys or
> values on PFObject. Use NSNull for values.'
I managed to find out that the following line of code is the culprit
doctors["Rate"] = Int(rateValueInt)
if I do this
doctors["Rate"] = rateValueInt
I have no error but my Parse the column is a number column hence the effort to make it as a number.
EDIT
2016-01-31 03:58:11.222 iLegaltwo[3662:186816] Attempting to load the
view of a view controller while it is deallocating is not allowed and
may result in undefined behavior ()
Optional(Swift.String.CharacterView(_core:
Swift._StringCore(_baseAddress: 0x00007fb2c3667472, _countAndFlags: 4,
_owner: Optional(Swift._HeapBufferStorage)))) 2016-01-31 03:58:30.728 iLegaltwo[3662:186900]
[Error]: invalid type for key createdby, expected *_User, but got
string (Code: 111, Version: 1.12.0) image Optional(Error Domain=Parse
Code=111 "invalid type for key createdby, expected *_User, but got
string" UserInfo={code=111, temporary=0, error=invalid type for key
createdby, expected *_User, but got string,
NSLocalizedDescription=invalid type for key createdby, expected
*_User, but got string})
EDIT after modifications for the above error:
func createCase()
{
let cases = PFObject(className: "Cases")
if caseName.text?.characters.count > 5
{
caseName.textColor = UIColor.redColor()
}
else
{
let currentUser = String( PFUser.currentUser())
let rateValue = rateLabel.text
let rateValueInt = String(rateValue?.characters.dropFirst(2))
print(rateValueInt)
cases["CaseName"] = caseName.text
cases["Rate"] = Int(rateValueInt) ?? Int(0)
cases["CaseContent"] = caseDetails.text
cases["createdby"] = currentUser
cases.saveInBackgroundWithBlock ({(success: Bool, error: NSError?) -> Void in
if error == nil
{
print("created case")
}
else
{
print("image \(error)")
}
self.performSelectorOnMainThread("success", withObject: nil, waitUntilDone: true)
})
}
}
func success() {
let alert = UIAlertController(title: "Success", message: "New doctor Created" , preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
Can't touch the UI from the background thread
Push the view controller from the main thread. Culprit:
saveInBackgroundWithBlock...
self.presentViewController
Solutions
- use `performSelectorOnMainThread`
- use dispatch_async(dispatch_get_main_queue(), ^(void) { } })
Example
Replace alert creation by
self.performSelectorOnMainThread("success", withObject: nil, waitUntilDone: true)
and add this method:
func success() {
let alert = UIAlertController(title: "Success", message: "New doctor Created" , preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
}
Dictionary key/value can't be nil
Int("someString") doesn't return an Int but an optional Int?.
The documentation lists these accessors as Array Accessors, while in reality, using key/value, they should be considered Dictionary Accessors.
Thus you should treat PFObject is a dictionary wrapper: can't pass nil for either key nor value. You store objects in a dictionary, not optionals.
Twist
Int(rateValueInt) ends up being a Int?, because of the Int(rateValueInt) conversion.
Solution
doctors["Rate"] = Int(rateValueInt) ?? Int(0)
This will explicitly substitute an Int in case Int(rateValueInt) can't be unwrapped.

iOS - User Authentication Function That Returns Bool

Ultimately, what I want to have is one function (or probably a function within a separate class) that prompts the user to authenticate via TouchID, then passcode and if either of these are successful then returns a true boolean.
I've figured out the authentication mostly however I can't get the function to return a boolean, here's roughly what I have so far:
The authenticate user function:
func authenticateUser() -> Bool {
let context = LAContext()
var error: NSError?
let reasonString = "Authentication is needed to access your places."
if context.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error) {
context.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: reasonString, reply: { (success, policyError) -> Void in
if success {
print("touchID authentication succesful")
} else {
switch policyError!.code {
case LAError.UserFallback.rawValue:
print("User selected to enter password.")
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
default:
print("Authentication failed! :(")
}
}
})
} else {
print(error?.localizedDescription)
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
self.showPasswordAlert()
})
}
return true
}
It's just set to return true for now for testing purposes. However I'd like to have it return true whenever there's a successful authentication. I can't place the return within the context.evaluatePolicy because it's inside the block method. Is there another way to do what I want? Or am I going about this in totally the wrong manner?
Also, for reference here is my showPasswordAlert function:
func showPasswordAlert() {
let alertController = UIAlertController(title: "Passcode", message: "Please enter your passcode.", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "OK", style: .Default) { (action) -> Void in
if let textField = alertController.textFields?.first as UITextField? {
if let passcode = self.keychainWrapper.myObjectForKey("v_Data") as? String {
if textField.text == passcode {
print("Authentication successful! :) ")
} else {
}
}
}
}
let cancelAction = UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Cancel, handler: nil)
alertController.addAction(defaultAction)
alertController.addAction(cancelAction)
alertController.addTextFieldWithConfigurationHandler { (textField) -> Void in
textField.placeholder = "Enter passcode..."
textField.textAlignment = NSTextAlignment.Center
textField.secureTextEntry = true
textField.keyboardType = UIKeyboardType.NumberPad
}
self.presentViewController(alertController, animated: true, completion: nil)
}
So in my head what I'm thinking is: showPasswordAlert could also return a true boolean to authenticateUser and then this would in turn return a true boolean to where the authenticateUser function is being called. I know there's a simpler way to do that but I'd just like to get it working for now.
So after much trial and error I've come up with possibly what is the best solution for me at the moment.
It seems that since evaluatePolicy and co. are run asynchronously you can't return variables from them or access variables. You can however, call selectors from inside these blocks (why this is I have no idea).
So my current solution as of writing this post is to call the authenticate function as such:
func authenticateUserWithAction(actionSelector: Selector, object: AnyObject){}
I pass it an action (declared elsewhere in the class, but basically what you want to do if authentication is successful) and an object. The object is just incase the action requires something to be passed to the function. So in my app for example, after authentication a viewController is presented and an object on that viewController is set to an object in the original viewController. This object is passed in the authenticate function.
From within the authenticate user function I can call to an authenticateUserWithPasscode(actionSelector: Selector, object: AnyObject) that takes in the same action and object as the original authenticate function.
The action and object are passed down the chain until the user is authenticated and they are performed.
Pretty hacky code overall but it seems to be working fine for me.
Also had this problem, I ended up making a struct called Authentication which has a static function authenticate which gets passed the view controller from where you're calling it and a callback function:
import LocalAuthentication
import UIKit
struct Authentication {
static func authenticate(viewController: UIViewController, callback:
#escaping (Bool) -> ()) {
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
let reason = "Please Authenticate"
context.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: reason) {
[weak viewController] success, authenticationError in
guard let viewController = viewController else {
return
}
DispatchQueue.main.async {
if success {
callback(true)
} else {
let ac = UIAlertController(title: "Authentication failed", message: "Please try again", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
viewController.present(ac, animated: true)
callback(false)
}
}
}
} else {
let ac = UIAlertController(title: "Touch ID not available", message: "Your device is not configured for Touch ID.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
viewController.present(ac, animated: true)
callback(false)
}
}
}
Then calling it:
Authentication.authenticate(viewController: parentViewController, callback: {
[weak self] (authenticated: Bool) in
if authenticated {
self?.yourFunctionHere()
}
})

Resources