Skip to content

Commit b14769d

Browse files
committed
2.3.0-1.10.0-rc02-3
- Improves swiftformat from the output swift files
1 parent 4014905 commit b14769d

10 files changed

Lines changed: 108 additions & 22 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [2.3.0-1.10.0-rc02-3]
4+
5+
- Improves swift output to match `swiftformat` default rules
6+
7+
---
8+
39
## [2.3.0-1.10.0-rc02-2]
410

511
- Improves swift output to match `swiftformat` default rules

CHANGELOG_PLUGIN.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Changelog
22

3+
## [2.3.0-1.10.0-rc02-3]
4+
5+
- Compatible with 2.3.0-1.10.0-rc02-3
6+
7+
---
8+
39
## [2.3.0-1.10.0-rc02-2]
410

511
- Compatible with 2.3.0-1.10.0-rc02-2

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ KSP library and Gradle plugin for generating `ComposeUIViewController` and `UIVi
66

77
| Version | Kotlin | KSP | Compose Multiplatform | Xcode |
88
|-----------------------|:------:|:-----:|:---------------------:|:------:|
9-
| `2.3.0-1.10.0-rc02-2` | 2.3.0 | 2.3.4 | 1.10.0-rc02 | 26.2.0 |
9+
| `2.3.0-1.10.0-rc02-3` | 2.3.0 | 2.3.4 | 1.10.0-rc02 | 26.2.0 |
1010

1111
[![Android Weekly](https://androidweekly.net/issues/issue-583/badge)](https://androidweekly.net/issues/issue-583) [![Featured in Kotlin Weekly - Issue #378](https://img.shields.io/badge/Featured_in_Kotlin_Weekly-Issue_%23378-7878b4)](https://mailchi.mp/kotlinweekly/kotlin-weekly-378) [![Featured in Kotlin Weekly - Issue #389](https://img.shields.io/badge/Featured_in_Kotlin_Weekly-Issue_%23389-7878b4)](https://mailchi.mp/kotlinweekly/kotlin-weekly-389) <a href="https://jetc.dev/issues/177.html"><img src="https://img.shields.io/badge/As_Seen_In-jetc.dev_Newsletter_Issue_%23177-blue?logo=Jetpack+Compose&amp;logoColor=white" alt="As Seen In - jetc.dev Newsletter Issue #177"></a> <a href="https://jetc.dev/issues/188.html"><img src="https://img.shields.io/badge/As_Seen_In-jetc.dev_Newsletter_Issue_%23188-blue?logo=Jetpack+Compose&amp;logoColor=white" alt="As Seen In - jetc.dev Newsletter Issue #188"></a>
1212

build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ buildscript {
1515

1616
allprojects {
1717
group = "com.github.guilhe.kmp"
18-
version = "2.3.0-1.10.0-rc02-2"
18+
version = "2.3.0-1.10.0-rc02-3"
1919
}
2020

2121
dependencies {

kmp-composeuiviewcontroller-gradle-plugin/build.gradle.kts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ dependencies {
2727
testImplementation(libs.test.kotlin)
2828
}
2929

30-
version = "2.3.0-1.10.0-rc02-2"
30+
version = "2.3.0-1.10.0-rc02-3"
3131
group = "io.github.guilhe.kmp"
3232

3333
gradlePlugin {

kmp-composeuiviewcontroller-gradle-plugin/src/main/kotlin/com/github/guilhe/kmp/composeuiviewcontroller/gradle/KmpComposeUIViewControllerPlugin.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@ public class KmpComposeUIViewControllerPlugin : Plugin<Project> {
313313
listOf(iosAppFolderName, iosAppName, targetName, autoExport, exportFolderName)
314314

315315
internal companion object {
316-
private const val VERSION_LIBRARY = "2.3.0-1.10.0-rc02-2"
316+
private const val VERSION_LIBRARY = "2.3.0-1.10.0-rc02-3"
317317
private const val LOG_TAG = "KmpComposeUIViewControllerPlugin"
318318
internal const val PLUGIN_KMP = "org.jetbrains.kotlin.multiplatform"
319319
internal const val PLUGIN_KSP = "com.google.devtools.ksp"

kmp-composeuiviewcontroller-ksp/src/main/kotlin/com/github/guilhe/kmp/composeuiviewcontroller/ksp/Processor.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,7 +357,8 @@ internal class Processor(
357357
}
358358
}
359359
""".trimIndent()
360-
val updatedCode = indentParameters(indentParameters(code, frameworks), letParameters)
360+
val indentedCode = indentParameters(indentParameters(code, frameworks), letParameters)
361+
val updatedCode = removeEmptyLineBetweenStructAndFunc(indentedCode)
361362
codeGenerator
362363
.createNewFile(
363364
dependencies = Dependencies(true),

kmp-composeuiviewcontroller-ksp/src/main/kotlin/com/github/guilhe/kmp/composeuiviewcontroller/ksp/Utils.kt

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,28 @@ private fun removeAdjacentEmptyLines(list: List<String>): List<String> {
239239
}.toList()
240240
}
241241

242+
/**
243+
* Removes empty lines between struct declaration and the first public func in generated Swift code
244+
* when there are no parameters. This ensures proper formatting ([swiftformat](https://github.com/nicklockwood/SwiftFormat)) of the generated code.
245+
*/
246+
internal fun removeEmptyLineBetweenStructAndFunc(code: String): String {
247+
val lines = code.lines().toMutableList()
248+
var i = 0
249+
while (i < lines.size - 1) {
250+
val currentLine = lines[i].trim()
251+
val nextLine = lines.getOrNull(i + 1)?.trim() ?: ""
252+
253+
if (currentLine.endsWith("UIViewControllerRepresentable {") &&
254+
nextLine.isEmpty() &&
255+
i + 2 < lines.size &&
256+
lines[i + 2].trim().startsWith("public func")) {
257+
lines.removeAt(i + 1)
258+
}
259+
i++
260+
}
261+
return lines.joinToString("\n")
262+
}
263+
242264
/**
243265
* Iterates all parameters and returns package names that do not belong to the module's [packageName].
244266
*
@@ -357,4 +379,5 @@ internal class ValueParameterResolutionError(parameter: KSValueParameter) : Ille
357379
internal class TypeResolutionError(parameter: KSType) : IllegalArgumentException("Cannot resolve type for parameter $parameter.")
358380

359381
internal class ModuleDecodeException(e: Exception) :
360-
IllegalArgumentException("Could not decode $FILE_NAME_ARGS file with exception: ${e.localizedMessage}")
382+
IllegalArgumentException("Could not decode $FILE_NAME_ARGS file with exception: ${e.localizedMessage}")
383+

kmp-composeuiviewcontroller-ksp/src/test/kotlin/composeuiviewcontroller/ProcessorTest.kt

Lines changed: 31 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ class ProcessorTest {
103103
assertTrue(generatedSwiftFiles.isNotEmpty())
104104

105105
val generatedSwiftFile = generatedSwiftFiles.first().readText()
106-
assertEquals(generatedSwiftFile, ExpectedOutputs.swiftRepresentableWithState())
106+
assertEquals(ExpectedOutputs.swiftRepresentableWithState(), generatedSwiftFile)
107107
}
108108

109109
@Test
@@ -129,26 +129,41 @@ class ProcessorTest {
129129

130130
val generatedKotlinFile = generatedKotlinFiles.first().readText()
131131
assertEquals(
132-
generatedKotlinFile, ExpectedOutputs.kotlinUIViewControllerWithoutState(
133-
params = "data: SomeClass, value: Int, callBack: () -> Unit"
134-
)
132+
ExpectedOutputs.kotlinUIViewControllerWithoutState(params = "data: SomeClass, value: Int, callBack: () -> Unit"),
133+
generatedKotlinFile
135134
)
136135

137136
val generatedSwiftFiles = findGeneratedSwiftFile(compilation, "ScreenUIViewControllerRepresentable.swift")
138137
assertTrue(generatedSwiftFiles.isNotEmpty())
139138

140139
val generatedSwiftFile = generatedSwiftFiles.first().readText()
141140
assertEquals(
142-
generatedSwiftFile, ExpectedOutputs.swiftRepresentableWithoutState(
141+
ExpectedOutputs.swiftRepresentableWithoutState(
143142
params = listOf(
144143
"data" to "SomeClass",
145144
"value" to "Int32", // Direct parameter uses native type
146145
"callBack" to "() -> Void"
147146
)
148-
)
147+
),
148+
generatedSwiftFile
149149
)
150150
}
151151

152+
@Test
153+
fun `Basic screen without params respects SwiftFormat Template`() {
154+
val code = CodeTemplates.basicScreenWithoutStateAndParams()
155+
val compilation = prepareCompilation(kotlin("Screen.kt", code), *klibSourceFiles().toTypedArray())
156+
157+
val result = compilation.compile()
158+
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
159+
160+
val generatedSwiftFiles = findGeneratedSwiftFile(compilation, "ScreenUIViewControllerRepresentable.swift")
161+
assertTrue(generatedSwiftFiles.isNotEmpty())
162+
163+
val generatedSwiftFile = generatedSwiftFiles.first().readText()
164+
assertEquals(ExpectedOutputs.swiftFormatTemplate1(), generatedSwiftFile)
165+
}
166+
152167
@Test
153168
fun `Only 1 @ComposeUIViewControllerState is allowed`() {
154169
val code = CodeTemplates.screenWithMultipleStateAnnotations()
@@ -176,21 +191,21 @@ class ProcessorTest {
176191
val result = compilation.compile()
177192
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
178193
assertEquals(
194+
4,
179195
TestFileUtils.countGeneratedFiles(
180196
compilation,
181197
"ScreenAUIViewController.kt", "ScreenAUIViewControllerRepresentable.swift",
182198
"ScreenBUIViewController.kt", "ScreenBUIViewControllerRepresentable.swift"
183199
),
184-
4
185200
)
186201

187202
val generatedKotlinFiles = TestFileUtils.findGeneratedKotlinFile(compilation, "ScreenAUIViewController.kt")
188203
val generatedKotlinFile = generatedKotlinFiles.first().readText()
189-
assertEquals(generatedKotlinFile, ExpectedOutputs.kotlinUIViewControllerWithState())
204+
assertEquals(ExpectedOutputs.kotlinUIViewControllerWithState(), generatedKotlinFile)
190205

191206
val generatedSwiftFiles = findGeneratedSwiftFile(compilation, "ScreenAUIViewControllerRepresentable.swift")
192207
val generatedSwiftFile = generatedSwiftFiles.first().readText()
193-
assertEquals(generatedSwiftFile, ExpectedOutputs.swiftRepresentableWithState(functionName = "ScreenA", stateType = "ViewAState"))
208+
assertEquals(ExpectedOutputs.swiftRepresentableWithState(functionName = "ScreenA", stateType = "ViewAState"), generatedSwiftFile)
194209
}
195210

196211
@Test
@@ -226,13 +241,13 @@ class ProcessorTest {
226241
val result = compilation.compile()
227242
assertEquals(KotlinCompilation.ExitCode.OK, result.exitCode)
228243
assertEquals(
244+
6,
229245
TestFileUtils.countGeneratedFiles(
230246
compilation,
231247
"ScreenAUIViewController.kt", "ScreenAUIViewControllerRepresentable.swift",
232248
"ScreenBUIViewController.kt", "ScreenBUIViewControllerRepresentable.swift",
233249
"ScreenCUIViewController.kt", "ScreenCUIViewControllerRepresentable.swift"
234-
),
235-
6
250+
)
236251
)
237252
}
238253

@@ -304,7 +319,7 @@ class ProcessorTest {
304319
assertTrue(generatedSwiftFiles.isNotEmpty())
305320

306321
val expectedSwiftOutput = ExpectedOutputs.swiftRepresentableWithExternalDependency()
307-
assertEquals(generatedSwiftFiles.first().readText(), expectedSwiftOutput)
322+
assertEquals(expectedSwiftOutput, generatedSwiftFiles.first().readText())
308323
}
309324

310325
@Test
@@ -332,7 +347,7 @@ class ProcessorTest {
332347
functionName = "Screen"
333348
)
334349

335-
assertEquals(generatedSwiftFiles.first().readText(), expectedSwiftOutput)
350+
assertEquals(expectedSwiftOutput, generatedSwiftFiles.first().readText())
336351
}
337352

338353
@Test
@@ -395,7 +410,7 @@ class ProcessorTest {
395410
//This typealias can be avoided if you use the `flattenPackage = "com.mycomposable.data"` in KMP swiftExport settings
396411
typealias Data = ExportedKotlinPackages.com.mycomposable.data.Data
397412
""".trimIndent()
398-
assertEquals(generatedSwiftFiles.first().readText(), expectedTypeAliasSwiftOutput)
413+
assertEquals(expectedTypeAliasSwiftOutput, generatedSwiftFiles.first().readText())
399414
}
400415

401416
@Test
@@ -411,7 +426,7 @@ class ProcessorTest {
411426
assertTrue(generatedSwiftFiles.isNotEmpty())
412427

413428
val expectedSwiftOutput = ExpectedOutputs.swiftRepresentableWithObjCTypes()
414-
assertEquals(generatedSwiftFiles.first().readText(), expectedSwiftOutput)
429+
assertEquals(expectedSwiftOutput, generatedSwiftFiles.first().readText())
415430
}
416431

417432
@Test
@@ -427,7 +442,7 @@ class ProcessorTest {
427442
assertTrue(generatedSwiftFiles.isNotEmpty())
428443

429444
val expectedSwiftOutput = ExpectedOutputs.swiftRepresentableWithSwiftExportTypes()
430-
assertEquals(generatedSwiftFiles.first().readText(), expectedSwiftOutput)
445+
assertEquals(expectedSwiftOutput, generatedSwiftFiles.first().readText())
431446
}
432447

433448
@Test

kmp-composeuiviewcontroller-ksp/src/test/kotlin/composeuiviewcontroller/Templates.kt

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,15 @@ object Templates {
8080
fun $functionName(@ComposeUIViewControllerState state: $stateType${if (additionalParams.isNotEmpty()) ", $additionalParams" else ""}) { }
8181
""".trimIndent()
8282

83+
fun simpleComposableWithoutParams(
84+
functionName: String = "Screen",
85+
framework: String = DEFAULT_FRAMEWORK,
86+
) = """
87+
@ComposeUIViewController("$framework")
88+
@Composable
89+
fun $functionName() { }
90+
""".trimIndent()
91+
8392
fun composableWithStateAnnotationOnly(
8493
functionName: String = "Screen",
8594
stateType: String = "ViewState"
@@ -112,6 +121,16 @@ object Templates {
112121
${Composables.simpleComposableWithState(framework = framework)}
113122
""".trimIndent()
114123

124+
fun basicScreenWithoutStateAndParams(
125+
framework: String = DEFAULT_FRAMEWORK,
126+
packageName: String = TEST_PACKAGE
127+
) = """
128+
package $packageName
129+
$COMMON_IMPORTS
130+
131+
${Composables.simpleComposableWithoutParams(framework = framework)}
132+
""".trimIndent()
133+
115134
fun screenWithoutState(
116135
framework: String = DEFAULT_FRAMEWORK,
117136
packageName: String = TEST_PACKAGE
@@ -724,6 +743,22 @@ public struct ${functionName}Representable: UIViewControllerRepresentable {
724743
//This typealias can be avoided if you use the `flattenPackage = "com.mycomposable.state"` in KMP swiftExport settings
725744
typealias ViewState = ExportedKotlinPackages.com.mycomposable.state.ViewState
726745
""".trimIndent()
746+
747+
fun swiftFormatTemplate1() = """
748+
// This file is auto-generated by KSP. Do not edit manually.
749+
import $DEFAULT_FRAMEWORK
750+
import SwiftUI
751+
752+
public struct ScreenRepresentable: UIViewControllerRepresentable {
753+
public func makeUIViewController(context _: Context) -> UIViewController {
754+
ScreenUIViewController().make()
755+
}
756+
757+
public func updateUIViewController(_ uiViewController: UIViewController, context _: Context) {
758+
// unused
759+
}
760+
}
761+
""".trimIndent()
727762
}
728763

729764
object TestFileUtils {

0 commit comments

Comments
 (0)