diff --git a/.github/swiftformat.json b/.github/swiftformat.json new file mode 100644 index 00000000..46271304 --- /dev/null +++ b/.github/swiftformat.json @@ -0,0 +1,18 @@ +{ + "problemMatcher": [ + { + "owner": "swiftformat", + "pattern": [ + { + "regexp": "react-native-test-app\\/(?!react-native-test-app)(.*):(\\d+):(\\d+): (\\w+): \\((.*?)\\) (.*?)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "code": 5, + "message": 6 + } + ] + } + ] +} diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6ad4259e..0aaf7f37 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -57,6 +57,12 @@ jobs: echo "::remove-matcher owner=eslint-stylish::" yarn tsc yarn test:js --ci + - name: SwiftFormat + run: | + brew install swiftformat + echo "::add-matcher::.github/swiftformat.json" + yarn format:swift --lint + echo "::remove-matcher owner=swiftformat::" - name: SwiftLint run: | echo "::add-matcher::.github/swiftlint.json" diff --git a/.swiftlint.yml b/.swiftlint.yml index 7ab6fa1e..e2938795 100644 --- a/.swiftlint.yml +++ b/.swiftlint.yml @@ -1,5 +1,6 @@ disabled_rules: - function_body_length + - opening_brace # Conflicts with SwiftFormat - trailing_comma excluded: - example/*/Pods/ diff --git a/Brewfile b/Brewfile index f8872e33..d2fc0251 100644 --- a/Brewfile +++ b/Brewfile @@ -1,6 +1,7 @@ tap 'homebrew/cask-versions' brew 'clang-format' brew 'node' +brew 'swiftformat' brew 'swiftlint' brew 'yarn' cask 'adoptopenjdk8' diff --git a/ios/ReactTestApp/AppDelegate.swift b/ios/ReactTestApp/AppDelegate.swift index 0ee6acaa..74e50baa 100644 --- a/ios/ReactTestApp/AppDelegate.swift +++ b/ios/ReactTestApp/AppDelegate.swift @@ -16,26 +16,28 @@ class AppDelegate: UIResponder, UIApplicationDelegate { @objc var window: UIWindow? { get { application?.windows.first } // swiftlint:disable:next unused_setter_value - set { } + set {} } func application(_ application: UIApplication, - didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + didFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool + { self.application = application return true } // MARK: UISceneSession Lifecycle - func application(_ application: UIApplication, + func application(_: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, - options: UIScene.ConnectionOptions) -> UISceneConfiguration { + options _: UIScene.ConnectionOptions) -> UISceneConfiguration + { // Called when a new scene session is being created. // Use this method to select a configuration to create the new scene with. - return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) } - func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + func application(_: UIApplication, didDiscardSceneSessions _: Set) { // Called when the user discards a scene session. // If any sessions were discarded while the application was not running, this will be called shortly after // application:didFinishLaunchingWithOptions. diff --git a/ios/ReactTestApp/ContentView.swift b/ios/ReactTestApp/ContentView.swift index b0044033..5fdfcd34 100644 --- a/ios/ReactTestApp/ContentView.swift +++ b/ios/ReactTestApp/ContentView.swift @@ -5,8 +5,8 @@ // LICENSE file in the root directory of this source tree. // -import UIKit import QRCodeReader +import UIKit typealias NavigationLink = (String, () -> Void) @@ -30,29 +30,30 @@ final class ContentViewController: UITableViewController { title = "ReactTestApp" - #if targetEnvironment(simulator) - let keyboardShortcut = " (⌃⌘Z)" - #else - let keyboardShortcut = "" - #endif + #if targetEnvironment(simulator) + let keyboardShortcut = " (⌃⌘Z)" + #else + let keyboardShortcut = "" + #endif sections.append(SectionData( items: [], footer: "\(runtimeInfo())\n\nShake your device\(keyboardShortcut) to open the React Native debug menu." )) } - required init?(coder: NSCoder) { + @available(*, unavailable) + required init?(coder _: NSCoder) { fatalError("\(#function) has not been implemented") } // MARK: - UIViewController overrides - public override func viewDidLoad() { + override public func viewDidLoad() { super.viewDidLoad() DispatchQueue.global(qos: .userInitiated).async { [weak self] in self?.reactInstance.initReact { components in - let items: [NavigationLink] = components.map { (component) in + let items: [NavigationLink] = components.map { component in (component.displayName ?? component.appKey, { self?.navigate(to: component) }) } DispatchQueue.main.async { @@ -80,7 +81,7 @@ final class ContentViewController: UITableViewController { // MARK: - UITableViewDelegate overrides - public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + override public func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let (_, action) = sections[indexPath.section].items[indexPath.row] action() tableView.deselectRow(at: indexPath, animated: true) @@ -88,11 +89,11 @@ final class ContentViewController: UITableViewController { // MARK: - UITableViewDataSource overrides - public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { - return sections[section].items.count + override public func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { + sections[section].items.count } - public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + override public func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let (title, _) = sections[indexPath.section].items[indexPath.row] let cell = UITableViewCell(style: .default, reuseIdentifier: "cell") let presentsNewContent = indexPath.section == 0 @@ -106,19 +107,20 @@ final class ContentViewController: UITableViewController { return cell } - public override func numberOfSections(in tableView: UITableView) -> Int { - return sections.count + override public func numberOfSections(in _: UITableView) -> Int { + sections.count } - public override func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? { - return sections[section].footer + override public func tableView(_: UITableView, titleForFooterInSection section: Int) -> String? { + sections[section].footer } // MARK: - Private private func navigate(to component: Component) { guard let bridge = reactInstance.bridge, - let navigationController = navigationController else { + let navigationController = navigationController + else { return } @@ -152,7 +154,7 @@ final class ContentViewController: UITableViewController { return "React Native version: \(version)" } - @objc private func scanForQRCode(_ notification: Notification) { + @objc private func scanForQRCode(_: Notification) { let builder = QRCodeReaderViewControllerBuilder { $0.reader = QRCodeReader( metadataObjectTypes: [.qr], diff --git a/ios/ReactTestApp/Manifest.swift b/ios/ReactTestApp/Manifest.swift index 7882d4c9..19f67253 100644 --- a/ios/ReactTestApp/Manifest.swift +++ b/ios/ReactTestApp/Manifest.swift @@ -44,14 +44,15 @@ struct Manifest: Decodable { static func fromFile() -> Manifest? { guard let manifestURL = Bundle.main.url(forResource: "app", withExtension: "json"), - let data = try? Data(contentsOf: manifestURL, options: .uncached) else { + let data = try? Data(contentsOf: manifestURL, options: .uncached) + else { return nil } return from(data: data) } static func from(data: Data) -> Manifest? { - return try? JSONDecoder().decode(self, from: data) + try? JSONDecoder().decode(self, from: data) } } diff --git a/ios/ReactTestApp/ReactInstance.swift b/ios/ReactTestApp/ReactInstance.swift index 9aa2e71a..a43f9c09 100644 --- a/ios/ReactTestApp/ReactInstance.swift +++ b/ios/ReactTestApp/ReactInstance.swift @@ -12,7 +12,7 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele NSNotification.Name("ReactInstance.scanForQRCodeNotification") static func jsBundleURL() -> URL? { - return RCTBundleURLProvider.sharedSettings().jsBundleURL( + RCTBundleURLProvider.sharedSettings().jsBundleURL( forBundleRoot: "index", fallbackResource: nil ) @@ -32,7 +32,7 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele override init() { #if DEBUG - remoteBundleURL = ReactInstance.jsBundleURL() + remoteBundleURL = ReactInstance.jsBundleURL() #endif super.init() @@ -73,27 +73,27 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele object: nil ) - #if os(iOS) - NotificationCenter.default.addObserver( - self, - selector: #selector(onRemoteBundleURLReceived(_:)), - name: .didReceiveRemoteBundleURL, - object: nil - ) - #endif + #if os(iOS) + NotificationCenter.default.addObserver( + self, + selector: #selector(onRemoteBundleURLReceived(_:)), + name: .didReceiveRemoteBundleURL, + object: nil + ) + #endif - #if USE_FLIPPER - if let flipper = FlipperClient.shared() { - flipper.add(FlipperKitLayoutPlugin( - rootNode: UIApplication.shared, - with: SKDescriptorMapper() - )) - flipper.add(FKUserDefaultsPlugin(suiteName: nil)) - flipper.add(FlipperKitReactPlugin()) - flipper.add(FlipperKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter())) - flipper.start() - } - #endif + #if USE_FLIPPER + if let flipper = FlipperClient.shared() { + flipper.add(FlipperKitLayoutPlugin( + rootNode: UIApplication.shared, + with: SKDescriptorMapper() + )) + flipper.add(FKUserDefaultsPlugin(suiteName: nil)) + flipper.add(FlipperKitReactPlugin()) + flipper.add(FlipperKitNetworkPlugin(networkAdapter: SKIOSNetworkAdapter())) + flipper.start() + } + #endif } init(forTestingPurposesOnly: Bool) { @@ -128,80 +128,82 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele // MARK: - RCTBridgeDelegate details - func sourceURL(for bridge: RCTBridge?) -> URL? { + func sourceURL(for _: RCTBridge?) -> URL? { if let remoteBundleURL = remoteBundleURL { return remoteBundleURL } - #if os(iOS) - let possibleEntryFiles = [ - "index.ios", - "main.ios", - "index.mobile", - "main.mobile", - "index.native", - "main.native", - "index", - "main", - ] - #elseif os(macOS) - let possibleEntryFiles = [ - "index.macos", - "main.macos", - "index.native", - "main.native", - "index", - "main", - ] - #endif + #if os(iOS) + let possibleEntryFiles = [ + "index.ios", + "main.ios", + "index.mobile", + "main.mobile", + "index.native", + "main.native", + "index", + "main", + ] + #elseif os(macOS) + let possibleEntryFiles = [ + "index.macos", + "main.macos", + "index.native", + "main.native", + "index", + "main", + ] + #endif let jsBundleURL = possibleEntryFiles .lazy - .map({ + .map { Bundle.main.url( forResource: $0, withExtension: "jsbundle" ) - }) + } .first(where: { $0 != nil }) return jsBundleURL ?? ReactInstance.jsBundleURL() } - func extraModules(for bridge: RCTBridge!) -> [RCTBridgeModule] { - return [] + func extraModules(for _: RCTBridge!) -> [RCTBridgeModule] { + [] } // MARK: - RCTTurboModuleLookupDelegate details - func module(forName moduleName: UnsafePointer?) -> Any? { - return nil + func module(forName _: UnsafePointer?) -> Any? { + nil } - func module(forName moduleName: UnsafePointer?, warnOnLookupFailure: Bool) -> Any? { - return nil + func module(forName _: UnsafePointer?, warnOnLookupFailure _: Bool) -> Any? { + nil } - func moduleIsInitialized(_ moduleName: UnsafePointer?) -> Bool { - return false + func moduleIsInitialized(_: UnsafePointer?) -> Bool { + false } // MARK: - Private @objc private func onJavaScriptLoaded(_ notification: Notification) { guard let bridge = notification.userInfo?["bridge"] as? RCTBridge, - let currentBundleURL = bridge.bundleURL else { + let currentBundleURL = bridge.bundleURL + else { return } - RCTExecuteOnMainQueue({ [weak self] in + RCTExecuteOnMainQueue { [weak self] in guard let devMenu = bridge.devMenu else { return } devMenu.add(RCTDevMenuItem.buttonItem( titleBlock: { - currentBundleURL.isFileURL ? "Load From Dev Server" - : "Load Embedded JS Bundle" + currentBundleURL.isFileURL + ? "Load From Dev Server" + : "Load Embedded JS Bundle" }, handler: { guard let strongSelf = self else { @@ -216,15 +218,15 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele } )) - #if os(iOS) && !targetEnvironment(simulator) - devMenu.add(RCTDevMenuItem.buttonItem(withTitle: "Scan QR Code") { - NotificationCenter.default.post( - name: ReactInstance.scanForQRCodeNotification, - object: self - ) - }) - #endif - }) + #if os(iOS) && !targetEnvironment(simulator) + devMenu.add(RCTDevMenuItem.buttonItem(withTitle: "Scan QR Code") { + NotificationCenter.default.post( + name: ReactInstance.scanForQRCodeNotification, + object: self + ) + }) + #endif + } } @objc private func onJavaScriptLoading(_ notification: Notification) { @@ -241,7 +243,8 @@ final class ReactInstance: NSObject, RCTBridgeDelegate, RCTTurboModuleLookupDele @objc private func onRemoteBundleURLReceived(_ notification: Notification) { guard let value = notification.userInfo?["value"] as? String, - var urlComponents = URLComponents(string: value) else { + var urlComponents = URLComponents(string: value) + else { return } diff --git a/ios/ReactTestApp/SceneDelegate.swift b/ios/ReactTestApp/SceneDelegate.swift index 824cae81..16d4c6cb 100644 --- a/ios/ReactTestApp/SceneDelegate.swift +++ b/ios/ReactTestApp/SceneDelegate.swift @@ -9,7 +9,6 @@ import Foundation import UIKit class SceneDelegate: UIResponder, UIWindowSceneDelegate { - var window: UIWindow? var isRunningTests: Bool { @@ -18,8 +17,9 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } func scene(_ scene: UIScene, - willConnectTo session: UISceneSession, - options connectionOptions: UIScene.ConnectionOptions) { + willConnectTo _: UISceneSession, + options _: UIScene.ConnectionOptions) + { // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene // `scene`. // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. @@ -38,7 +38,7 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { } } - func sceneDidDisconnect(_ scene: UIScene) { + func sceneDidDisconnect(_: UIScene) { // Called as the scene is being released by the system. // This occurs shortly after the scene enters the background, or when its session is discarded. // Release any resources associated with this scene that can be re-created the next time the scene connects. @@ -46,22 +46,22 @@ class SceneDelegate: UIResponder, UIWindowSceneDelegate { // `application:didDiscardSceneSessions` instead). } - func sceneDidBecomeActive(_ scene: UIScene) { + func sceneDidBecomeActive(_: UIScene) { // Called when the scene has moved from an inactive state to an active state. // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. } - func sceneWillResignActive(_ scene: UIScene) { + func sceneWillResignActive(_: UIScene) { // Called when the scene will move from an active state to an inactive state. // This may occur due to temporary interruptions (ex. an incoming phone call). } - func sceneWillEnterForeground(_ scene: UIScene) { + func sceneWillEnterForeground(_: UIScene) { // Called as the scene transitions from the background to the foreground. // Use this method to undo the changes made on entering the background. } - func sceneDidEnterBackground(_ scene: UIScene) { + func sceneDidEnterBackground(_: UIScene) { // Called as the scene transitions from the foreground to the background. // Use this method to save data, release shared resources, and store enough scene-specific state information // to restore the scene back to its current state. diff --git a/macos/ReactTestApp/AppDelegate.swift b/macos/ReactTestApp/AppDelegate.swift index 7f011c46..64f2fadc 100644 --- a/macos/ReactTestApp/AppDelegate.swift +++ b/macos/ReactTestApp/AppDelegate.swift @@ -9,17 +9,17 @@ import Cocoa @NSApplicationMain final class AppDelegate: NSObject, NSApplicationDelegate { - @IBOutlet weak var reactMenu: NSMenu! + @IBOutlet var reactMenu: NSMenu! private(set) lazy var reactInstance = ReactInstance() private weak var mainWindow: NSWindow? - func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { + func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { true } - func applicationDidFinishLaunching(_ aNotification: Notification) { + func applicationDidFinishLaunching(_: Notification) { // `keyWindow` might be `nil` while loading or when the window is not // active. Use `identifier` to find our main window. let windows = NSApplication.shared.windows @@ -59,7 +59,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } } - func applicationWillTerminate(_ aNotification: Notification) { + func applicationWillTerminate(_: Notification) { // Insert code here to tear down your application } @@ -75,12 +75,12 @@ final class AppDelegate: NSObject, NSApplicationDelegate { } @IBAction - func onLoadEmbeddedBundle(_ sender: NSMenuItem) { + func onLoadEmbeddedBundle(_: NSMenuItem) { reactInstance.remoteBundleURL = nil } @IBAction - func onLoadFromDevServer(_ sender: NSMenuItem) { + func onLoadFromDevServer(_: NSMenuItem) { reactInstance.remoteBundleURL = ReactInstance.jsBundleURL() } @@ -88,7 +88,8 @@ final class AppDelegate: NSObject, NSApplicationDelegate { private func present(_ component: Component) { guard let window = mainWindow, - let bridge = reactInstance.bridge else { + let bridge = reactInstance.bridge + else { return } diff --git a/macos/ReactTestApp/ViewController.swift b/macos/ReactTestApp/ViewController.swift index 6a52474a..4cb616cc 100644 --- a/macos/ReactTestApp/ViewController.swift +++ b/macos/ReactTestApp/ViewController.swift @@ -8,13 +8,13 @@ import Cocoa final class ViewController: NSViewController { - override func viewDidLoad() { super.viewDidLoad() view.wantsLayer = true guard let layer = view.layer, - let reactMenuImage = NSImage(named: "ReactMenu") else { + let reactMenuImage = NSImage(named: "ReactMenu") + else { return } @@ -24,8 +24,7 @@ final class ViewController: NSViewController { override var representedObject: Any? { didSet { - // Update the view, if already loaded. + // Update the view, if already loaded. } } - } diff --git a/package.json b/package.json index 3ec0b6e8..b1d1a8ce 100644 --- a/package.json +++ b/package.json @@ -32,8 +32,10 @@ "clean": "npx --quiet rimraf example/node_modules/react-native-test-app && git clean -dfqx", "format:c": "clang-format -i $(git ls-files '*.cpp' '*.h' '*.m' '*.mm')", "format:js": "prettier --write $(git ls-files '*.js' '*.yml' 'test/**/*.json')", + "format:swift": "swiftformat --swiftversion 5.3 ios macos", "lint:js": "eslint $(git ls-files '*.js')", "lint:rb": "bundle exec rubocop", + "lint:swift": "swiftlint", "test:js": "jest", "test:rb": "bundle exec ruby -Ilib:test test/test_test_app.rb" },