Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ swiftklib {

### Examples

More samples can be found in the [examples/](https://github.com/ttypic/swift-klib-plugin/tree/main/examples) folder.
- More samples can be found in the [examples/](https://github.com/ttypic/swift-klib-plugin/tree/main/examples) folder.
- Component demonstrating a multipurpose Kotlin Multiplatform and Swift Package audio player: [radioplayer-kt](https://github.com/markst/radioplayer-kt)

## License

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,188 @@ class SwiftPackageModulesTest {
assertThat(result).output().contains("Package path must exist")
}

@Test
fun `build with remote SPM dependency using Firebase is successful`() {
// Given
val fixture = SwiftKlibTestFixture.builder()
.withSwiftSources(
SwiftSource.of(content = """
import FirebaseAuth
import Firebase

@objc public class FirebaseData: NSObject {
@objc public func printVersion() {
print(FirebaseVersion())
print(ActionCodeOperation.emailLink)
}
}
""".trimIndent())
)
.withConfiguration {
minIos = "14.0"
minMacos = "10.15"
dependencies {
remote("FirebaseAuth") {
url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk")
exactVersion("11.0.0")
}
}
}
.build()

// When
val result = build(fixture.gradleProject.rootDir, "build")

// Then
assertThat(result).task(":library:build").succeeded()
assertPackageResolved(fixture, "firebase-ios-sdk")
}

@Test
fun `build with remote SPM dependency using multi product Firebase is successful`() {
// Given
val fixture = SwiftKlibTestFixture.builder()
.withSwiftSources(
SwiftSource.of(content = """
import FirebaseAuth
import Firebase
import FirebaseRemoteConfig

@objc public class FirebaseData: NSObject {
@objc public func testLinking() {
print(FirebaseVersion())
print(ActionCodeOperation.emailLink)
print(RemoteConfigSettings())
}
}
""".trimIndent())
)
.withConfiguration {
minIos = "14.0"
minMacos = "10.15"
dependencies {
remote(listOf("FirebaseAuth", "FirebaseRemoteConfig")) {
url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk")
exactVersion("11.0.0")
}
}
}
.build()

// When
val result = build(fixture.gradleProject.rootDir, "build")

// Then
assertThat(result).task(":library:build").succeeded()
assertPackageResolved(fixture, "firebase-ios-sdk")
}

@Test
fun `build with complex and mix spm repo`() {
// Given
val fixture = SwiftKlibTestFixture.builder()
.withSwiftSources(
SwiftSource.of(content = """
import FirebaseAuth
import Firebase
import FirebaseRemoteConfig
import KeychainAccess
import SwiftyJSON

@objc public class FirebaseData: NSObject {
@objc public func testLinking() {
print(FirebaseVersion())
print(ActionCodeOperation.emailLink)
print(RemoteConfigSettings())
}
}
@objc public class DataManager: NSObject {
private let keychain = Keychain(service: "test-service")

@objc public func processJson(jsonString: String) throws -> String {
let json = try JSON(parseJSON: jsonString)
return json.description
}
}
""".trimIndent())
)
.withConfiguration {
minIos = "14.0"
minMacos = "10.15"
dependencies {
remote(listOf("FirebaseAuth", "FirebaseRemoteConfig")) {
url("https://github.com/firebase/firebase-ios-sdk.git", "firebase-ios-sdk")
exactVersion("11.0.0")
}
remote("KeychainAccess") {
github("kishikawakatsumi", "KeychainAccess")
exactVersion("4.2.2")
}
remote("SwiftyJSON") {
github("SwiftyJSON", "SwiftyJSON")
versionRange("5.0.0", "6.0.0", true)
}
}
}
.build()

// When
val result = build(fixture.gradleProject.rootDir, "build")

// Then
assertThat(result).task(":library:build").succeeded()
assertPackageResolved(fixture, "firebase-ios-sdk")
}

@Test
fun `build with valid toolsVersion`() {
val fixture = SwiftKlibTestFixture.builder()
.withSwiftSources(
SwiftSource.of(content = """
import Foundation
""".trimIndent())
)
.withConfiguration {
toolsVersion = "5.5"
dependencies {
}
}
.build()

// When
val result = build(fixture.gradleProject.rootDir, "build")

// Then
assertThat(result).task(":library:build").succeeded()
getManifestContent(fixture) { manifest ->
assertTrue(manifest.contains("swift-tools-version: 5.5"))
}
}

@Test
fun `build with invalid toolsVersion`() {
val fixture = SwiftKlibTestFixture.builder()
.withSwiftSources(
SwiftSource.of(content = """
import Foundation
""".trimIndent())
)
.withConfiguration {
toolsVersion = "100.0"
dependencies {
}
}
.build()

// When
val result = buildAndFail(fixture.gradleProject.rootDir, "build")

// Then
assertThat(result).output().contains("is using Swift tools version 100.0.0")
getManifestContent(fixture) { manifest ->
assertTrue(manifest.contains("swift-tools-version: 100.0"), "must contains version 100.0")
}
}

private fun assertPackageResolved(fixture: SwiftKlibTestFixture, vararg packageNames: String) {
val resolvedFile = File(
Expand All @@ -305,12 +487,31 @@ class SwiftPackageModulesTest {
)
assertTrue(resolvedFile.exists(), "Package.resolved file not found")

val content = resolvedFile.readText()
packageNames.forEach { packageName ->
assertTrue(
content.contains("\"identity\" : \"$packageName\"", ignoreCase = true),
"$packageName dependency not found"
)
getPackageResolvedContent(fixture) { content ->
packageNames.forEach { packageName ->
assertTrue(
content.contains("\"identity\" : \"$packageName\"", ignoreCase = true),
"$packageName dependency not found"
)
}
}
}

private fun getManifestContent(fixture: SwiftKlibTestFixture, content: (String) -> Unit) {
val resolvedFile = File(
fixture.gradleProject.rootDir,
"library/build/swiftklib/test/iosArm64/swiftBuild/Package.swift"
)
assertTrue(resolvedFile.exists(), "Package.swift file not found")
content(resolvedFile.readText())
}

private fun getPackageResolvedContent(fixture: SwiftKlibTestFixture, content: (String) -> Unit) {
val resolvedFile = File(
fixture.gradleProject.rootDir,
"library/build/swiftklib/test/iosArm64/swiftBuild/Package.resolved"
)
assertTrue(resolvedFile.exists(), "Package.resolved file not found")
content(resolvedFile.readText())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,19 @@ abstract class SwiftKlibTestFixture private constructor(

// Only add minimum version configurations if they differ from defaults
if (entry._minIos.hasValue()) {
appendLine(" minIos.set(${entry.minIos})")
appendLine(" minIos = \"${entry.minIos}\"")
}
if (entry._minMacos.hasValue()) {
appendLine(" minMacos.set(${entry.minMacos})")
appendLine(" minMacos = \"${entry.minMacos}\"")
}
if (entry._minTvos.hasValue()) {
appendLine(" minTvos.set(${entry.minTvos})")
appendLine(" minTvos = \"${entry.minTvos}\"")
}
if (entry._minWatchos.hasValue()) {
appendLine(" minWatchos.set(${entry.minWatchos})")
appendLine(" minWatchos = \"${entry.minWatchos}\"")
}
if (entry._toolsVersions.hasValue()) {
appendLine(" toolsVersion = \"${entry.toolsVersion}\"")
}

if (entry.dependencies.isNotEmpty()) {
Expand Down Expand Up @@ -190,16 +193,18 @@ val Plugin.Companion.kotlinMultiplatform

private class TestSwiftKlibEntryImpl : SwiftKlibEntry {
val _path = notNull<File>()
val _minIos = notNull<Int>()
val _minMacos = notNull<Int>()
val _minTvos = notNull<Int>()
val _minWatchos = notNull<Int>()
val _minIos = notNull<String>()
val _minMacos = notNull<String>()
val _minTvos = notNull<String>()
val _minWatchos = notNull<String>()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a breaking change in plugin API.
Is it really necessary to store versions as strings?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I change from a Int to a String; it has too much limitation.
For example, we can't use this kind of version macos = "10.15.1", this is possible to set in the Package manifest.

I don't think it's a big deal, as it's just a type of the input to change.

val _toolsVersions = notNull<String>()

override var path: File by _path
override var minIos: Int by _minIos
override var minMacos: Int by _minMacos
override var minTvos: Int by _minTvos
override var minWatchos: Int by _minWatchos
override var minIos: String by _minIos
override var minMacos: String by _minMacos
override var minTvos: String by _minTvos
override var minWatchos: String by _minWatchos
override var toolsVersion: String by _toolsVersions

val dependencies = mutableListOf<TestDependencyConfig>()

Expand All @@ -223,23 +228,31 @@ private class TestSwiftPackageConfigurationImpl : SwiftPackageConfiguration {
}

override fun remote(name: String, configuration: RemotePackageConfiguration.() -> Unit) {
val config = TestRemotePackageConfigurationImpl(name)
remote(listOf(name), configuration)
}

override fun remote(names: List<String>, configuration: RemotePackageConfiguration.() -> Unit) {
val config = TestRemotePackageConfigurationImpl(names)
config.configuration()
dependencies.add(config.build())
}
}

private class TestRemotePackageConfigurationImpl(private val name: String) : RemotePackageConfiguration {
private class TestRemotePackageConfigurationImpl(private val name: List<String>) :
RemotePackageConfiguration {

private var url: String? = null
private var packageName: String? = null
private var versionConfig: TestVersionConfig? = null

override fun github(owner: String, repo: String) {
override fun github(owner: String, repo: String, packageName: String?) {
url = "https://github.com/$owner/$repo.git"
this.packageName = packageName
}

override fun url(url: String) {
override fun url(url: String, packageName: String?) {
this.url = url
this.packageName = packageName
}

override fun exactVersion(version: String) {
Expand All @@ -262,7 +275,8 @@ private class TestRemotePackageConfigurationImpl(private val name: String) : Rem
return TestDependencyConfig.Remote(
name = name,
url = url,
version = versionConfig
version = versionConfig,
packageName = packageName
)
}
}
Expand All @@ -275,14 +289,23 @@ private sealed interface TestDependencyConfig {
}

data class Remote(
val name: String,
val name: List<String>,
val url: String?,
val version: TestVersionConfig?
val version: TestVersionConfig?,
val packageName: String?
) : TestDependencyConfig {
override fun toConfigString() = buildString {
append("remote(\"$name\") {\n")
if (name.size == 1) {
append("remote(\"${name.first()}\") {\n")
} else {
append("remote(listOf(\"${name.joinToString("\",\"")}\")) {\n")
}
if (url != null) {
append(" url(\"$url\")\n")
if (packageName != null) {
append(" url(\"$url\", \"$packageName\")\n")
} else {
append(" url(\"$url\")\n")
}
}
if (version != null) {
append(" ${version.toConfigString()}\n")
Expand Down Expand Up @@ -318,7 +341,8 @@ private class NotNullVar<T : Any>() : ReadWriteProperty<Any?, T> {
private var value: T? = null

public override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
return value
?: throw IllegalStateException("Property ${property.name} should be initialized before get.")
}

public override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import javax.inject.Inject
@ExperimentalSwiftklibApi
class RemotePackageBuilder @Inject constructor(
private val objects: ObjectFactory,
private val name: String
private val name: List<String>
) {
private val urlProperty: Property<String> = objects.property(String::class.java)
private var dependency: SwiftPackageDependency.Remote? = null
Expand Down
Loading