Skip to content
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

[Change] Omit objc_getClassList calls from swizzling #1099

Merged
merged 5 commits into from
Jun 2, 2022

Conversation

jkasten2
Copy link
Member

@jkasten2 jkasten2 commented May 31, 2022

Description

One Line Summary

Remove calls to objc_getClassList from swizzling code as it creates issues with other libraries as it can trigger +initialize to be called before +load for some classes.

Details

Motivation

Fixes compatibility with Kotlin pre-1.7.0 (issue #1042) as well as fixes another similar crash. This also should improve startup time, by preventing the SDK from loading all classes at once on the main thread, issue #307.

Scope

This effect which classes are swizzled, implementation has been simplified to swizzle only the class of the instance passed in, instead of trying look at the parent and sub classes.

Testing

Unit testing

Added new tests for inheritance cases to ensure the simplified logic can still cover all cases.

Manual testing

Tested on an iPhone 6s with iOS 14.4.1
Tested the the following projects:

  • SwiftUI
  • Objective-C
  • Some light tests with Flutter with FlutterFire

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
    • Swizzling
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible
  • I have reviewed this PR myself, ensuring it meets each checklist item

This change is Reviewable

We have logic in our swizzling checking for parent classes however there
were no tests covering this.

testAppDelegateInheritsFromBaseWhereBothHaveSelectorsButSuperIsNotCalled
is a test that was added here that fails, uncovers a bug with our
swizzling!
The issue is injectToProperClass takes in a list of child classes
however it only swizzles the first one instead of all of them, which
we my swizzle one that isn't even in use. However this raises the
question why did we not keep it simple and just swizzle the passed in
delegate instance? That will be something to solve in the next commit!
The class that was passed in is the only thing that needs to be
swizzled, the checks to try to swizzle the parent class or child classes
was found to be unnecessary. In the last commit a number of tests
starting with testAppDelegateInheritsFromBase* were added to cover all
possible inheritance scenarios and these still pass after making this
change.
testAppDelegateInheritsFromBaseWhereBothHaveSelectorsButSuperIsNotCalled
was one test that was actually failing but now passes after this change.

Research was done to find the reason behind
getClassWithProtocolInHierarchy to see why it was added incase I
overlooked something. The root of the change was found in the following:
GameThrive/ios-push-plugin@e656e99#diff-7ebd209501fe17a14bf53eaa7a246d9fe9f7b4b1d5139cdd2eb672fb3ae2fc29R900
Unfortunately the details are light, it notes that
"...a class that inherits from your AppDelegate class", however we have
added a unit test for this and things are working as expected. Since
this was added ~8 years ago so many things have change both in this code
base as well as iOS itself the original cause my no longer be an issue.

Since getClassWithProtocolInHierarchy was removed we no longer need
ClassGetSubclasses. This is because [delegate class] is the concrete
class.
The last commit removed the usage of getClassWithProtocolInHierarchy and
ClassGetSubclasses in swizzling and this PR is cleaning up the code
itself. Was also able to remove checkIfInstanceOverridesSelector, since
it was only used in relation to these methods. injectToProperClass can
be removed, however will do so in a different comment since this will
touch a number of files.
@jkasten2 jkasten2 requested review from nan-li and emawby May 31, 2022 23:56
Since the last commit no more rule is needed to for sub classes so
injectSelector was replace everywhere in the code base.

The parameters were reordered on injectSelector to make it more
readable. Starting with the original class and selector then followed by
the replacement class and selector is easier see what is targeting and
what it is changing to.
@jkasten2 jkasten2 force-pushed the omit_objc_getClassList_calls_for_swizzling branch from a44350b to cde72e0 Compare June 1, 2022 05:24
@jkasten2 jkasten2 merged commit 52e05d9 into main Jun 2, 2022
@jkasten2 jkasten2 deleted the omit_objc_getClassList_calls_for_swizzling branch June 2, 2022 17:41
@emawby emawby mentioned this pull request Jun 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants