Is there a way to unit test Touch Id using XCTest? - ios

Is there a way to unit test TouchId's "evaluatePolicy" or "canevaluatePolicy" using XCTest? I want to unit test using simulator as base.
Thanks

I have described one way of writing unit test for Touch ID code in this post.
Basically, if you have some Touch ID managing class similar to this one:
final public class TouchIDManager {
/// Authentication context object we use for Touch ID. Typically it's just a `LAContext` instance that is mocked when testing.
internal var authenticationContext = LAContext()
/**
Checks Touch ID availability.
- returns: Flag indicating if Touch ID is available
*/
public func touchIDAvailable() -> Bool {
return authenticationContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: nil)
}
/**
Authenticates the user with Touch ID
- parameter completion: Completion handler
*/
public func authenticate(completion: (success: Bool) -> ()) {
guard touchIDAvailable() else {
completion(false)
return
}
authenticationContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Wanna Touch my ID?") {
(success: Bool, error: NSError?) -> Void in
completion(success: success)
}
}
}
You can simply fake the LAContext behaviour with a StubLAContext class:
func testSuccessfulAuthentication() {
/// A class faking Touch ID availability and successful user verification
class StubLAContext: LAContext {
override func evaluatePolicy(policy: LAPolicy, localizedReason: String, reply: (Bool, NSError?) -> Void) { reply(true, nil) }
override func canEvaluatePolicy(policy: LAPolicy, error: NSErrorPointer) -> Bool { return true }
}
let manager = TouchIDManager()
manager.authenticationContext = StubLAContext()
manager.authenticate { (success) in
XCTAssertTrue(success)
}
}
Clearly this particular test doesn't do much, but it might be a good starting point to test other Touch ID related logic you have in your application.

Related

FaceID should fallback to Passcode but does not

I have inherited a code base with the following class providing support for Face/Touch ID.
The expected behaviour is that on Face/Touch ID success the user is signed in. This works.
However, should the user fail Face ID and opt to enter their passcode, they are signed out once the completion handler is called. I believe opting to use passcode is triggering
else {
self.authState = .unauthenticated
completion(.unauthenticated)
}
How can I trigger the passcode prompt instead? Should I create a second policy using LAPolicy.deviceOwnerAuthentication and evaluate that instead?
import LocalAuthentication
public enum AuthenticationState {
case unknown
case authenticated
case unauthenticated
public func isAuthenticated() -> Bool {
return self == .authenticated
}
}
public protocol TouchIDAuthenticatorType {
var authState: AuthenticationState { get }
func authenticate(reason: String, completion: #escaping (AuthenticationState) -> Void) -> Void
func removeAuthentication() -> Void
}
public protocol LAContextType: class {
func canEvaluatePolicy(_ policy: LAPolicy, error: NSErrorPointer) -> Bool
func evaluatePolicy(_ policy: LAPolicy, localizedReason: String, reply: #escaping (Bool, Error?) -> Void)
}
public class TouchIDAuthenticator: TouchIDAuthenticatorType {
public var authState: AuthenticationState = .unknown
private var context: LAContextType
private var policy = LAPolicy.deviceOwnerAuthenticationWithBiometrics
public init(context: LAContextType = LAContext()) {
self.context = context
}
public func authenticate(reason: String, completion: #escaping (AuthenticationState) -> Void) -> Void {
var error: NSError?
if context.canEvaluatePolicy(policy, error: &error) {
context.evaluatePolicy(policy, localizedReason: reason) { (success, error) in
DispatchQueue.main.async {
if success {
self.authState = .authenticated
completion(.authenticated)
} else {
self.authState = .unauthenticated
completion(.unauthenticated)
}
}
}
} else {
authState = .authenticated
completion(.authenticated)
}
}
public func removeAuthentication() -> Void {
authState = .unknown
context = LAContext() // reset the context
}
}
extension LAContext: LAContextType { }
I should point out, on the simulator this appears to work as expected, but on a device it does not and I signed out.
You have to use .deviceOwnerAuthentication instead of asking for biometrics. If FaceID is available, it will force the attempt to use this either way.
If you try enough times then you will get another dialogue to "Cancel" or fallback to "Use passcode".
Choosing the fallback will show you the passcode screen.
However, if you specified .deviceOwnerAuthenticationWithBiometrics, you will get the same fallback option. Rather than getting this dialogue I would have expected to receive an error of LAError.Code.biometryLockout. But instead I get this fallback option dialogue. But that is ok...
However, if I then tap the fallback option to "Use passcode", it will NOT present the passcode alert. Instead it fails with a LAError.Code.userFallback error.
If you use the policy without biometrics, you will not get and be able to catch the .userFallback error.
So to sum things up:
If you ask for the deviceOwnerAuthenticationWithBiometrics policy then you will have to handle the fallback yourself.
If you ask for deviceOwnerAuthentication only, then biometrics will be used if available and authorized, otherwise it will automatically fall back to passcode if biometrics is unavailable, or give you the fallback option to enter passcode automatically if biometrics attemps fail.

Firebase observer architecture

Okay, so I'm trying to build an iOS app that relies on Firebase (To work with its android version)
I started with creating a repository for each actor in my app and a general repository to manage them all
Each repository manages the observers of this actor. An example:
Inside the PagesRepository, this is a function that retrieves all the pages from Firebase and returns it inside a completionHandler:
//MARK: Gets the whole pages list
func getPagesList(completionHandler: #escaping (_ pages: [Page]?, _ error: NSError?) -> Void) {
func displayError(error: String) {
print(error)
completionHandler(nil, self.getErrorFromString(error))
}
pagesRef.observe(DataEventType.value) { pagesSnapshot in
guard pagesSnapshot.exists() else {
displayError(error: "Pages snapshot doesn't exist")
return
}
var pagesList = [Page]()
for pageSnapshot in pagesSnapshot.children {
pagesList.append(Page(snapshot: pageSnapshot as! DataSnapshot))
}
completionHandler(pagesList, nil)
}
}
And then I call it from the ViewController like this:
repository.getPagesList { (pages, error) in
guard error == nil else {
return
}
//Do processing
}
I know this may be a lot to take in, but my problem is that every time I call the function, it creates a new observer but doesn't cancel the old one... So, the completionHandler is called multiple times with different values
How should I manage this problem?
(Sorry for being complicated and a little unclear, I'm just really lost)
It seems like you only want to observe the value once so I would use this instead:
func getPagesList(completionHandler: #escaping (_ pages: [Page]?, _ error: NSError?) -> Void) {
func displayError(error: String) {
print(error)
completionHandler(nil, self.getErrorFromString(error))
}
pagesRef.observeSingleEvent(of: .value, with: { (pagesSnapshot) in
var pagesList = [Page]()
for pageSnapshot in pagesSnapshot.children {
pagesList.append(Page(snapshot: pageSnapshot as! DataSnapshot))
}
completionHandler(pagesList, nil)
}) { (error) in
// Display error
}
}

Global handling of 401 Responses with RestKit and Swift

I am currently working on an iOS App that is developed using Swift. For the REST calls, I am using the RestKit framework.
The next stage of my project is to start using authentication against the services. An issue that I have come up against is handling 401 (Not Authenticated) responses from the service. In all of these circumstances, I would like to display a login page. I want to avoid implementing the error handling of this multiple times.
I followed the tutorial at http://blog.higgsboson.tk/2013/09/03/global-request-management-with-restkit/. However, this is in Objective-C and I would like to do things slightly differently.
As such, I want to build a class that extends RKObjectRequestOperation as in the tutorial but using Swift. I have come up with an issue as I am receiving the error
Overriding method with selector 'setCompletionBlockWithSuccess:failure:' has incompatible type '((RKObjectRequestOperation, RKMappingResult) -> Void (RKObjectRequestOperation, NSError) -> Void) -> Void'
I am a bit stuck on this and so was hoping someone could help. The code for the method that is failing is below.
class CustomRequestOperation : RKObjectRequestOperation {
func setCompletionBlockWithSuccess(success: (operation: RKObjectRequestOperation, mappingResult: RKMappingResult) -> Void, failure: (operation: RKObjectRequestOperation, error: NSError) -> Void) -> Void {
}
}
Can anyone point out what is wrong with my method signature?
You're overriding the method so you can get Xcode to add the signature for you if you start typing the method name and hie escape.
It should be
func setCompletionBlockWithSuccess(success: (operation: RKObjectRequestOperation, mappingResult: RKMappingResult) -> Void, failure: (operation: RKObjectRequestOperation, error: NSError) -> Void) {
(you are adding a return spec that doesn't exist in the superclass method)
Here is the full class in swift...
class CustomRKObjectRequestOperation : RKObjectRequestOperation
{
override func setCompletionBlockWithSuccess(success: ((RKObjectRequestOperation!, RKMappingResult!) -> Void)!, failure: ((RKObjectRequestOperation!, NSError!) -> Void)!) {
super.setCompletionBlockWithSuccess({ (operation, RKMappingResult mappingResult) -> Void in
if ((success) != nil) {
success(operation, mappingResult);
}
}, failure: { (RKObjectRequestOperation operation, NSError error) -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("connectionFailure",object:operation)
if ((failure) != nil) {
failure(operation, error);
}
})
}
}
App Delegate
Register notification
NSNotificationCenter.defaultCenter().addObserver(self, selector:"connectionFailedWithOperation:",name:"connectionFailure", object:nil)
func connectionFailedWithOperation(notification: NSNotification ){
let operation = notification.object as! RKObjectRequestOperation?;
if ((operation) != nil) {
let statusCode = operation!.HTTPRequestOperation.response.statusCode;
if (statusCode == 401) {
// log user out
}
}
}

Cannot reference a local function with capture from another local function (swift)

var first_name = ""
func problemFunc() {
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
first_name = fbGraphUserDict["first_name"] as NSString
println(first_name)
}
}
}
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc() // error is here
}
})
This code is inside an #Ibaction button. I cannot build because the call to problemFunc() triggers the error message in the title of this post. If I move the first_name var definition inside the problemFunc it will work ok. But I need it out, because another function will need to access its value.
I'm really not sure at what causes this problem, if you have a clue, please help.
Use a closure instead of a function:
var first_name = ""
let problemFunc = { () -> () in
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
first_name = fbGraphUserDict["first_name"] as NSString
println(first_name)
}
}
}
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc() // error is here
}
})
Here are the basic principles in play: (from Apple's docs: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html#//apple_ref/doc/uid/TP40014097-CH11-ID103)
"Global and nested functions, as introduced in Functions, are actually special cases of closures. Closures take one of three forms:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values from their surrounding context."
ie this is ok
func someFunc() {
func nestFunc() {}
}
but this is not
func someFunc() {
func nestFunc() {
func nestedFunc2() { }
}
}
If you look at this in Xcode the third function (func nestedFunc2) will give you the error "Cannot reference a local function with capture from another local function"
The top function (func someFunc) is a global scope function and those work like regular functions/methods.
The second function (func nestFunc) is a nested function which is a named closure one level deep that can capture the scope of its parent global function.
Nested functions, can capture the scope of a global function but not the scope of another nested function.
That's why we need a closure i.e.
func someFunc() {
func nestFunc() {
let strictClosure = { () -> () in
//this is where you write the code
}
}
}
#fluidsonic answer should solve the problem. However note that you're doing some spaghetti code, because you are modifying a variable captured by a closure, and executed in the context of another function. That's hard to track if you need to debug, and more generally hard to follow when and how that variable is modified.
A more linear and better readable flow is to define problemFunc as a function taking a function as parameter, and calling that function rather than directly setting the value in the first_name variable:
let problemFunc = { (callback: (String -> Void) -> ()) in
FBRequestConnection.startForMeWithCompletionHandler { (connection: FBRequestConnection!, result: AnyObject!, error: NSError!) -> Void in
if let fbGraphUserDict = result as? Dictionary<String, AnyObject>{
let first_name = fbGraphUserDict["first_name"] as NSString
callback(first_name) // << here you call the callback passing the `first_name` local variable
println(first_name)
}
}
}
and do the actual assignment to first_name in a closure you define when calling problemFunc:
PFFacebookUtils.logInWithPermissions(permissions, {
(user: PFUser!, error: NSError!) -> Void in
if user == nil {
NSLog("Uh oh. The user cancelled the Facebook login.")
} else if user.isNew {
NSLog("User signed up and logged in through Facebook!")
} else {
NSLog("User logged in through Facebook!")
problemFunc { (name: String) -> Void in
first_name = name
}
}
})

Swift: Asynchronous callback

How do I make asynchronous callbacks in swift? I'm writing a little Framework for my app because it's supposed to run on both, iOS und OS X. So I put the main code that is not device-specific into this framework that also handles requests to my online api. And obviously I also want the app's GUI and therefore my ViewControllers to react as soon as a api request has finished. In Objective-C I've done this by saving the view containing the function that had to be called in an id variable and the function itself in a selector variable. Then I invoked the function using the following code:
SEL selector = callbackMethod;
((void (*)(id, SEL))[callbackViewController methodForSelector:selector])(callbackViewController, selector);
How can I accomplish this in swift? Or is there a better way of doing this?
I really appreciate all your help!
I've shared the pattern that I use for this scenario in the following gist: https://gist.github.com/szehnder/84b0bd6f45a7f3f99306
Basically, I create a singleton DataProvider.swift that setups an AFNetworking client. Then the View Controllers call methods on that DataProvider, each of which is terminated by a closure that I've defined as a typealias called ServiceResponse. This closure returns either a dictionary or an error.
It allows you to very cleanly (imo) call for an async data action from the VC's with a very clear indication of what you want performed when that async response returns.
DataProvider.swift
typealias ServiceResponse = (NSDictionary?, NSError?) -> Void
class DataProvider: NSObject {
var client:AFHTTPRequestOperationManager?
let LOGIN_URL = "/api/v1/login"
class var sharedInstance:DataProvider {
struct Singleton {
static let instance = DataProvider()
}
return Singleton.instance
}
func setupClientWithBaseURLString(urlString:String) {
client = AFHTTPRequestOperationManager(baseURL: NSURL.URLWithString(urlString))
client!.operationQueue = NSOperationQueue.mainQueue()
client!.responseSerializer = AFJSONResponseSerializer()
client!.requestSerializer = AFJSONRequestSerializer()
}
func loginWithEmailPassword(email:String, password:String, onCompletion: ServiceResponse) -> Void {
self.client!.POST(LOGIN_URL, parameters: ["email":email, "password":password] , success: {(operation:AFHTTPRequestOperation!, responseObject:AnyObject!) -> Void in
self.setupClientWithBaseURLString("http://somebaseurl.com")
let responseDict = responseObject as NSDictionary
// Note: This is where you would serialize the nsdictionary in the responseObject into one of your own model classes (or core data classes)
onCompletion(responseDict, nil)
}, failure: {(operation: AFHTTPRequestOperation!, error:NSError!) -> Void in
onCompletion(nil, error)
})
}
}
MyViewController.swift
import UIKit
class MyViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
DataProvider.sharedInstance.loginWithEmailPassword(email:"some#email.com", password:"somepassword") { (responseObject:NSDictionary?, error:NSError?) in
if (error) {
println("Error logging you in!")
} else {
println("Do something in the view controller in response to successful login!")
}
}
}
}
I'd like to recommend use a block or closure callback instead of using NSThread and selectors.
For example, in my API I have follow method:
Swift:
Below you will find an updated implementation.
func getUsers(completion: (result: NSArray?, error: NSError?)->())
{
var session = NSURLSession.sharedSession()
var task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if error != nil {
completion(nil, error)
} else {
var result:NSArray = data to NSArray;
completion(result, nil)
}
}
task.resume()
}
Objective-C:
...
typedef void (^CBSuccessBlock)(id result);
typedef void (^CBFailureBlock)(NSError *error);
...
- (void)usersWithSucces:(CBSuccessBlock)success failure:(CBFailureBlock)failure
{
NSURLSession *session = [NSURLSession sharedSession];
[[session dataTaskWithURL:[NSURL URLWithString:url]
completionHandler:^(NSData *data,
NSURLResponse *response,
NSError *error) {
NSArray *users = //convert data to array
if(error)
failure(error);
else
success(users);
}] resume];
}
Then, just make a call to api from view controller:
Objc:
[api usersWithSucces:^(id result)
{
//Success callback
} failure:^(NSError *error)
{
//Failure callback
}];
Swift:
api.getUsers({(result: AnyObject?, error: NSError?) -> Int in
// callback here
})
UPDATE:
Meanwhile, I see that the question and answers are still being useful and interested. Well, here is an updated version of swift implementation using generic enum as a result object:
//Generic enum that represents the result
enum AsyncResult<T>
{
case Success(T)
case Failure(NSError?)
}
class CustomUserObject
{
}
func getUsers(completion: (AsyncResult<[CustomUserObject]>)->())
{
let request = NSURLRequest()
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request){
(data, response, error) -> Void in
if let error = error
{
completion(AsyncResult.Failure(error))
} else {
let result: [CustomUserObject] = []//deserialization json data into array of [CustomUserObject]
completion(AsyncResult.Success(result))
}
}
task.resume()
}
//Usage:
getUsers { (result) in
switch result
{
case .Success(let users):
/* work with users*/
break
case .Failure(let error):
/* present an error */
break
}
}
I've just made this little example:
Swift: Async callback block pattern example
Basically there is ClassA:
//ClassA it's the owner of the callback, he will trigger the callback when it's the time
class ClassA {
//The property of that will be associated to the ClassB callback
var callbackBlock : ((error : NSError?, message : String?, adress : String? ) -> Void)?
init() {
//Do Your staff
}
//Define your function with the clousure as a parameter
func yourFunctionWithCallback(#functionCallbackParameter : (error : NSError?,message : String?, adress : String?) -> ()) {
//Set the calback with the calback in the function parameter
self.callbackBlock = functionCallbackParameter
}
//Later On..
func callbackTrigger() {
self.callbackBlock?(error: nil,message: "Hello callback", adress: "I don't know")
}
}
And ClassB:
//ClassB it's the callback reciver the callback
class ClassB {
#IBAction func testCallbackFunction(sender: UIButton) {
let classA = ClassA()
classA.yourFunctionWithCallback { (error, message, adress) -> () in
//Do your stuff
}
}
}
ClassA: it's the owns a property witch is the callbackBlock. ClassB will set this property by Call the yourFunctionWithCallback function. Later on then ClassA it's ready, will trigger the callback by calling the callBackBlock inside the callbackTrigger function.
ClassB: will call the ClassA method to set the callback block and wait until the block has been triggered.
Can NSThread help you? :
NSThread.detachNewThreadSelector(<#selector: Selector#>, toTarget: <#AnyObject?#>, withObject: <#AnyObject?#>)

Resources