How to swizzle URLSession class method data(for:) in swift 5 - ios

I want to swizzle URLSession class method data(for:) but not been able to get it done.
I tried this swizzling in Swift but it not working, bellow is the error:
the code:
#selector(URLSession.data(for:))
the error:
Please help me.
Update 1:
My Problem:
I have swizzle URLSession with
#selector(URLSession.dataTask(with:) as (URLSession) -> (URLRequest) -> URLSessionDataTask)
and
#selector(URLSession.dataTask(with:) as (URLSession) -> (URL) -> URLSessionDataTask).
It works fine before iOS 16+ with the follow code: try await URLSession.shared.data(for: request). I don't know why, So, I want swizzle the data(for:) method to test my guess, but it fails.

Related

GoogleSignIn (iOS) getTokensWithHandler doesn't call its closure after token has expired

I am using Google Sign-In 5.0.2 with a Swift 4 iOS app. Here is my code to get a current id Token:
public static func getJwtToken(completion: #escaping (Result<String, Error>) -> Void) {
assert(Config.isAuthEnabled())
GIDSignIn.sharedInstance()?.currentUser?.authentication.getTokensWithHandler({ gidAuth, error in
if let error = error {
completion(.failure(error))
} else if let token = gidAuth?.idToken {
completion(.success(token))
} else {
assertionFailure("shouldn't have come here")
}
})
}
This works fine when I launch the app.
But if I leave the app running for an hour (to let the id token expire), then the next time getTokensWithHandler is called, it will not call my closure. Subsequent calls to getTokensWithHandler will once again call my closure.
I would like it to behave consistently so that my closure is always called, even if the token needs to be refreshed.
Anyone have any ideas on what I need to do to achieve this?
I figured out the answer to this problem after spending some more time on it.
My problem was I was trying to hack the Google SDK's closure to be synchronous instead of just letting it call the closure when it was ready. The function call that I claimed never called its closure would've called it if I had let the program flow naturally instead of trying to make it call the closure when I expected it to be called. I was using a semaphore somewhere in the call stack that was waiting until the closure got called and I just needed stop making assumptions about when Google's SDK would perform its work.
I'm still trying to get used to this async-first paradigm of Swift.

SiriKit: How to set output property when intent is handled in the main app?

Context
I'm working on adding support for Siri Shortcuts (Intents?) to a non-trivial app. My main goal is to allow users to automatise some tasks within the app using Shortcuts.app.
I have defined MyIntent in the Intents.intentdefinition in the Intents Extension target, together with MyIntentResponse, which has an output property file: INFile.
Given the non-trivial nature of the app, I'm forwarding the intent from Intent Extension to the main app:
func handle(intent: MyIntent, completion: #escaping (MyIntentResponse) -> Void) {
// Can't handle the intent in the app extension yet, let the main app handle it
let response = MyIntentResponse(code: .continueInApp, userActivity: nil)
// TODO: How to update the response (with an output property) if the intent continues in the app?
completion(response)
}
Problem
Once the intent is forwarded to the app, the AppDelegate.application(_:continue:restorationHandler:) method is called with a user activity which also has the property interaction: INInteraction? set.
From the provided INInteraction I can get both the intent: INIntent and response: INIntentResponse to handle the intent accordingly.
What's missing, however, is a way how to communicate back to the Shortcuts.app about the result of the intent and provide the desired output property (file: INFile).
Question
Is there a way how to provide an intent response with an output property set if the intent is handled in the main app?
Details
Interestingly, there's a AppDelegate method that should handle this use case:
optional func application(_ application: UIApplication,
handle intent: INIntent,
completionHandler: #escaping (INIntentResponse) -> Void)
And the documentation says:
An app with an Intents app extension can use this method to handle an intent directly, instead of handling it in the app extension. You might use this method to implement workflows that you cannot implement easily in your extension. For example, you might use it to start or manage a user's workout session. If your app is not running, SiriKit launches your app in the background so that the Siri interface remains active.
More info: https://developer.apple.com/documentation/uikit/uiapplicationdelegate/2887573-application
In theory, it should be possible just to call the completionHandler with a newly instantiated MyIntentResponse which would have file property set correctly.
However, this method is never called. Instead, the above mentioned AppDelegate.application(_:continue:restorationHandler:) is called.
If you use .handleInApp instead of .continueInApp in your Intents extension handle() function, then your app delegate's handleIntent() function will be called instead of the continue:restorationHandler() function. This will allow you to pass back INIntentResponse back to the extension in the completionHandler.

SiriKit Intent Handler not being called

I have an app that incorporated Siri Intents and they work well. You can have Siri launch the app and it will perform the action as expected. All of my intents need to happen in the app so I do not have an extension for them.
However using the Shortcuts app, when I use my shortcut the app will just stop in my app and not continue so Siri is not receiving the handler response.
Right now my delegate will open from an action inside of the NSUserActivity call and I will use a custom class to determine which Shortcut it is and perform said action.
I do have that custom class conforming to the IntentHandler Protocol for each action and I call a .success response in the completion handler.
Heres where I think I may be mistaken. I manually call the Handler protocol inside of my custom class.
func handleSiri(_ intent: INItent) {
if intent is ActionIntent {
func handle(intent: ActionIntent, completion: #escaping (ActionIntent Response) -> Void) {
let response = DisconnectIntentResponse.init(code: .success, userActivity: nil)
print("Intent was a success")
completion(response)
}
}
}
However Siri never completes. Should I be calling this manually? Do I need to have an extension call these functions?
The only reason why this is not called is because you have not added the Voice Intent extension into your dependencies list under Build Phase section for your App Target.

Alamofire 4: Cannot call value of non-function type 'HTTPURLResponse?'

Using Alamofire 4 with Swift 3.2. The code in question worked at one point, but I had to move it. Now no matter how I simplify it, it won't compile. I get the error "Cannot call value of non-function type 'HTTPURLResponse?'" on the .response( line.
override func doRequest(_ request : URLRequest, call: AlamoRequest) {
Alamofire.request(request)
.validate(statusCode: 200..<400)
.response(completionHandler: { (theResponse) in
self.delegate.handleUnserializedRequest(theResponse, call: call)
})
}
And as usual, the act of forcing myself to post this question actually immediately led me to the answer.
The problem was that my delegate's implementation of handleUnserializedRequest was a private method. Commenting it out was the clue, suddenly the error disappeared. Then I realized the problem was actually on the line in the completion method.

How to fix "NSURLErrorDomain error code -999" in iOS

I've been trying to use Corona SDK's Facebook API to post the score on the game I'm developing on facebook. However, I'm having a problem with it. During the first time I try to post to facebook, I get this error after login and user authentication:
NSURLErrorDomain error code -999
Then, it won't post on facebook. What are possible causes of this error and how can I address it?
By the way, I am not using webview on my app. Just the widget api and a show_dialog listener in my Facebook class.
The error has been documented on the Mac Developer Library(iOS docs)
The concerned segment from the documentation will be:
URL Loading System Error Codes
These values are returned as the error code property of an NSError
object with the domain “NSURLErrorDomain”.
enum
{
NSURLErrorUnknown = -1,
NSURLErrorCancelled = -999,
NSURLErrorBadURL = -1000,
NSURLErrorTimedOut = -1001,
As you can see; -999 is caused by ErrorCancelled. This means: another request is made before the previous request is completed.
Just wanted to add here, when receiving a -999 "cancelled" the problem usually is one of two things:
You're executing the exact same request again.
You're maintaining a weak reference to your manager object that gets deallocated prematurely. (Create strong reference)
hjpotter92 is absolutely right, I just want to provide solution for my case. Hopefully it is useful for you as well. Here is my situation:
On log in page > press log in > pop up loading dialog > call log in service > dismiss dialog > push another screen > call another service --> cause error -999
To fix it, I put a delay between dismissing dialog and pushing new screen:
[indicatorAlert dismissWithClickedButtonIndex:0 animated:YES];
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC), dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"HomeSegue" sender:nil];
});
It is strange that this issue happens on iOS 7 only.
I have faced the same error with Alamofire and it was because the certificate pinning.
The certificate wasn't valid anymore, so I had to remove it and add the new one.
Hope it helps.
In addition to what Ramon wrote, there is a third possible reason when receiving a NSURLErrorDomain -999 cancelled:
You cancelled the task while it was executing either by calling .cancel() on the datatask object or because you used .invalidateAndCancel() on the session object. If you are creating a custom session with a delegate, you should call .invalidateAndCancel() or .finishTasksAndInvalidate() to resolve the strong reference between the session and its delegate, as mentioned in the Apple Developer Documentation:
The session object keeps a strong reference to the delegate until your app exits or explicitly invalidates the session. If you don’t invalidate the session, your app leaks memory until it exits.
If you are wondering about this logging behaviour, I found the following explanation in the Apple Developer forums:
By way of explanation, back in iOS 10 we introduced a new logging system-wide logging architecture (watch WWDC 2016 Session 721 Unified Logging and Activity Tracing for the details) and lots of subsystem, including CFNetwork, are in the process of moving over to that. Until that move is fully finished you’re going to encounter some weird edge cases like this one.
I didn't use Corona SDK's Facebook API but I encountered this problem when using Alamofire, the secondRequest always cancel in execution with the error -999, according to the posts I found on internet, the reason is that session property is deinit before completion of async work since it is out of the scope, I finally solved this problem by deinit the session property manually so the compiler won't deinit it at wrong position:
class SessionManager {
var session:SessionManager?
init() {
self.session = SessionManager(configuration:URLSessionConfiguration.ephemeral)
}
private func firstRequest() {
guard let session = self.session else {return}
session.request(request_url).responseData {response in
if let data=response.data {
self.secondRequest()
}
}
private func secondRequest() {
guard let session = self.session else {return}
session.request(request_url).responseData {response in
if let data=response.data {
self.secondRequest()
}
//session will no longer be needed, deinit it
self.session = nil
}
}
Our company's app has many -999 error in iOS. I have searched around, find the reason has two, like the network task has been dealloc or the certificate isn't valid. But I have checked our code, these two aren't possible. I am using Alamofire
which is using URLSession. Luckily, our company's android app's network is normal. So we check the difference. We found the http request from iOS is Http2.0, while android is Http1.1. So we force the backend http support version down to http1.1, then -999 error count descends!!!
I think there maybe some bug in Apple's URLSession. Check the link New NSURLSession for every DataTask overkill? for some detail thoughts
Please check If you call cancel() on URLSessionDataTask to fix
NSURLErrorDomain Code=-999 "cancelled"
I was getting this error in iOS specific version of Xamarin app. Not sure the underlying cause, but in my case was able to work around it by using post method instead of get for anything passing the server context in the request body -- which makes more sense anyway. Android / Windows / the service all handle the GET with content, but in iOS app will become partially unresponsive then spit out the 999 NSUrlErrorDomain stuff in the log. Hopefully, that helps someone else running into this. I assume the net code is getting stuck in a loop, but could not see the code in question.
For my Cordova project (or similar), turns out it was a plugin issue. Make sure you're not missing any plugins and make sure they're installed properly without issue.
Easiest way to verify this is simply to start fresh by recreating the Cordova project (cordova create <path>) along with the required platforms (cordova platform add <platform name>) and add each plugin with the verbose flag (--verbose) so that you can see if anything went wrong in the console log while the plugin is being downloaded, added to project and installed for each platform (cordova plugin add cordova-plugin-device --verbose)
Recap:
cordova create <path>
cordova platform add <platform name>
cordova plugin add cordova-plugin-device --verbose
For my case, I used an upload task post that did not need body contents:
// The `from: nil` induces error "cancelled" code -999
let task = session.uploadTask(with: urlRequest, from: nil, completionHandler: handler)
The fix is to use zero byte data instead of nil,
let task = session.uploadTask(with: urlRequest, from: Data(), completionHandler: handler)
The framework documentation doesn't specify why the from bodyData is an optional type, or what happens when it is nil.
We solved this problem by reloading the web view when it failed loading.
extension WebViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
webView.reload()
}
}

Resources