Unit testing for URLSessionDelegate method - Swift - swift

I have a custom delegate class called CertificatePinningDelegate which conforms to URLSessionDelegate. I'm using the delegate method
urlSession(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: #escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void)
to authenticate with the server. Now I want to write a unit test for this.
I've been trying to manually call the delegate method to pass a custom URLAuthenticationChallenge object, which seems impossible. Does anyone know any alternative/better option for this?

Related

Optional #escaping Closure?

I want to use Optional #escaping closures to execute something when the closure is called in the function, but if i make it optional like that:
func checkForFavorites(_ completion: (#escaping (Bool) -> Void)?) {
}
i get this error
#escaping attribute may only be used in function parameter position
This may be an easy question so please have understanding

Urban Airship receivedBackgroundNotification never called

After reading all the guides, and after checking hundred of articles on the internet, I'm quite sure that the method receivedBackgroundNotification is never called.
Everything works perfect, but when the app is in background, a notification is shown and this method never is called. Seems to be impossible to get it working.
Assuming all the normal operations and the basic configuration is well done and is working, what can I do to intercept and manage background push notifications with this library?
I will appreciate a lot any help.
Make sure you have the following configured:
Remote notifications background mode enabled in the target's capabilities
Background app refreshed is enabled on your test device
Assuming you are trying to use the UAPushNotificationDelegate, make sure you either have automatic setup enabled or you are forwarding all the proper methods to UA SDK.
Apple will only wake up your application if you send the push notification with content-available=1 in the payload. The option is exposed in the composer as "background processing" or you can set it in the iOS overrides when using the push api.
In Urban Airship iOS SDK v.13.4.0 if func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) is not called you can handle notifications in func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) if you display notifications as alerts. Remember that you should only handle the notifications with actionIdentifier == UNNotificationDefaultActionIdentifier (the user opened the app from the notification interface).
This is an example of UAPushNotificationDelegate implementation:
extension MyPushNotificationDelegate: UAPushNotificationDelegate {
public func receivedForegroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping () -> Void) {
completionHandler()
}
public func receivedBackgroundNotification(_ notificationContent: UANotificationContent, completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(.newData)
}
public func receivedNotificationResponse(_ notificationResponse: UANotificationResponse, completionHandler: #escaping () -> Void) {
guard notificationResponse.actionIdentifier == UNNotificationDefaultActionIdentifier else {
completionHandler()
return
}
someFunc() {
completionHandler()
}
}
public func extend(_ options: UNNotificationPresentationOptions = [], notification: UNNotification) -> UNNotificationPresentationOptions {
[.alert, .badge, .sound]
}
}

Error: Generic parameter 'T' could not be inferred.

I have a problem with calling this method:
func setUpFeedbackForm<T:UIViewController>(viewController: T,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ())
where T:FeedbackFormDelegate { ... }
inside this wrapper function:
public class func setUpFeedbackFormWrapper(viewController: UIViewController,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
setUpFeedbackForm(viewController: viewController,
viewForScreenshot: viewForScreenshot,
completionHandler: completionHandler)
}
I am getting error: Generic parameter 'T' could not be inferred. I do understand what the error means, but I don't know how to implement this call correctly. Also the reason why I have this wrapper is that I want to expose func setUpFeedbackForm to obj-c code and I can't import it directly to obj-c because of swift's generics.
Could you please show me the correct way of calling it?
You have two constraints on the viewController parameter that need to be satisfied when calling setUpFeedbackForm:
inheritance from UIViewController
conformance to FeedbackFormDelegate
, and setUpFeedbackFormWrapper satisfies only one, thus the compiler doesn't know what to do with the other one.
The problem is caused by a limitation of Swift which can't directly express variables/parameters that satisfy both class inheritance and protocol conformance, unless using generics, which breaks the Objective-C compatibility.
Thus a valid UIViewController<FeedbackFormDelegate> construct in Objective-C doesn't have a direct equivalent in Swift.
A workaround to this limitation is to declare a 3rd method that exposes the class inheritance and protocol conformance arguments as two distinct parameters, and call that method from both the Objective-C compatible and the Swift-only versions.
func setUpFeedbackForm<T:UIViewController>(viewController: T,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ())
where T:FeedbackFormDelegate {
setupFeedbackFormImpl(viewController: viewController,
feedbackFormDelegate: viewController,
viewForScreenshot: viewForScreenshot, completionHandler: completionHandler)
}
func setupFeedbackFormImpl(viewController: UIViewController,
feedbackFormDelegate: FeedbackFormDelegate,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
// actual code here
}
public func setUpFeedbackFormWrapper(viewController: UIViewController,
viewForScreenshot: UIView,
completionHandler: #escaping () -> ()) {
guard let feedbackFormDelegate = viewController as? FeedbackFormDelegate else {
// you can also report errors here, if you want to
// forbid runtime calls with controllers that are not FeedbackFormDelegate
return
}
setupFeedbackFormImpl(viewController: viewController,
feedbackFormDelegate: feedbackFormDelegate,
viewForScreenshot: viewForScreenshot,
completionHandler: completionHandler)
}
If we think in terms of SOLID programming, then this workaround follows the Interface Segregation Principle, as we receive one argument for the view controller stuff, and another one for the delegate stuff, even if they point to the same object behind.

WCSession Delegate | Is it necessary to call reply handler synchronously?

Is it necessary to call replyHandler before delegate function returns? I need to make few Web Service API calls before I can reply, is following implementation correct?
func session(_ session: WCSession, didReceiveMessage message: [String : Any], replyHandler: #escaping ([String : Any]) -> Void) {
DispatchQueue.main.async {
//Following function makes couple of API calls inside
//UIBackgroundTask and replies when
//background task time expires
//or response from API is received.
self.handleMessage(message, replyHandler: replyHandler)
}
}
No, it's fine to grab a copy of the block and call it later, though if you wait too long and your app is running in the background you may get suspended.

"Does not conform to protocol" error when extending different class

I'm attempting to test my own class by injecting objects that adapt the URLSession and URLSessionDataTask protocols. I'm extending NSURLSession and NSURLSessionDataTask to adopt those protocols so that I can use the existing objects normally, but use test objects in unit tests.
I have the following code, with the error commented:
typealias SessionHandler = (NSData?, NSURLResponse?, NSError?) -> Void
protocol URLSession {
func dataTaskWithURL(url: NSURL, completionHandler: SessionHandler) -> URLSessionDataTask
}
protocol URLSessionDataTask {
}
// Type 'NSURLSession' does not conform to protocol 'URLSession'
extension NSURLSession : URLSession {}
extension NSURLSessionDataTask : URLSessionDataTask {}
I understand the error, my protocol doesn't exactly match the method as implimented by NSURLSession. How do I fix this?
What I ended up doing was creating a protocol extension that creates the necessary method that NSURLSession requires.
extension NSURLSession : URLSession {
func dataTaskWithURL(url: NSURL, completionHandler: SessionHandler) -> URLSessionDataTask {
return dataTaskWithURL(url, completionHandler: completionHandler) as NSURLSessionDataTask
}
}