How Can I Unit Test Calling of iOS Application Delegate Methods? - ios

I have an iOS application that integrates with the GitHub API. I am unit testing my OAuth requests, which requires testing the receipt of a code from the GitHub API that I will use to exchange for a token.
In my AppDelegate.swift, I have the following method, which is used to handle the callback from GitHub when the user authorizes my application to use their GitHub account:
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return true
}
The steps are as follows:
Open the application.
Using the URL for authorizing GitHub account access (https://github.com/login/oauth/authorize), an instance of SFSafariViewController is presented, allowing the user to press the 'Authorize' button.
GitHub uses the callback URL to my application that I provided when registering my application with GitHub, which sends a notification to open my app.
The method above is executed, where I retrieve the code parameter from url.
However, I am stuck trying to find a way to test this without actually making a request to the GitHub API. I can create a URL instance that mimics what GitHub supplies my application with, but I would like to test this without making an actual request.
Is there a way to unit test this, or is this something that I shouldn't worry about since it's handled by the OS, and instead, only test my code for parsing the code parameter of a test URL?
UPDATE
After taking Jon's advice, I created a test class to allow me to simulate the GitHub callback in action:
class GitHubAuthorizationCallbackTests: XCTestCase {
let delegate = AppDelegateMock()
func test_AuthorizationCallbackFromGitHub_ApplicationOpensURL() {
guard let url = URL(string: "xxxxxxxxxxxxxx://?code=********************") else { return XCTFail("Could not construct URL") }
let isURLOpened = delegate.application(UIApplication.shared, open: url)
XCTAssertTrue(isURLOpened, "URL is not opened from GitHub authorization callback. Expected URL to be opened from GitHub authorization callback.")
}
}
Then, I created AppDelegateMock.swift to be used instead of AppDelegate.swift, adding in the intended method to be called when the GitHub callback is executed to open my app:
import UIKit
class AppDelegateMock: NSObject, UIApplicationDelegate {
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
return true
}
}
The test passes, allowing me to test the logic that I need to test for handling the code parameter that is returned from GitHub int he url parameter of the method.

Because you want to test a callback… just have tests invoke the callback directly, as if the GitHub framework invoked it.
Write simple tests around the happy path. Then, because you're dealing with external data you can't control, write tests that (if Swift allows) invoke the callback with strange options.

Related

How to fetch the appID without using Branch SDK iOS?

I am working in iOS swift project. I have integrated branch for deep link activity. In the branch link we have appended the appID and retrieved the id when the user clicked the app link using below code in didFinishLaunch in AppDelegate.
let branch: Branch = Branch.getInstance()
branch.initSession(launchOptions: launchOptions, automaticallyDisplayDeepLinkController: true, deepLinkHandler: { params, error in
if error == nil {
// parse appID
}
})
But sometimes, we can’t fetch the ID due to branch error. So we planned to remove branch integration in our code. But plist contains URL Scheme, URL identifier etc.. for navigate to this app.
So, When the user clicks the app link, how to fetch the value with out using above code?
Additional note:
The below function is not triggered in my app while clicking the app link. Is any other way to trigger this function, so that I can check the url value in the below function to processing further.
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
}

Testing passwordless auth in Firebase test lab for iOS

I am trying to figure out how to perform e2e test via firebase test lab for iOS that allow to check passwordless authentication flow, which essentially should do following
Enters email within my app
Firebase sends auth link to such email
Somehow I need to be logged into such email somewhere in firebases test device, I assume either in mail app, or gmail?
I need to know when new email arrives and open it
Once I opened an email I need to click on auth link
This should bring me back into the app and authenticate
My biggest issue at the moment is figuring out steps that happen outside my app i.e. how can I prepare for this test and log in under my email address (is it better to log into gmail in safari for example or somehow add this acc to apples mail app?).
Testing email
In my experience, testing your own code to see if an email was sent is not straightforward beyond checking if the method call you expect to send the email has happened.
Add on top of that using Firebase, which does not expose its underlying email send code, and that looks like a challenge to me.
In terms of testing, I suggest you assert that your method calls to send email happened or that the relevant code path was reached. In Firebase web, this looks like:
firebase.auth().sendSignInLinkToEmail(email, actionCodeSettings)
.then(function() {
// The link was successfully sent. Inform the user.
// Save the email locally so you don't need to ask the user for it again
// if they open the link on the same device.
window.localStorage.setItem('emailForSignIn', email);
// TODO save email to something accessible in your iOS tests
// TODO In your tests, confirm that email was saved after it was sent
})
.catch(function(error) {
// Some error occurred, you can inspect the code: error.code
});
See: https://firebase.google.com/docs/auth/web/email-link-auth#send_an_authentication_link_to_the_users_email_address
Another option:
You could setup a test user with an email address on a mail server that you manage, and check for incoming mail for that test user with your own custom mail reading code.
I would use Firebase Admin tools for this: https://firebase.google.com/docs/auth/admin/manage-users#create_a_user
I think you should first take a look at firebase docs for iOS on how to create dynamic links that you can use for email auth.
https://firebase.google.com/docs/auth/ios/email-link-auth
https://firebase.google.com/docs/auth/ios/passing-state-in-email-actions#configuring_firebase_dynamic_links
After you're done with those two check out the following code:
func application(_ application: UIApplication, open url: URL, sourceApplication: String?, annotation: Any) -> Bool {
// [END old_delegate]
if handlePasswordlessSignIn(withURL: url) {
return true
}
}
func handlePasswordlessSignIn(withURL url: URL) -> Bool {
let link = url.absoluteString
// [START is_signin_link]
if Auth.auth().isSignIn(withEmailLink: link) {
// [END is_signin_link]
UserDefaults.standard.set(link, forKey: "Link")
(window?.rootViewController as? UINavigationController)?.popToRootViewController(animated: false)
window?.rootViewController?.children[0].performSegue(withIdentifier: "passwordless", sender: nil)
return true
}
return false
}
This is just an example on how you can handle the deep link in your app after the user taps the link. The delegate method
func application(_ application: UIApplication, open url: URL,
sourceApplication: String?, annotation: Any) -> Bool
in AppDelegate is used for all deep links into an app. You could set up for example your own scheme that your app conforms to. And you can send url type links with your custom scheme into your app from the browser for example.
To do this just Open Xcode, go to Project Settings -> Info, and add inside ‘The URL Types” section a new URL scheme. Add something of the sort of com.myApp in order for it to be as unizue as possible. Then you can just type into a browser com.myApp://main and handle that in the appDelegate.
Edit: It says so in their docs that you can present a prompt inside the app for the user to input the email. Where the user opens his email from isn't really your concern as long as your dynamic link is set up properly.

iOS How to check how many request was sent for server, parse swift 4

I have something strange with my parse server, I start to check Analytics toll at the parse, and I saw during some period I have more than600 request per minutes, to the server, in my opinion, is not possible because only I am, testing app.
At image you can see a trend, I have 10-14 requests, and immediately I get 600?
How I can check how many requests are sent my app?
You can use a proxy to catch your request
-> Charles is a really useful tool for this
If you want count in your app how many are sent, it depends on how you organised your code.
-> you could create a counter for this (in your custom "ReqeustMaker" create a static variable or even in in a shared instance class if you need more fancy stuff)
Using the following code you can trap all the calls, and find them all on Xcode debug console:
class MyURLProtocol: NSURLProtocol {
class func canInit(with request: URLRequest) -> Bool {
print("Requests : \(request.url?.absoluteString ?? "")")
return false
}
}
and call this when app is loading:
URLProtocol.registerClass(MyURLProtocol.self)

Authorization callback URL GitHub

I'm beginning iOS developer. I create an application that uses GitHub authorization. When I register a new OAuth application in GitHub developer program I must enter Authorization callback URL. But I do not have any site for my app. What do I need to specify in this field?
You can use deep linking.
you can read more about it here
The deeplink will try to open the app or redirect to it. The web browser or SFAuthenticationSession will close the browser and call the completion hander where you can check for the response code without any implementation for the deeplink.
To add the deep link in the app you can this below:
Select the project in Xcode navigator.
then select your target that you want to add the deep link to it.
select info from the top bar
at the bottom open the URL Types
add a name for the scheme
when you generate the URL for the oauth you can pass anything you want I just pass login in this example:
func getAuthenticateURL() -> URL {
var urlComponent = URLComponents(string: "https://github.com/login/oauth/authorize")!
var queryItems = urlComponent.queryItems ?? []
queryItems.append(URLQueryItem(name: "client_id", value: "YOUR_CLIENT_ID_HERE"))
queryItems.append(URLQueryItem(name: "redirect_uri", value: "APP_SCHEME_GOES_HERE://login"))
urlComponent.queryItems = queryItems
return urlComponent.url!
}
Then when you need to login do this:
import SafariServices
var authSession: SFAuthenticationSession?
func authenticate(with url: URL, completion: #escaping ((_ token: String?, _ error: Error?) -> Void)) {
authSession?.cancel()
authSession = SFAuthenticationSession(url: url, callbackURLScheme: nil, completionHandler: { url, error in
//get the token and call the completion handler
})
authSession?.start()
}
or use ASWebAuthenticationSession the same way if you're on iOS 12
Using SFAuthenticationSession you can do something like this. On your App add URLType:
Then on GitHub 'Developer Settings' for your app, add Authorization callback URL like this:
This way, after you login and authorize, Git Hub will call back //yourappname and Safari will redirect it back to your app completing the flow.

run iOS app from web (angular)

I'm developing an iOS app
i have a payment page designed by angular
user click on a payment button in ios app and i run a url page with few paramaters :
UIApplication.sharedApplication().openURL(NSURL(string:"http://www.testt.com/price/personId/packageName")!)
price is money user has entered in textfield and package-name id the schema name i should send it to web page that runs my app (return to app with running that string) i have declared in info.plist
then after been successful or unsuccessful payment. it should return to app by clicking on “return to app” button on web site.
actually angular runs the packageName i have sent with url like this way : http://packageName://
i tried to implement this by universal link like this way : packageName:// but wont open this link because of special chars in url.i used encoding method to encode chars but not successful because url removes the chars :// then i tried app site association method which i faced cannot parse app site association file
so i have few question for you :
1_is there any trick to run url with special chars ??
2_what would you do if you were me ??
3_i tried apple-app-site-association too but can not parse error which i have a question about this method how could this file opens my app? this way : applink:http://msite.com ?? because it contains spacial chars in it again
excuse my awful English at the end
talk to me before voting down
update :
var encodedChars="openMyApp" //schema name
encodedChars=encodedChars.addingPercentEncoding(withAllowedCharacters:CharacterSet.alphanumerics)!
let url="http://test.com/#/payment/\(id)/\(price!)/\(encodedChars)"
UIApplication.shared.openURL(NSURL(string: url)! as URL)
angular code :
if (this.accounting.packageName === 'openMyApp') {
this.url = this.accounting.packageName + '://';
} else {
this.url = 'http://' + this.accounting.packageName;
}
<a class="btn btn-default" title="" href="{{url}}"></a>
I have implemented custom url scheme for my app. If i write some thing like this
myappName://
in safari and press enter, my app is opened.
My angular + backend developer is using this line of code to open my app using custom url scheme. he is sending parameters along the scheme which i can read
this.document.location.href = 'myappname://appname.com/login?name='+id+'&id='+token;
Follow these 2 simple steps, i hope this will help you.
1.How to implement custom url schemes
Official docs
Helpful link
2.How to handle custom URL scheme with params in app delegate
handle the custom url and read params in appDelegate in this function
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
if let query = url.query {
//that's my logic. yours url may be different
let components = query.components(separatedBy: "=")
print(components)
}
}
let me know if you need any help

Resources