问题
Based on this issue about using NSString
formatting I try to implement multiplatform implementation for formatting when using vararg
, with no luck so far.
What I did
- added FoundationInterop.def
language = Objective-C
---
#import <Foundation/NSString.h>
NSString* format(NSString* format, ...) {
va_list args;
va_start(args, format);
NSString* result = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
return result;
}
- compiled it in gradle
targets {
final def iOSTarget = System.getenv('SDK_NAME')?.startsWith("iphoneos") \
? presets.iosArm64 : presets.iosX64
// https://kotlinlang.org/docs/reference/mpp-dsl-reference.html#native-targets
fromPreset(iOSTarget, 'ios') {
binaries {
}
compilations.main.cinterops {
FoundationInterop {
}
}
}
}
- Created
StringExtensions.kt
incommonMain
expect class StringType
expect fun String.format(format: String, vararg args: Any?): StringType?
- in
iosMain
actual typealias StringType = String
/**
* https://github.com/JetBrains/kotlin-native/issues/1834
*/
actual fun String.format(format: String, vararg args: Any?): StringType? {
return FoundationInterop.format(format, args as Any)
}
- example
val fmt = "http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=%f&longitude=%f&altitude=0&date=%d-%02d-%02d&format=json"
val url = fmt.format(urlFmt, 59.127934932762166, 38.00503518930868, 2020, 1, 3)
- output - as you see no values substitutions occured for some reason
http://geomag.bgs.ac.uk/web_service/GMModels/igrf/13/?latitude=0.000000&longitude=0.000000&altitude=0&date=43344272-198763328-00&format=json
Edit
stringWithFormat
gives the same result
actual fun String.format(format: String, vararg args: Any?): StringType? {
return NSString.stringWithFormat(format, args as Any)
}
Edit 2
Created issue https://youtrack.jetbrains.com/issue/KT-42925
回答1:
I confirm what you say about NSString.stringWithFormat
. The feature is missing as we read in the JB offical answer from
Svyatoslav Scherbina and we could follow the issue from you here: KT-42925
As an awful workaround, I was suggesting something like that (WARNING: not exhaustive, without many index count checks...)
import platform.Foundation.NSString
import platform.Foundation.stringWithFormat
actual typealias StringType = String
actual fun String.format(format: String, vararg args: Any?): StringType? {
var returnString = ""
val regEx = "%[\\d|.]*[sdf]|[%]".toRegex()
val singleFormats = regEx.findAll(format).map {
it.groupValues.first()
}.asSequence().toList()
val newStrings = format.split(regEx)
for (i in 0 until args.count()) {
val arg = args[i]
returnString += when (arg) {
is Double -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Double)
}
is Int -> {
NSString.stringWithFormat(newStrings[i] + singleFormats[i], args[i] as Int)
}
else -> {
NSString.stringWithFormat(newStrings[i] + "%@", args[i])
}
}
}
return returnString
}
But look if it could be a valid workaround for you.
回答2:
You can't forward Kotlin variadic arguments to C or Objective-C. C variadics are compile time. It is not a Kotlin-specific limitation. You can't call a variadic C function from another variadic C function with forwarding all the arguments.
NSString.stringWithFormat(format, args as Any)
This is not correct.
This line takes args
(which is an Array
), casts it to Any
and passes as a single argument.
So this is mostly equivalent to something like
[NSString stringWithFormat:format, createKotlinArray(/* all arguments here */)]
which doesn't do what you expect.
So KT-42925 is not valid. Your problem could possibly be solved by one of the missing features:
- Common
String.format
in stdlib (KT-25506). But this is not easy. - Support for building
va_list
dynamically, e.g. from KotlinArray
(KT-42973). In this case it would be easy to use this variant: https://developer.apple.com/documentation/foundation/nsstring/1407827-initwithformat
来源:https://stackoverflow.com/questions/64495182/kotlin-native-ios-string-formatting-with-vararg