-
Notifications
You must be signed in to change notification settings - Fork 151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Invalid cast in GTMSessionFetcherService #190
Comments
This sounds like what also came up in #130, because of swizzling, these things in changing the internals of the library, but not doing it in safe ways. We landed on workaround to try an address this, but from looking at this last time, the other SDKs that are doing this should also be properly forwarding selectors when the hook things like this. The delegate in question here should be an internal detail, and if they are swapping it out without ensure everything is forwarded correctly, nothing says the library will work correctly. |
From my understanding, this library expects that the delegate for the common Other libraries, that do swizzling, do forward method calls for delegate, but they cannot and should not implement methods from The correct solution might be to store So returning nil when type of delegate is not what is expected isn't correct? Does it break other things? Please correct me if I misunderstand the code of the library. |
Swizzling is almost definitionally unsafe, and I don't think it's reasonable to expect libraries to bend over backward to support other developers swizzling anything and everything out from under them. In a case like this, when code swizzles out the delegate from a system object, it should be certain to forward any unknown methods to the original delegate. It's not a matter of implementing those methods, but rather making sure to forward unknown methods, for which Objective-C has built-in facilities for. This would be an issue of forward-safety, too—if a new SDK includes delegate methods, and the swizzling library doesn't forward them correctly but is also not updated, the relevant methods will not be called. |
I see your point. I agree that object that does swizzling should forward all unknown methods back to original object. Here is TrustKit code, for example: It seems they are doing all that and it still somehow crashes. Do you see any problems with their code? What about the fix I proposed? Will it cause additional problems if We cannot get rid of TrustKit or SplunkMint and need to use FirebaseAuth, so need some solution. Any advice? |
Some more information after more debugging:
The result:
Note the class of the object where the selector was sent/forwarded to: Thoughts? |
Do you expect proxy class ( - (BOOL)isKindOfClass:(Class)aClass
{
return [super isKindOfClass:aClass] || [_originalDelegate isKindOfClass:aClass];
} Where |
More details:
So when session is created inside
Then fetcher-as-delegate gets wrapped into proxy class by different 3rd-party libraries (TrustKit, SplunkMint, etc). In my case it gets wrapped twice (by both TrustKit and SplunkMint). So in this particular case there is no |
I hope this will get fixed sometime in the future. For now this is a workaround we applied: // Code to swizzle `GTMSessionFetcherService.delegateDispatcherForFetcher:` in order to fix a crash
private extension GTMSessionFetcherService {
private static var delegateDispatcherForFetcherIsSwizzled: Bool = false
static func swizzleDelegateDispatcherForFetcher() {
if delegateDispatcherForFetcherIsSwizzled {
return
}
delegateDispatcherForFetcherIsSwizzled = true
// `delegateDispatcherForFetcher:` is private and so we cannot use `#selector(..)`
let originalSelector = sel_registerName("delegateDispatcherForFetcher:")
let newSelector = #selector(new_delegateDispatcherForFetcher)
if let originalMethod = class_getInstanceMethod(self, originalSelector),
let newMethod = class_getInstanceMethod(self, newSelector) {
method_setImplementation(originalMethod, method_getImplementation(newMethod))
}
}
/*
Modified code from GTMSessionFetcherService.m:
https://github.com/google/gtm-session-fetcher/blob/c879a387e0ca4abcdff9e37eb0e826f7142342b1/Source/GTMSessionFetcherService.m#L382
Original code returns GTMSessionFetcherSessionDelegateDispatcher but it's a private class
so we are returning NSObject which is its superclass.
*/
// Internal utility. Returns a fetcher's delegate if it's a dispatcher, or nil if the fetcher
// is its own delegate (possibly via proxy) and has no dispatcher.
@objc
private func new_delegateDispatcherForFetcher(_ fetcher: GTMSessionFetcher?) -> NSObject? {
if let fetcherDelegate = fetcher?.session?.delegate,
let delegateDispatcherClass = NSClassFromString("GTMSessionFetcherSessionDelegateDispatcher"),
fetcherDelegate.isKind(of: delegateDispatcherClass) {
return fetcherDelegate as? NSObject
}
return nil
}
} Just call |
A big thank-you to @vladfilyakov, swizzling delegateDispatcherForFetcher works for me! Here's my case, it might help others: I use React Native and Firebase Auth and I have to do certificate pinning as it's a business requirement. I can't configure TrustKit manually because it's impossible to customize the NSURLSession/NSURLConnection (see facebook/react-native#27701). So it seems to me that it's the only way to go in a React Native environment. |
Hi @oailloud I have the scenario. My question, where did you put the code suggested by @vladfilyakov |
@vladfilyakov @oailloud I found the file in .../pods/GTMSessionFetcher/Core/GTMSessionFetcherService.m I have not worked on Objective C or Swift. When I try to add the code I get the following errors :
|
I had to create a .swift file and to add it to the project with XCode, it goes: import Foundation
@objc extension GTMSessionFetcherService {
private static var delegateDispatcherForFetcherIsSwizzled: Bool = false
@objc
static func swizzleDelegateDispatcherForFetcher() {
// see https://github.com/google/gtm-session-fetcher/issues/190#issuecomment-607398226
}
@objc
private func new_delegateDispatcherForFetcher(_ fetcher: GTMSessionFetcher?) -> NSObject? {
// see https://github.com/google/gtm-session-fetcher/issues/190#issuecomment-607398226
}
} |
@oailloud, Thank you for reply. Sorry couldn't get back to you, as I was building rest of the react-native project. I have the code as following in a .swift file under the project in Xcode 12.5. I get a Cannot find type 'GTMSessionFetcherService' in scope error (picture attached). I cleared the build, removed DerivedData as per usual suggestions. Can you help me why do I get this error ? Do I need to import anything extra ? I have no experience in coding Objective-C import Foundation
@objc extension GTMSessionFetcherService {
private static var delegateDispatcherForFetcherIsSwizzled: Bool = false
@objc
static func swizzleDelegateDispatcherForFetcher() {
if delegateDispatcherForFetcherIsSwizzled {
return
}
delegateDispatcherForFetcherIsSwizzled = true
// `delegateDispatcherForFetcher:` is private and so we cannot use `#selector(..)`
let originalSelector = sel_registerName("delegateDispatcherForFetcher:")
let newSelector = #selector(new_delegateDispatcherForFetcher)
if let originalMethod = class_getInstanceMethod(self, originalSelector),
let newMethod = class_getInstanceMethod(self, newSelector) {
method_setImplementation(originalMethod, method_getImplementation(newMethod))
}
}
@objc
private func new_delegateDispatcherForFetcher(_ fetcher: GTMSessionFetcher?) -> NSObject? {
if let fetcherDelegate = fetcher?.session?.delegate,
let delegateDispatcherClass = NSClassFromString("GTMSessionFetcherSessionDelegateDispatcher"),
fetcherDelegate.isKind(of: delegateDispatcherClass) {
return fetcherDelegate as? NSObject
}
return nil
}
} |
I have pretty much the same content, maybe you need to create a bridging header to use Objective C in Swift? Take a look at https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_objective-c_into_swift |
@oailloud Thank you, that helped. I had to @oailloud @vladfilyakov @blitzcrank @marcelo-schroeder I have one more question how do I access |
I personally called it in the This way the swizzling should be done for Firebase calls to come. |
Thank you so much @oailloud. This solved my issue. I had to import |
@oailloud, Now I face the same issue with Firebase storage. We use the following from firebase. Everything works fine except This happens when I try to invoke the TrustKit: Proxy-ing NSURLSessionDelegate: GTMSessionFetcherSessionDelegateDispatcher The app hangs after this As I already included the fix for GTMSessionFetcher don't know how to proceed further. Any thoughts ? "@react-native-firebase/app": "12.1.0",
"@react-native-firebase/auth": "12.1.0",
"@react-native-firebase/firestore": "12.1.0",
"@react-native-firebase/messaging": "12.1.0",
"@react-native-firebase/storage": "12.1.0", I tried with other versions of the firebase npm modules, but the error persists |
I wouldn't know, I haven't used the storage module... I would expect it not to be related to modules though, GTMSessionFetcher seems pretty transversal and not on such a high level of the lib |
Hi, Actually, I too faced the same issue, and why can't TrustKit SDK and etc avoid handling of swizzling support for GTMServiceFetcher delegate methods? [delegate class] == NSClassFromString(@"GTMSessionFetcher") Thanks, |
@mwyman Would you mind having a second look? |
For fetcher issues you’ll need to reach out to @thomasvl.On Jul 14, 2023, at 9:56 AM, Andrés Cecilia Luque ***@***.***> wrote:
@mwyman Would you mind having a second look?
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Hello
This issue was described in several tickets but solution is still not there.
There are several third-party libraries that swizzle
URLSession
'sdelegate
with their own proxy objects. For example TrustKit and SplunkMint do that. HoweverGTMSessionFetcherService
completely ignores this fact and hard-casts these proxy objects to its own class -GTMSessionFetcherSessionDelegateDispatcher
:gtm-session-fetcher/Source/GTMSessionFetcherService.m
Line 396 in c879a38
Obviously later we get crashes in different places due to "unrecognized selector sent to instance" exceptions.
The "dumb" solution is to check the class of object before casting it:
But having almost zero knowledge of how GTMSessionFetcher works, it's hard for me to understand if this code breaks something else. Any advice from the team?
We consume this library indirectly via FirebaseAuth.
Thanks,
Vlad
The text was updated successfully, but these errors were encountered: