From 2dabd9a0a9c49e7e962aecb2f98b6814a8c59e9f Mon Sep 17 00:00:00 2001 From: Diogo Autilio Date: Sun, 5 Sep 2021 12:43:22 -0300 Subject: [PATCH] Convert NBSMockedApplication from Objc to Swift --- Nimble_Snapshots.xcodeproj/project.pbxproj | 12 +- .../DynamicType/NBSMockedApplication.h | 13 -- .../DynamicType/NBSMockedApplication.m | 160 ------------------ .../DynamicType/NBSMockedApplication.swift | 150 ++++++++++++++++ Package.swift | 16 +- 5 files changed, 159 insertions(+), 192 deletions(-) delete mode 100644 Nimble_Snapshots/DynamicType/NBSMockedApplication.h delete mode 100644 Nimble_Snapshots/DynamicType/NBSMockedApplication.m create mode 100644 Nimble_Snapshots/DynamicType/NBSMockedApplication.swift diff --git a/Nimble_Snapshots.xcodeproj/project.pbxproj b/Nimble_Snapshots.xcodeproj/project.pbxproj index 1d768cb1..7c59d8f5 100644 --- a/Nimble_Snapshots.xcodeproj/project.pbxproj +++ b/Nimble_Snapshots.xcodeproj/project.pbxproj @@ -15,9 +15,8 @@ D47CB5FA1EC9973C00D5588D /* XCTestObservationCenter+CurrentTestCaseTracker.m in Sources */ = {isa = PBXBuildFile; fileRef = D47CB5F81EC9973C00D5588D /* XCTestObservationCenter+CurrentTestCaseTracker.m */; }; D4F7D47E1ED764CD00B4D2FE /* DynamicSizeSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F7D4781ED764CD00B4D2FE /* DynamicSizeSnapshot.swift */; }; D4F7D47F1ED764CD00B4D2FE /* HaveValidDynamicTypeSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F7D47A1ED764CD00B4D2FE /* HaveValidDynamicTypeSnapshot.swift */; }; - D4F7D4801ED764CD00B4D2FE /* NBSMockedApplication.h in Headers */ = {isa = PBXBuildFile; fileRef = D4F7D47B1ED764CD00B4D2FE /* NBSMockedApplication.h */; settings = {ATTRIBUTES = (Public, ); }; }; - D4F7D4811ED764CD00B4D2FE /* NBSMockedApplication.m in Sources */ = {isa = PBXBuildFile; fileRef = D4F7D47C1ED764CD00B4D2FE /* NBSMockedApplication.m */; }; D4F7D4821ED764CD00B4D2FE /* PrettyDynamicTypeSyntax.swift in Sources */ = {isa = PBXBuildFile; fileRef = D4F7D47D1ED764CD00B4D2FE /* PrettyDynamicTypeSyntax.swift */; }; + DD83832926E555BC0040266F /* NBSMockedApplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = DD83832826E555BC0040266F /* NBSMockedApplication.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -35,9 +34,8 @@ D47CB5F81EC9973C00D5588D /* XCTestObservationCenter+CurrentTestCaseTracker.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "XCTestObservationCenter+CurrentTestCaseTracker.m"; sourceTree = ""; }; D4F7D4781ED764CD00B4D2FE /* DynamicSizeSnapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicSizeSnapshot.swift; sourceTree = ""; }; D4F7D47A1ED764CD00B4D2FE /* HaveValidDynamicTypeSnapshot.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HaveValidDynamicTypeSnapshot.swift; sourceTree = ""; }; - D4F7D47B1ED764CD00B4D2FE /* NBSMockedApplication.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NBSMockedApplication.h; sourceTree = ""; }; - D4F7D47C1ED764CD00B4D2FE /* NBSMockedApplication.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NBSMockedApplication.m; sourceTree = ""; }; D4F7D47D1ED764CD00B4D2FE /* PrettyDynamicTypeSyntax.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PrettyDynamicTypeSyntax.swift; sourceTree = ""; }; + DD83832826E555BC0040266F /* NBSMockedApplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NBSMockedApplication.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -98,9 +96,8 @@ D4F7D4791ED764CD00B4D2FE /* DynamicType */ = { isa = PBXGroup; children = ( + DD83832826E555BC0040266F /* NBSMockedApplication.swift */, D4F7D47A1ED764CD00B4D2FE /* HaveValidDynamicTypeSnapshot.swift */, - D4F7D47B1ED764CD00B4D2FE /* NBSMockedApplication.h */, - D4F7D47C1ED764CD00B4D2FE /* NBSMockedApplication.m */, D4F7D47D1ED764CD00B4D2FE /* PrettyDynamicTypeSyntax.swift */, ); path = DynamicType; @@ -114,7 +111,6 @@ buildActionMask = 2147483647; files = ( D47CB5F91EC9973C00D5588D /* XCTestObservationCenter+CurrentTestCaseTracker.h in Headers */, - D4F7D4801ED764CD00B4D2FE /* NBSMockedApplication.h in Headers */, 095B47711D4773F000880922 /* Nimble_Snapshots.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; @@ -207,13 +203,13 @@ buildActionMask = 2147483647; files = ( 095B47911D47794300880922 /* PrettySyntax.swift in Sources */, - D4F7D4811ED764CD00B4D2FE /* NBSMockedApplication.m in Sources */, D47CB5FA1EC9973C00D5588D /* XCTestObservationCenter+CurrentTestCaseTracker.m in Sources */, D4F7D47F1ED764CD00B4D2FE /* HaveValidDynamicTypeSnapshot.swift in Sources */, 095B478F1D47794300880922 /* HaveValidSnapshot.swift in Sources */, 095B47901D47794300880922 /* CurrentTestCaseTracker.swift in Sources */, D4F7D4821ED764CD00B4D2FE /* PrettyDynamicTypeSyntax.swift in Sources */, D4F7D47E1ED764CD00B4D2FE /* DynamicSizeSnapshot.swift in Sources */, + DD83832926E555BC0040266F /* NBSMockedApplication.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Nimble_Snapshots/DynamicType/NBSMockedApplication.h b/Nimble_Snapshots/DynamicType/NBSMockedApplication.h deleted file mode 100644 index 8ab0b7f5..00000000 --- a/Nimble_Snapshots/DynamicType/NBSMockedApplication.h +++ /dev/null @@ -1,13 +0,0 @@ -#import -#import - -NS_ASSUME_NONNULL_BEGIN - -@interface NBSMockedApplication : NSObject - -- (void)mockPreferredContentSizeCategory:(UIContentSizeCategory)category; -- (void)stopMockingPreferredContentSizeCategory; - -@end - -NS_ASSUME_NONNULL_END diff --git a/Nimble_Snapshots/DynamicType/NBSMockedApplication.m b/Nimble_Snapshots/DynamicType/NBSMockedApplication.m deleted file mode 100644 index 2a1d66c6..00000000 --- a/Nimble_Snapshots/DynamicType/NBSMockedApplication.m +++ /dev/null @@ -1,160 +0,0 @@ -#import "NBSMockedApplication.h" -#import - -@interface NBSMockedApplication () - -@property (nonatomic) BOOL isSwizzled; - -@end - -@interface UIFont (Swizzling) - -+ (void)nbs_swizzle; - -@end - -@interface UIApplication (Swizzling) - -+ (void)nbs_swizzle; - -@property (nonatomic) UIContentSizeCategory nbs_preferredContentSizeCategory; - -@end - -@interface UITraitCollection (Swizzling) - -+ (void)nbs_swizzle; - -@end - -@implementation NBSMockedApplication - -/* On iOS 9, +[UIFont preferredFontForTextStyle:] uses -[UIApplication preferredContentSizeCategory] - to get the content size category. However, this changed on iOS 10. While I haven't found what UIFont uses to get - the current category, swizzling preferredFontForTextStyle: to use +[UIFont preferredFontForTextStyle: compatibleWithTraitCollection:] - (only available on iOS >= 10), passing an UITraitCollection with the desired contentSizeCategory. - */ - -- (void)mockPreferredContentSizeCategory:(UIContentSizeCategory)category { - UIApplication.sharedApplication.nbs_preferredContentSizeCategory = category; - - if (!self.isSwizzled) { - [UIApplication nbs_swizzle]; - [UIFont nbs_swizzle]; - [UITraitCollection nbs_swizzle]; - self.isSwizzled = YES; - } - - [[NSNotificationCenter defaultCenter] postNotificationName:UIContentSizeCategoryDidChangeNotification - object:[UIApplication sharedApplication] - userInfo:@{UIContentSizeCategoryNewValueKey: category}]; -} - -- (void)stopMockingPreferredContentSizeCategory { - if (self.isSwizzled) { - [UIApplication nbs_swizzle]; - [UIFont nbs_swizzle]; - [UITraitCollection nbs_swizzle]; - self.isSwizzled = NO; - } -} - -- (void)dealloc { - [self stopMockingPreferredContentSizeCategory]; -} - -@end - -@implementation UIFont (Swizzling) - -+ (UIFont *)nbs_preferredFontForTextStyle:(UIFontTextStyle)style { - UIContentSizeCategory category = UIApplication.sharedApplication.preferredContentSizeCategory; - if (@available(iOS 10.0, tvOS 10.0, *)) { - UITraitCollection *categoryTrait = [UITraitCollection traitCollectionWithPreferredContentSizeCategory:category]; - return [UIFont preferredFontForTextStyle:style compatibleWithTraitCollection:categoryTrait]; - } else { - return [UIFont preferredFontForTextStyle:style]; - } -} - -+ (void)nbs_swizzle { - if (![UITraitCollection instancesRespondToSelector:@selector(preferredContentSizeCategory)]) { - return; - } - - SEL selector = @selector(preferredFontForTextStyle:); - SEL replacedSelector = @selector(nbs_preferredFontForTextStyle:); - - Method originalMethod = class_getClassMethod(self, selector); - Method extendedMethod = class_getClassMethod(self, replacedSelector); - method_exchangeImplementations(originalMethod, extendedMethod); -} - -@end - -@implementation UIApplication (Swizzling) - -- (UIContentSizeCategory)nbs_preferredContentSizeCategory { - return objc_getAssociatedObject(self, @selector(nbs_preferredContentSizeCategory)); -} - -- (void)setNbs_preferredContentSizeCategory:(UIContentSizeCategory)category { - objc_setAssociatedObject(self, @selector(nbs_preferredContentSizeCategory), - category, OBJC_ASSOCIATION_COPY_NONATOMIC); -} - -+ (void)nbs_swizzle { - SEL selector = @selector(preferredContentSizeCategory); - SEL replacedSelector = @selector(nbs_preferredContentSizeCategory); - - Method originalMethod = class_getInstanceMethod(self, selector); - Method extendedMethod = class_getInstanceMethod(self, replacedSelector); - method_exchangeImplementations(originalMethod, extendedMethod); -} - -@end - -@implementation UITraitCollection (Swizzling) - -- (UIContentSizeCategory)nbs_preferredContentSizeCategory { - return UIApplication.sharedApplication.preferredContentSizeCategory; -} - -- (BOOL)nbs__changedContentSizeCategoryFromTraitCollection:(id)arg { - return YES; -} - -+ (void)nbs_swizzle { - [self nbs_swizzlePreferredContentSizeCategory]; - [self nbs_swizzleChangedContentSizeCategoryFromTraitCollection]; -} - -+ (void)nbs_swizzlePreferredContentSizeCategory { - SEL selector = @selector(preferredContentSizeCategory); - - if (![self instancesRespondToSelector:selector]) { - return; - } - - SEL replacedSelector = @selector(nbs_preferredContentSizeCategory); - - Method originalMethod = class_getInstanceMethod(self, selector); - Method extendedMethod = class_getInstanceMethod(self, replacedSelector); - method_exchangeImplementations(originalMethod, extendedMethod); -} - -+ (void)nbs_swizzleChangedContentSizeCategoryFromTraitCollection { - SEL selector = sel_registerName("_changedContentSizeCategoryFromTraitCollection:"); - - if (![self instancesRespondToSelector:selector]) { - return; - } - - SEL replacedSelector = @selector(nbs__changedContentSizeCategoryFromTraitCollection:); - - Method originalMethod = class_getInstanceMethod(self, selector); - Method extendedMethod = class_getInstanceMethod(self, replacedSelector); - method_exchangeImplementations(originalMethod, extendedMethod); -} - -@end diff --git a/Nimble_Snapshots/DynamicType/NBSMockedApplication.swift b/Nimble_Snapshots/DynamicType/NBSMockedApplication.swift new file mode 100644 index 00000000..6cc41682 --- /dev/null +++ b/Nimble_Snapshots/DynamicType/NBSMockedApplication.swift @@ -0,0 +1,150 @@ +import Foundation +import UIKit + +public class NBSMockedApplication { + + private var isSwizzled: Bool = false + + public init() {} + + deinit { + stopMockingPreferredContentSizeCategory() + } + + /* On iOS 9, +[UIFont preferredFontForTextStyle:] uses -[UIApplication preferredContentSizeCategory] + to get the content size category. However, this changed on iOS 10. While I haven't found what UIFont uses to get + the current category, swizzling preferredFontForTextStyle: to use +[UIFont preferredFontForTextStyle: compatibleWithTraitCollection:] + (only available on iOS >= 10), passing an UITraitCollection with the desired contentSizeCategory. + */ + public func mockPreferredContentSizeCategory(_ category: UIContentSizeCategory) { + + UIApplication.shared.nbs_preferredContentSizeCategory = category + + if !isSwizzled { + UIApplication.nbs_swizzle() + UIFont.nbs_swizzle() + UITraitCollection.nbs_swizzle() + isSwizzled = true + } + NotificationCenter.default.post(name: UIContentSizeCategory.didChangeNotification, + object: UIApplication.shared, + userInfo: [UIContentSizeCategory.newValueUserInfoKey: category]) + } + + public func stopMockingPreferredContentSizeCategory() { + if isSwizzled { + UIApplication.nbs_swizzle() + UIFont.nbs_swizzle() + UITraitCollection.nbs_swizzle() + isSwizzled = false + } + } +} + +extension UIFont { + + static func nbs_swizzle() { + if !UITraitCollection.instancesRespond(to: #selector(getter: UIApplication.preferredContentSizeCategory)) { + return + } + + let selector = #selector(UIFont.preferredFont(forTextStyle:)) + let replacedSelector = #selector(nbs_preferredFont(for:)) + + let originalMethod = class_getClassMethod(self, selector) + let extendedMethod = class_getClassMethod(self, replacedSelector) + if let originalMethod = originalMethod, let extendedMethod = extendedMethod { + method_exchangeImplementations(originalMethod, extendedMethod) + } + } + + @objc + static func nbs_preferredFont(for style: UIFont.TextStyle) -> UIFont? { + let category = UIApplication.shared.preferredContentSizeCategory + + if #available(iOS 10.0, tvOS 10.0, *) { + let categoryTrait = UITraitCollection(preferredContentSizeCategory: category) + return UIFont.preferredFont(forTextStyle: style, compatibleWith: categoryTrait) + } + return UIFont.preferredFont(forTextStyle: style) + } +} + +extension UIApplication { + + struct AssociatedKeys { + static var contentSizeCategory: UInt8 = 0 + } + + @objc var nbs_preferredContentSizeCategory: UIContentSizeCategory? { + get { + return objc_getAssociatedObject(self, &AssociatedKeys.contentSizeCategory) as? UIContentSizeCategory + } + set { + objc_setAssociatedObject(self, &AssociatedKeys.contentSizeCategory, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC) + } + } + + static func nbs_swizzle() { + let selector = #selector(getter: UIApplication.preferredContentSizeCategory) + let replacedSelector = #selector(getter: self.nbs_preferredContentSizeCategory) + + let originalMethod = class_getInstanceMethod(self, selector) + let extendedMethod = class_getInstanceMethod(self, replacedSelector) + if let originalMethod = originalMethod, let extendedMethod = extendedMethod { + method_exchangeImplementations(originalMethod, extendedMethod) + } + } +} + +extension UITraitCollection { + + @objc + func nbs_preferredContentSizeCategory() -> UIContentSizeCategory { + return UIApplication.shared.preferredContentSizeCategory + } + + @objc + func nbs__changedContentSizeCategory(fromTraitCollection arg: Any?) -> Bool { + return true + } + + static func nbs_swizzlePreferredContentSizeCategory() { + let selector = #selector(getter: UIApplication.preferredContentSizeCategory) + + if !self.instancesRespond(to: selector) { + return + } + + let replacedSelector = #selector(getter: UIApplication.nbs_preferredContentSizeCategory) + + let originalMethod = class_getInstanceMethod(self, selector) + let extendedMethod = class_getInstanceMethod(self, replacedSelector) + + if let originalMethod = originalMethod, let extendedMethod = extendedMethod { + method_exchangeImplementations(originalMethod, extendedMethod) + } + } + + static func nbs_swizzleChangedContentSizeCategoryFromTraitCollection() { + let selector = sel_registerName("_changedContentSizeCategoryFromTraitCollection:") + + if !self.instancesRespond(to: selector) { + return + } + + let replacedSelector = #selector(nbs__changedContentSizeCategory(fromTraitCollection:)) + + let originalMethod = class_getInstanceMethod(self, selector) + let extendedMethod = class_getInstanceMethod(self, replacedSelector) + + if let originalMethod = originalMethod, let extendedMethod = extendedMethod { + method_exchangeImplementations(originalMethod, extendedMethod) + } + } + + static func nbs_swizzle() { + nbs_swizzlePreferredContentSizeCategory() + nbs_swizzleChangedContentSizeCategoryFromTraitCollection() + } +} diff --git a/Package.swift b/Package.swift index f81e7893..21ca7126 100644 --- a/Package.swift +++ b/Package.swift @@ -26,16 +26,13 @@ let package = Package( path: "Nimble_Snapshots", exclude: ["XCTestObservationCenter+CurrentTestCaseTracker.h", "XCTestObservationCenter+CurrentTestCaseTracker.m", - "DynamicType/NBSMockedApplication.h", - "DynamicType/NBSMockedApplication.m", "Info.plist", "Nimble_Snapshots.xcconfig"], sources: ["CurrentTestCaseTracker.swift", "HaveValidSnapshot.swift", "PrettySyntax.swift", - "DynamicSize/DynamicSizeSnapshot.swift", - "DynamicType/HaveValidDynamicTypeSnapshot.swift", - "DynamicType/PrettyDynamicTypeSyntax.swift"] + "DynamicSize", + "DynamicType"] ), .target( name: "NimbleSnapshotsObjc", @@ -46,15 +43,12 @@ let package = Package( exclude: ["CurrentTestCaseTracker.swift", "HaveValidSnapshot.swift", "PrettySyntax.swift", - "DynamicSize/DynamicSizeSnapshot.swift", - "DynamicType/HaveValidDynamicTypeSnapshot.swift", - "DynamicType/PrettyDynamicTypeSyntax.swift", + "DynamicSize", + "DynamicType", "Info.plist", "Nimble_Snapshots.xcconfig"], sources: ["XCTestObservationCenter+CurrentTestCaseTracker.h", - "XCTestObservationCenter+CurrentTestCaseTracker.m", - "DynamicType/NBSMockedApplication.h", - "DynamicType/NBSMockedApplication.m"] + "XCTestObservationCenter+CurrentTestCaseTracker.m"] ) ], swiftLanguageVersions: [.v4_2, .v5]