diff --git a/.editorconfig b/.editorconfig index 65365be6..88b1541f 100644 --- a/.editorconfig +++ b/.editorconfig @@ -13,3 +13,22 @@ end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true + +[*.kt] +indent_size = 2 +max_line_length = 140 +ktlint_standard_no-wildcard-imports = disabled +ktlint_standard_filename = disabled +ktlint_standard_package-name = disabled +ktlint_standard_indent = disabled +ktlint_standard_function-signature = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_function-expression-body = disabled +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_standard_blank-line-before-declaration = disabled +ktlint_standard_import-ordering = disabled +ktlint_standard_string-template-indent = disabled +ktlint_standard_backing-property-naming = disabled +ktlint_standard_no-consecutive-comments = disabled diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cbdc6e49..bb20f048 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,6 +11,31 @@ on: - checks_requested jobs: + lint-swift: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: SwiftLint + uses: norio-nomura/action-swiftlint@3.2.1 + with: + args: --config .swiftlint.yml + + lint-kotlin: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: ktlint + uses: ScaCap/action-ktlint@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + reporter: github-pr-review + ktlint_version: "1.5.0" + android: true + lint: runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index 9745e649..e22a908a 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,6 @@ lib/ # React Native Codegen ios/generated android/generated + +# ktlint binary cache +.ktlint/ diff --git a/.swiftlint.yml b/.swiftlint.yml new file mode 100644 index 00000000..dca075ef --- /dev/null +++ b/.swiftlint.yml @@ -0,0 +1,22 @@ +included: + - ios + +excluded: + - Pods + - nitrogen/generated + +disabled_rules: + - line_length + - file_length + - type_body_length + - function_body_length + - cyclomatic_complexity + - identifier_name + - force_cast + - force_try + - nesting + - trailing_whitespace + - todo + - unused_optional_binding + +reporter: github-actions-logging diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt index e636013a..d03d07e7 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt @@ -25,12 +25,12 @@ class HybridRiveFile : HybridRiveFileSpec() { get() = riveFile?.viewModelCount?.toDouble() override fun viewModelByIndex(index: Double): HybridViewModelSpec? { - val vm = riveFile?.getViewModelByIndex(index.toInt()) ?: return null; + val vm = riveFile?.getViewModelByIndex(index.toInt()) ?: return null return HybridViewModel(vm) } override fun viewModelByName(name: String): HybridViewModelSpec? { - val vm = riveFile?.getViewModelByName(name) ?: return null; + val vm = riveFile?.getViewModelByName(name) ?: return null return HybridViewModel(vm) } diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt index bee73739..74dc9e81 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveFileFactory.kt @@ -133,4 +133,3 @@ class HybridRiveFileFactory : HybridRiveFileFactorySpec() { } } } - diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt b/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt index f379337f..ecd642eb 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridRiveView.kt @@ -103,7 +103,7 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { override fun bindViewModelInstance(viewModelInstance: HybridViewModelInstanceSpec) = executeOnUiThread { - val hybridVmi = viewModelInstance as? HybridViewModelInstance ?: return@executeOnUiThread; + val hybridVmi = viewModelInstance as? HybridViewModelInstance ?: return@executeOnUiThread view.bindViewModelInstance(hybridVmi.viewModelInstance) } @@ -147,7 +147,6 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { afterUpdate() } - override fun afterUpdate() { logged(TAG, "afterUpdate") { val hybridFile = file as? HybridRiveFile @@ -163,7 +162,7 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { layoutScaleFactor = layoutScaleFactor?.toFloat() ?: DefaultConfiguration.LAYOUTSCALEFACTOR, bindData = dataBind.toBindData() ) - view.configure(config, dataBindingChanged=dataBindingChanged, needsReload, initialUpdate= initialUpdate) + view.configure(config, dataBindingChanged = dataBindingChanged, needsReload, initialUpdate = initialUpdate) if (needsReload && hybridFile != null) { hybridFile.registerView(this) @@ -187,7 +186,7 @@ class HybridRiveView(val context: ThemedReactContext) : HybridRiveViewSpec() { } private fun executeOnUiThread(action: () -> Unit) { - context.currentActivity?.runOnUiThread() { + context.currentActivity?.runOnUiThread { try { action() } catch (e: Exception) { diff --git a/android/src/main/java/com/margelo/nitro/rive/HybridViewModel.kt b/android/src/main/java/com/margelo/nitro/rive/HybridViewModel.kt index 6660aa11..a085233f 100644 --- a/android/src/main/java/com/margelo/nitro/rive/HybridViewModel.kt +++ b/android/src/main/java/com/margelo/nitro/rive/HybridViewModel.kt @@ -18,7 +18,7 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec() override fun createInstanceByIndex(index: Double): HybridViewModelInstanceSpec? { try { val vmi = viewModel.createInstanceFromIndex(index.toInt()) - return HybridViewModelInstance(vmi); + return HybridViewModelInstance(vmi) } catch (e: ViewModelException) { return null } @@ -27,7 +27,7 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec() override fun createInstanceByName(name: String): HybridViewModelInstanceSpec? { try { val vmi = viewModel.createInstanceFromName(name) - return HybridViewModelInstance(vmi); + return HybridViewModelInstance(vmi) } catch (e: ViewModelException) { return null } @@ -36,7 +36,7 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec() override fun createDefaultInstance(): HybridViewModelInstanceSpec? { try { val vmi = viewModel.createDefaultInstance() - return HybridViewModelInstance(vmi); + return HybridViewModelInstance(vmi) } catch (e: ViewModelException) { return null } @@ -45,7 +45,7 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec() override fun createInstance(): HybridViewModelInstanceSpec? { try { val vmi = viewModel.createBlankInstance() - return HybridViewModelInstance(vmi); + return HybridViewModelInstance(vmi) } catch (e: ViewModelException) { return null } diff --git a/android/src/main/java/com/rive/RiveReactNativeView.kt b/android/src/main/java/com/rive/RiveReactNativeView.kt index cdc00515..a4cf6a2b 100644 --- a/android/src/main/java/com/rive/RiveReactNativeView.kt +++ b/android/src/main/java/com/rive/RiveReactNativeView.kt @@ -19,7 +19,6 @@ import app.rive.runtime.kotlin.core.SMIInput import app.rive.runtime.kotlin.core.SMINumber import app.rive.runtime.kotlin.core.ViewModelInstance import app.rive.runtime.kotlin.core.errors.ViewModelException -import com.margelo.nitro.core.AnyMap import com.margelo.nitro.rive.EventPropertiesOutput import com.margelo.nitro.rive.EventPropertiesOutputExtensions as EPO import com.margelo.nitro.rive.RiveEventType @@ -187,7 +186,7 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { fun play() = riveAnimationView?.play() - fun pause() = riveAnimationView?.pause(); + fun pause() = riveAnimationView?.pause() fun addEventListener(onEvent: (event: RNEvent) -> Unit) { val eventListener = object : RiveFileController.RiveEventListener { @@ -329,7 +328,7 @@ class RiveReactNativeView(context: ThemedReactContext) : FrameLayout(context) { } } - return newMap; + return newMap } /** diff --git a/ios/HybridRiveFile.swift b/ios/HybridRiveFile.swift index b3109e8b..9828a8a0 100644 --- a/ios/HybridRiveFile.swift +++ b/ios/HybridRiveFile.swift @@ -2,7 +2,7 @@ import RiveRuntime typealias ReferencedAssetCache = [String: RiveFileAsset] -class HybridRiveFile: HybridRiveFileSpec, RiveViewSource { +class HybridRiveFile: HybridRiveFileSpec, RiveViewSource { var riveFile: RiveFile? var referencedAssetCache: ReferencedAssetCache? var assetLoader: ReferencedAssetLoader? diff --git a/ios/HybridRiveFileFactory.swift b/ios/HybridRiveFileFactory.swift index c63fb586..dc8823be 100644 --- a/ios/HybridRiveFileFactory.swift +++ b/ios/HybridRiveFileFactory.swift @@ -85,8 +85,7 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl // MARK: Public Methods func fromURL(url: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws - -> Promise<(any HybridRiveFileSpec)> - { + -> Promise<(any HybridRiveFileSpec)> { return try genericFrom( check: { guard let url = URL(string: url) else { @@ -104,8 +103,7 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl } func fromFileURL(fileURL: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws - -> Promise<(any HybridRiveFileSpec)> - { + -> Promise<(any HybridRiveFileSpec)> { return try genericFrom( check: { guard let url = URL(string: fileURL) else { @@ -126,8 +124,7 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl } func fromResource(resource: String, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws - -> Promise<(any HybridRiveFileSpec)> - { + -> Promise<(any HybridRiveFileSpec)> { return try genericFrom( check: { guard Bundle.main.path(forResource: resource, ofType: "riv") != nil else { @@ -151,8 +148,7 @@ final class HybridRiveFileFactory: HybridRiveFileFactorySpec, @unchecked Sendabl func fromBytes(bytes: ArrayBufferHolder, loadCdn: Bool, referencedAssets: ReferencedAssetsType?) throws -> Promise< (any HybridRiveFileSpec) - > - { + > { let data = bytes.toData(copyIfNeeded: false) return try genericFrom( check: { data }, diff --git a/ios/HybridRiveView.swift b/ios/HybridRiveView.swift index f375c788..c14b863a 100644 --- a/ios/HybridRiveView.swift +++ b/ios/HybridRiveView.swift @@ -67,7 +67,7 @@ where Wrapped == HybridDataBindMode { class HybridRiveView: HybridRiveViewSpec { // MARK: View Props - var dataBind: HybridDataBindMode? = nil { + var dataBind: HybridDataBindMode? { didSet { if !dataBind.isEqual(to: oldValue) { dataBindingChanged = true @@ -221,7 +221,7 @@ extension HybridRiveView { func logged(tag: String, note: String? = nil, _ fn: () throws -> Void) { do { return try fn() - } catch (let e) { + } catch let e { let (errorType, errorDescription) = detectErrorType(e) let noteString = note.map { " \($0)" } ?? "" let errorMessage = "[RIVE] \(tag)\(noteString) \(errorDescription)" diff --git a/ios/ReferencedAssetLoader.swift b/ios/ReferencedAssetLoader.swift index eadf530b..f4c18c4c 100644 --- a/ios/ReferencedAssetLoader.swift +++ b/ios/ReferencedAssetLoader.swift @@ -19,7 +19,7 @@ func createIncorrectRiveURL(_ url: String) -> NSError { domain: RiveErrorDomain, code: 900, userInfo: [ NSLocalizedDescriptionKey: "Unable to download Rive file from: \(url)", - "name": "IncorrectRiveFileURL", + "name": "IncorrectRiveFileURL" ]) } @@ -64,7 +64,7 @@ final class ReferencedAssetLoader { } let request = URLRequest(url: requestUrl) - let task = queue.dataTask(with: request) { [weak self] data, response, error in + let task = queue.dataTask(with: request) { [weak self] data, _, error in if error != nil { self?.handleInvalidUrlError(url: url) onError() @@ -230,13 +230,12 @@ final class ReferencedAssetLoader { factory factoryOut: SendableRef, fileRef: SendableRef ) - -> LoadAsset? - { + -> LoadAsset? { guard let referencedAssets = referencedAssets, let referencedAssets = referencedAssets.data else { return nil } - return { (asset: RiveFileAsset, data: Data, factory: RiveFactory) -> Bool in + return { (asset: RiveFileAsset, _: Data, factory: RiveFactory) -> Bool in let assetByUniqueName = referencedAssets[asset.uniqueName()] guard let assetData = assetByUniqueName ?? referencedAssets[asset.name()] else { return false diff --git a/ios/RiveReactNativeView.swift b/ios/RiveReactNativeView.swift index e733dcbc..eac96e22 100644 --- a/ios/RiveReactNativeView.swift +++ b/ios/RiveReactNativeView.swift @@ -101,7 +101,7 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { baseViewModel?.riveModel?.disableAutoBind() case .auto: - baseViewModel?.riveModel?.enableAutoBind { instance in + baseViewModel?.riveModel?.enableAutoBind { _ in // Auto-bind callback } @@ -186,8 +186,7 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { return textRun.text() } - private func textRunOptionPath(name: String, path: String?) throws -> RiveRuntime.RiveTextValueRun - { + private func textRunOptionPath(name: String, path: String?) throws -> RiveRuntime.RiveTextValueRun { let textRun: RiveRuntime.RiveTextValueRun? if let path = path { textRun = baseViewModel?.riveModel?.artboard?.textRun(name, path: path) @@ -219,7 +218,7 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { riveView.leadingAnchor.constraint(equalTo: leadingAnchor), riveView.trailingAnchor.constraint(equalTo: trailingAnchor), riveView.topAnchor.constraint(equalTo: topAnchor), - riveView.bottomAnchor.constraint(equalTo: bottomAnchor), + riveView.bottomAnchor.constraint(equalTo: bottomAnchor) ]) } } @@ -252,8 +251,7 @@ class RiveReactNativeView: UIView, RiveStateMachineDelegate { } private func convertEventProperties(_ properties: [String: Any]?) -> [String: - EventPropertiesOutput]? - { + EventPropertiesOutput]? { guard let properties = properties else { return nil } var newMap: [String: EventPropertiesOutput] = [:] diff --git a/package.json b/package.json index 4713e057..7559b700 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,10 @@ "release": "release-it", "dev:ios": "cd example && xed ios", "dev:android": "cd example && open -a \"/Applications/Android Studio.app\" ./android", - "copy:nitrogen-config": "mkdir -p lib/nitrogen/generated/shared/json && cp nitrogen/generated/shared/json/RiveViewConfig.json lib/nitrogen/generated/shared/json/" + "copy:nitrogen-config": "mkdir -p lib/nitrogen/generated/shared/json && cp nitrogen/generated/shared/json/RiveViewConfig.json lib/nitrogen/generated/shared/json/", + "lint:swift": "./scripts/lint-swift.sh", + "lint:kotlin": "./scripts/lint-kotlin.sh", + "lint:native": "yarn lint:swift && yarn lint:kotlin" }, "keywords": [ "react-native", diff --git a/scripts/lint-kotlin.sh b/scripts/lint-kotlin.sh new file mode 100755 index 00000000..e7ac5946 --- /dev/null +++ b/scripts/lint-kotlin.sh @@ -0,0 +1,15 @@ +#!/bin/bash +set -e + +KTLINT_VERSION="1.5.0" +KTLINT_DIR=".ktlint" +KTLINT_BIN="$KTLINT_DIR/ktlint" + +if [ ! -f "$KTLINT_BIN" ]; then + echo "Downloading ktlint $KTLINT_VERSION..." + mkdir -p "$KTLINT_DIR" + curl -sSL "https://github.com/pinterest/ktlint/releases/download/${KTLINT_VERSION}/ktlint" -o "$KTLINT_BIN" + chmod +x "$KTLINT_BIN" +fi + +"$KTLINT_BIN" "android/src/**/*.kt" --reporter=plain diff --git a/scripts/lint-swift.sh b/scripts/lint-swift.sh new file mode 100755 index 00000000..b19ad38d --- /dev/null +++ b/scripts/lint-swift.sh @@ -0,0 +1,9 @@ +#!/bin/bash +set -e + +if command -v swiftlint &> /dev/null; then + swiftlint lint --config .swiftlint.yml +else + echo "SwiftLint not installed. Install via: brew install swiftlint" + exit 1 +fi