Migration guide

August 20, 2026 · View on GitHub

Kiban is a port of java-iban. This document maps the old API onto the current one, for people arriving from java-iban, from kiban 0.3.0 or earlier, or from the Result-returning kiban 0.4.0 API.

Every break mapped below happened before 1.0, while the API was still being settled: there was no deprecation cycle and no compat layer, so the left-hand column is gone rather than deprecated, and each break is a compile error rather than a runtime surprise. That is not how removals work from 1.0 onwards — see VERSIONING.md for the semver contract, the binary compatibility guarantee and the deprecation cycle a post-1.0 removal has to run first. Kiban has not reached 1.0 yet, so the remaining 0.x releases can still add to this guide.

Upgrading from kiban 0.4.0 to 0.5.0

0.4.0 made Iban.parse, Iban(...), String.toIban() and Iban.compose return Result<Iban> instead of throwing. That shape didn't survive the trip to Swift (see docs/9-swift-interop-review.md), so 0.5.0 reverts it: parsing is strict again and throws a typed IbanParseException. 0.5.0 also removes every member that was @Deprecated in 0.4.0 and earlier — there are no replacements to reach for beyond what's listed here.

FromTo
Iban.parse(s)Iban(s) — 0.6.0 brings the name back, but it returns Iban and throws, it does not return Result<Iban>
Iban.parse(s).getOrThrow()Iban(s)
Iban.parse(s).getOrNull()s.toIbanOrNull()
Iban.parse(s).exceptionOrNull()try { Iban(s) } catch (e: IbanParseException) { … }
Iban.parse(s).fold(...)try/catch, or runCatching { Iban(s) }.fold(...)
Iban(s) (returned Result)Iban(s) — now returns Iban directly, throws
"...".toIban() (returned Result)"...".toIban() — now returns Iban directly, throws
Iban.valueOf(s)Iban(s)
Iban.compose(cc, bban).getOrThrow()Iban.compose(cc, bban)
Iban.format(s)no replacement — parse, then use iban.pretty
Iban.toPretty(s) / Iban.toPlain(s)no replacement — parse, then iban.pretty / iban.plain
iban.toPlainString()iban.plain
CountryCodes.getBankIdentifier(iban)iban.bankIdentifier
CountryCodes.getBranchIdentifier(iban)iban.branchIdentifier
CountryCodes.getLengthForCountryCode(cc)CountryCodes.ibanLength(cc) ?: -1
CountryCodes.lastUpdateDateStringCountryCodes.lastUpdateDate

runCatching { Iban(s) } reproduces the old Result-returning behaviour exactly, for anyone who wants it back locally without touching the public API.

Package and type names

java-ibankiban
nl.garvelink.ibannl.bijdorpstudio.kiban
IBANIban (a typealias IBAN = Iban exists on JVM)
Modulo97Modulo97
CountryCodesCountryCodes
IBANFieldsremoved — use Iban.bankIdentifier / Iban.branchIdentifier
IBANFieldsCompatnever ported

The IBAN alias is JVM-only: it is declared in the library's jvmMain source set, so Kotlin code compiled for the JVM (or for Android) sees it, while commonMain and the non-JVM targets see only Iban. It stays in 1.0 — a typealias is erased at compile time, so it costs nothing to carry — but new code should be written against Iban.

Parsing

Iban(input) — or the invoke operator's spelling, Iban.invoke(input) — is the primary entry point. It validates the input and confirms the check digits, throwing IbanParseException on any failure.

BeforeNow
IBAN.valueOf(input)Iban(input)
IBAN.parse(input)Iban(input)
try { IBAN.valueOf(s) } catch (e: IllegalArgumentException) { null }s.toIbanOrNull()
try { IBAN.valueOf(s); true } catch (e: IllegalArgumentException) { false }s.isValidIban()

IbanParseException extends IllegalArgumentException, so the narrowest possible migration from java-iban is to change nothing but the call itself:

// java-iban
val iban = IBAN.valueOf(input)

// kiban
val iban = Iban(input)

Typed failures

Catching IllegalArgumentException and reading message is not necessary. Failures carry a sealed type:

try {
    Iban(input)
} catch (failure: IbanParseException) {
    when (failure) {
        is IbanParseException.UnknownCountryCode -> failure.countryCode
        is IbanParseException.WrongLength -> "${failure.actualLength} != ${failure.expectedLength}"
        is IbanParseException.WrongChecksum -> "check digits do not match"
        is IbanParseException.Malformed -> failure.kind.name
    }
}

Instance API

BeforeNow
iban.toPlainString()iban.plain
iban.toString()iban.toString() or iban.pretty (unchanged behaviour: spaced formatting)
IBAN.toPretty(input)no replacement — parse with Iban(input), then use iban.pretty
IBAN.toPlain(input)no replacement — parse with Iban(input), then use iban.plain
iban.countryCodeiban.countryCode
iban.checkDigitsiban.checkDigits
iban.isSEPA / iban.isInSwiftRegistryunchanged

Bank and branch identifiers

BeforeNow
IBANFields.getBankIdentifier(iban)Optional<String>iban.bankIdentifierString?
IBANFields.getBranchIdentifier(iban)Optional<String>iban.branchIdentifierString?
CountryCodes.getBankIdentifier(iban)iban.bankIdentifier
CountryCodes.getBranchIdentifier(iban)iban.branchIdentifier

IBANFields was JVM-only and returned java.util.Optional; it is gone. Kotlin's nullable types cover the same ground, and ?: replaces orElse.

CountryCodes

BeforeNow
CountryCodes.getLengthForCountryCode(cc)-1 when unknownCountryCodes.ibanLength(cc)Int?, null when unknown
CountryCodes.LAST_UPDATE_DATE / lastUpdateDateStringCountryCodes.lastUpdateDatekotlin.time.Instant
CountryCodes.LAST_UPDATE_REVCountryCodes.lastUpdateRevision
CountryCodes.SHORTEST_IBAN_LENGTH (kiban 0.5.0)CountryCodes.shortestIbanLength
CountryCodes.LONGEST_IBAN_LENGTH (kiban 0.5.0)CountryCodes.longestIbanLength
CountryCodes.getLength(cc) (kiban 0.5.0)CountryCodes.ibanLength(cc)

The three names marked (kiban 0.5.0) were renamed in 0.6.0, together with Iban.SHORTEST_POSSIBLE_IBAN, which is now Iban.SHORTEST_POSSIBLE_IBAN_LENGTH. All four named a length without saying so, and the two CountryCodes lengths additionally surfaced on the JVM as getSHORTEST_IBAN_LENGTH() / getLONGEST_IBAN_LENGTH(). | CountryCodes.isKnownCountryCode(cc) | unchanged | | CountryCodes.isSEPACountry(cc) | unchanged |

Composition

Iban.compose throws on invalid input, same as Iban(input):

// before
val iban = IBAN.compose("BI", "10000100010000332045181")

// now
val iban = Iban.compose("BI", "10000100010000332045181")

If you used compose on kiban 0.3.0, note that it was broken for any country whose check digits are 10 or higher — it produced an IBAN with 00 check digits and then rejected it. Upgrading fixes that; there is no workaround to remove from your code, since the call simply failed before.

Modulo97

Unchanged, and still throwing. Modulo97.checksum, calculateCheckDigits, and verifyCheckDigits all take CharSequence and raise IllegalArgumentException on malformed input, because their inputs come from the programmer rather than from an end user.

Things that were never ported

  • IBANFieldsCompat — the Java 6/7 compatibility shim for IBANFields.
  • Any API that took a java.util.Locale or returned a java.util.Optional.