
AVIF encoding and decoding with native libavif, adaptive SMART/STRICT compression, automatic JPEG fallback, multi-threaded processing, priority presets, format detection, resizing and metadata preservation.
This is a Kotlin Multiplatform project targeting Android and iOS, built with Android Gradle Plugin 9.2 (requires Gradle 9.4.1+ and JDK 17+).
/shared — the AvifKit library itself. The cross-platform API lives in
commonMain; platform code is in androidMain (Kotlin JNI
bindings) and iosMain (Swift bridge). Published to Maven Central as
io.github.alfikri-rizky:avifkit. Uses the AGP 9 KMP library plugin
(com.android.kotlin.multiplatform.library).
/shared-native — the Android native build (libavif/AOM + JNI wrapper),
compiled with CMake/NDK. It's a plain com.android.library because the KMP library plugin
can't build native code. Published as the companion artifact avifkit-native, which :shared
pulls in transitively — so consumers only ever depend on avifkit.
/composeApp — the Android demo app, a plain com.android.application module
with Compose Multiplatform UI. Not published.
/iosApp — the iOS demo app (SwiftUI entry point). Not published.
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
./gradlew :composeApp:assembleDebug.\gradlew.bat :composeApp:assembleDebugTo build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
AvifKit is a production-ready Kotlin Multiplatform library for AVIF image encoding and decoding on Android and iOS.
__attribute__((constructor)) — no manual setup needed@Throws annotations propagate errors as NSError to Swift's do/catch
AvifError exceptions when dependencies are missing (no silent fallbacks)AvifKit uses native AVIF libraries on both platforms with explicit error reporting:
Shared XCFramework is self-contained (no avif.swift, no Swift bridge, no registration step). See docs/IOS_CINTEROP_SOLUTION.md.AvifError exceptions are thrown on failure (no silent fallbacks)val converter = AvifConverter()
// Convert to AVIF with priority preset
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED
)When you need to compress images to meet a specific file size limit, AvifKit offers two compression strategies:
Finds the highest quality image that still meets your target file size. This is the default and recommended strategy for most use cases.
val options = EncodingOptions(
maxSize = 200 * 1024, // 200KB target
compressionStrategy = CompressionStrategy.SMART // Default
)
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED,
options = options
)How it works:
Best for:
Finds the smallest possible image by continuing compression even after meeting the target size.
val options = EncodingOptions(
maxSize = 200 * 1024, // 200KB target
compressionStrategy = CompressionStrategy.STRICT
)
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED,
options = options
)How it works:
Best for:
| Aspect | SMART | STRICT |
|---|---|---|
| Goal | Best quality within limit | Smallest possible size |
| Speed | Faster (6-8 attempts) | Slower (up to 10 attempts) |
| Result Quality | Higher quality | Lower quality |
| Result Size | Near target size | Well below target |
| Use Case | General use | Storage-critical |
Example with 500KB target:
Priority.SPEED // Fast encoding, lower quality
Priority.QUALITY // Best quality, slower encoding
Priority.STORAGE // Minimum file size
Priority.BALANCED // Good balance (default)EncodingOptions(
quality = 75, // Base quality (0-100)
speed = 6, // Encoding speed (0-10)
subsample = ChromaSubsample.YUV420, // Chroma subsampling
alphaQuality = 90, // Alpha channel quality
maxDimension = 2048, // Auto-resize if larger
maxSize = 200 * 1024, // Target size in bytes
compressionStrategy = CompressionStrategy.SMART // SMART or STRICT
)AvifKit is published as a Kotlin Multiplatform library with seamless integration for both Android and iOS platforms.
Pick ONE iOS channel — do not mix.
- KMP / Compose Multiplatform apps → Gradle only. Add the
commonMainGradle dependency below. The iOS AVIF codec (libavif + AOM) is embedded in the Kotlin/Native artifact, so iOS links with no SPM package and no extra setup — exactly like Android. This is the recommended path for shared KMP code.- Pure SwiftUI / iOS-only apps → SPM (
import Shared; see iOS section).- Never add both Gradle and SPM in the same project. That links two separate copies of the
Sharedmodule into two different frameworks with disjoint symbol namespaces, which fails withUndefined symbol: _avif*at link time. If your app uses the shared KMP module via Gradle, remove the SPMAvifKitpackage reference from the iOS app target.
Add the dependency to your build.gradle.kts:
dependencies {
implementation("io.github.alfikri-rizky:avifkit:0.3.1")
}That's it! The library includes pre-built native binaries for all ABIs (arm64-v8a, armeabi-v7a, x86, x86_64) with full AVIF support via libavif.
In Xcode:
https://github.com/alfikri-rizky/AvifKit
0.3.1 or higherOr add to your Package.swift:
dependencies: [
.package(url: "https://github.com/alfikri-rizky/AvifKit", from: "0.3.1")
]Setup Notes:
import Shared and use AvifConverter() directlyUsage:
import Shared
let converter = AvifConverter()
print("AVIF available:", converter.isAvifSupported()) // trueTroubleshooting:
rm -rf ~/Library/Caches/org.swift.swiftpm ~/Library/org.swift.swiftpm
rm -rf ~/Library/Developer/Xcode/DerivedDataDownload from GitHub Releases: v0.3.1
CocoaPods support is technically available but not recommended due to validation issues:
pod 'AvifKit', '~> 0.3.1'Important Notes:
pod install since app deployment targets (iOS 15.0+) override pod settingsRecommended alternatives:
shared-native/src/main/cpp/, the :shared-native module)Technical Details:
-O3 compiler flagsAvifConverter.ios.kt → libavif) — the same C API as Android, no Swift bridgeShared.framework (self-contained XCFramework)isAvifSupported() returns true)@Throws annotations — errors propagate as NSError to Swift's do/catchTechnical Details:
scripts/build-ios-libavif.sh; linked via cinterop linkerOpts
| Component | Status | Location | Notes |
|---|---|---|---|
| Core Library | ✅ Complete | shared/src/commonMain/ |
Cross-platform API |
| Android Native | ✅ Complete | shared-native/src/main/cpp/ |
JNI + libavif (:shared-native module) |
| iOS Native | ✅ Complete | shared/src/iosMain/swift/ |
Swift + libavif |
| Adaptive Compression | ✅ Complete | Both platforms | SMART & STRICT strategies |
| Orientation Support | ✅ Complete | Both platforms | EXIF (Android), UIImage (iOS) |
| Fallback Mode | ❌ Removed | Both platforms | Replaced with explicit AvifError exceptions |
| Distribution | ✅ Complete | Package.swift |
SPM support (CocoaPods coming soon) |
| Build Configuration | ✅ Complete | shared/build.gradle.kts |
Ready for publishing |
Library Size:
No Fallback Mode (v0.2.3+):
AvifError exceptionsPlatform API Differences:
android.graphics.Bitmap
UIImage
PlatformBitmap expect/actual patternBuild Requirements (for library authors only):
Check if native AVIF is available:
val converter = AvifConverter()
val isSupported = converter.isAvifSupported() // true on both platforms (codec statically linked)The library automatically uses fallback when native library is unavailable:
AvifError.EncodingFailed or AvifError.DecodingFailed with actionable error messagesNSError via @Throws — caught by Swift's do/catch
If you want to build the library from source or contribute to development:
# 1. Clone the repository
git clone https://github.com/alfikri-rizky/AvifKit.git
cd AvifKit
# 2. Run the preparation script (downloads libavif, builds everything)
./scripts/prepare-for-publish.sh
# 3. Build the project
./gradlew :shared:buildThe library uses a comprehensive publishing setup:
To Maven Central (publishes the library and its native companion — both required):
./gradlew :shared-native:publishToMavenCentral :shared:publishToMavenCentralTo local Maven (for testing):
./gradlew :shared-native:publishToMavenLocal :shared:publishToMavenLocalTo CocoaPods:
pod trunk push AvifKit.podspecscripts/setup-android-libavif.sh - Downloads libavif for Android developmentscripts/setup-ios-avif.sh - Sets up iOS dependencies (CocoaPods/SPM)scripts/prepare-for-publish.sh - Prepares everything for release (runs both setup scripts + builds)scripts/verify-integration.sh - Verifies the integration is working correctlyNote: End users of your published library don't need these scripts - they're only for development and publishing.
Undefined symbol: _avifEncoderCreate (the codec flags lived in our
build, not in the published klib). The static codec libs (libavif.a + libaom.a)
are now embedded into the cinterop klib via -staticLibrary, so a Gradle/Maven
consumer links cleanly with only the commonMain dependency — no SPM, no manual
Xcode setup. See docs/AVIFKIT_FEEDBACK.md.Shared modules → undefined symbols).AvifKitIos singleton than the consumer read from (full analysis in docs/IOS_CINTEROP_SOLUTION.md). One framework, one Kotlin runtime, one code path.Shared.framework; no avif.swift dependency, no Swift bridge, no auto-registration.AvifKit product internals (AVIFNativeConverter, AvifKitSetup, AvifKitAutoRegister) and the Kotlin AvifKitIos / IosAvifNativeHandler registry.AvifKitSetup.registerNativeHandler() call from your app's init().import Shared and AvifConverter() directly. (The SPM AvifKit product now simply re-vends the self-contained Shared XCFramework.)Shared iOS framework from static (isStatic = true) to dynamic (isStatic = false). v0.2.9's Package.swift cleanup was the right direction but not sufficient — SPM still produced two link edges to the static Shared.xcframework (one through the AvifKit Swift target, one through the binary product itself), leaving two copies of the Kotlin runtime in the consumer binary and two AvifKitIos singletons. A dynamic framework is dyld-loaded once per process, so every link edge resolves to the same image and the same singleton.import AvifKit (added in v0.2.9). No code changes required.AvifError.EncodingFailed: Native AVIF handler not available, even after v0.2.7's claimed fix. Root cause was the static Shared.xcframework being linked into the consumer binary along two paths (once as a direct product target, once via the AvifKit Swift target's dependency), which produced two copies of the Kotlin runtime — and therefore two separate AvifKitIos singletons. The Swift bridge registered into one; consumer call sites read from the other; the read always returned null."Shared" from the AvifKit library product in Package.swift. Shared is still a .binaryTarget and is still reachable, but only through AvifKit's dependency closure — eliminating the duplicate link path.@_exported import Shared in a new AvifKitExports.swift so consumers see all Kotlin types (AvifConverter, EncodingOptions, ImageInput, AvifError, Priority, KotlinByteArray, …) by writing import AvifKit only..shared → .companion change in AVIFNativeConverter.swift. The original v0.2.7 diagnosis was wrong: top-level Kotlin objects are exposed to Swift as .shared (confirmed in the generated framework header). .companion is only valid for a companion object declared inside a class.import Shared with import AvifKit in your Swift sources. No other call-site changes are required — every Kotlin type previously available under Shared is now re-exported through AvifKit.Package.swift, AvifKit.podspec, and the XCFramework linker (build.gradle.kts).filekit-core 0.14.1 → 0.12.0 and kotlinx-io-core 0.9.0 → 0.8.2 for Kotlin 2.2.x KLIB ABI compatibility (rolled forward from v0.2.7).AvifKitIos.shared → AvifKitIos.companion in AVIFNativeConverter.swift. Kotlin object singletons are exposed to Swift/ObjC as .companion, not .shared. This bug caused 100% iOS AVIF encoding/decoding failure in v0.2.6.@OptIn annotations for experimental Kotlin/Native APIs__attribute__((constructor)) auto-registration)AvifKitObjC target to satisfy SPM mixed-language constraint (Swift + ObjC cannot be in the same target folder)__attribute__((constructor)) — consumers no longer need manual AvifKitSetup.registerNativeHandler() calls@Throws(Exception::class) to all public methods — errors now propagate as NSError to Swift's do/catch instead of crashingAvifError.EncodingFailed / AvifError.DecodingFailed with actionable messagesAVIFNativeConverter.swift — demo app uses symlinks to shared/src/iosMain/swift/
libavif-Xcode to avif.swift
#if canImport conditionals that caused silent JPEG fallback in productionlibavif-Xcode (raw C API) with avif.swift — high-level Swift AVIF encoder/decoderAVIFNativeConverter.swift — removed ~250 lines of manual pixel buffer managementnormalizeOrientation() before encodinglibavif ~> 0.11 to avif ~> 2.1
SDWebImage/libavif-Xcode to awxkee/avif.swift 2.1.x
.up orientationEncodingFailed crash when using SPEED preset (speed ≥ 7 triggered incompatible REALTIME mode in libaom)AvifKitNativeHandler (Swift method signature mismatch)avifResultToString for native encoding failuresLearn more about Kotlin Multiplatform…
This is a Kotlin Multiplatform project targeting Android and iOS, built with Android Gradle Plugin 9.2 (requires Gradle 9.4.1+ and JDK 17+).
/shared — the AvifKit library itself. The cross-platform API lives in
commonMain; platform code is in androidMain (Kotlin JNI
bindings) and iosMain (Swift bridge). Published to Maven Central as
io.github.alfikri-rizky:avifkit. Uses the AGP 9 KMP library plugin
(com.android.kotlin.multiplatform.library).
/shared-native — the Android native build (libavif/AOM + JNI wrapper),
compiled with CMake/NDK. It's a plain com.android.library because the KMP library plugin
can't build native code. Published as the companion artifact avifkit-native, which :shared
pulls in transitively — so consumers only ever depend on avifkit.
/composeApp — the Android demo app, a plain com.android.application module
with Compose Multiplatform UI. Not published.
/iosApp — the iOS demo app (SwiftUI entry point). Not published.
To build and run the development version of the Android app, use the run configuration from the run widget in your IDE’s toolbar or build it directly from the terminal:
./gradlew :composeApp:assembleDebug.\gradlew.bat :composeApp:assembleDebugTo build and run the development version of the iOS app, use the run configuration from the run widget in your IDE’s toolbar or open the /iosApp directory in Xcode and run it from there.
AvifKit is a production-ready Kotlin Multiplatform library for AVIF image encoding and decoding on Android and iOS.
__attribute__((constructor)) — no manual setup needed@Throws annotations propagate errors as NSError to Swift's do/catch
AvifError exceptions when dependencies are missing (no silent fallbacks)AvifKit uses native AVIF libraries on both platforms with explicit error reporting:
Shared XCFramework is self-contained (no avif.swift, no Swift bridge, no registration step). See docs/IOS_CINTEROP_SOLUTION.md.AvifError exceptions are thrown on failure (no silent fallbacks)val converter = AvifConverter()
// Convert to AVIF with priority preset
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED
)When you need to compress images to meet a specific file size limit, AvifKit offers two compression strategies:
Finds the highest quality image that still meets your target file size. This is the default and recommended strategy for most use cases.
val options = EncodingOptions(
maxSize = 200 * 1024, // 200KB target
compressionStrategy = CompressionStrategy.SMART // Default
)
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED,
options = options
)How it works:
Best for:
Finds the smallest possible image by continuing compression even after meeting the target size.
val options = EncodingOptions(
maxSize = 200 * 1024, // 200KB target
compressionStrategy = CompressionStrategy.STRICT
)
val result = converter.convertToFile(
input = ImageInput.from("/path/to/image.jpg"),
outputPath = "/path/to/output.avif",
priority = Priority.BALANCED,
options = options
)How it works:
Best for:
| Aspect | SMART | STRICT |
|---|---|---|
| Goal | Best quality within limit | Smallest possible size |
| Speed | Faster (6-8 attempts) | Slower (up to 10 attempts) |
| Result Quality | Higher quality | Lower quality |
| Result Size | Near target size | Well below target |
| Use Case | General use | Storage-critical |
Example with 500KB target:
Priority.SPEED // Fast encoding, lower quality
Priority.QUALITY // Best quality, slower encoding
Priority.STORAGE // Minimum file size
Priority.BALANCED // Good balance (default)EncodingOptions(
quality = 75, // Base quality (0-100)
speed = 6, // Encoding speed (0-10)
subsample = ChromaSubsample.YUV420, // Chroma subsampling
alphaQuality = 90, // Alpha channel quality
maxDimension = 2048, // Auto-resize if larger
maxSize = 200 * 1024, // Target size in bytes
compressionStrategy = CompressionStrategy.SMART // SMART or STRICT
)AvifKit is published as a Kotlin Multiplatform library with seamless integration for both Android and iOS platforms.
Pick ONE iOS channel — do not mix.
- KMP / Compose Multiplatform apps → Gradle only. Add the
commonMainGradle dependency below. The iOS AVIF codec (libavif + AOM) is embedded in the Kotlin/Native artifact, so iOS links with no SPM package and no extra setup — exactly like Android. This is the recommended path for shared KMP code.- Pure SwiftUI / iOS-only apps → SPM (
import Shared; see iOS section).- Never add both Gradle and SPM in the same project. That links two separate copies of the
Sharedmodule into two different frameworks with disjoint symbol namespaces, which fails withUndefined symbol: _avif*at link time. If your app uses the shared KMP module via Gradle, remove the SPMAvifKitpackage reference from the iOS app target.
Add the dependency to your build.gradle.kts:
dependencies {
implementation("io.github.alfikri-rizky:avifkit:0.3.1")
}That's it! The library includes pre-built native binaries for all ABIs (arm64-v8a, armeabi-v7a, x86, x86_64) with full AVIF support via libavif.
In Xcode:
https://github.com/alfikri-rizky/AvifKit
0.3.1 or higherOr add to your Package.swift:
dependencies: [
.package(url: "https://github.com/alfikri-rizky/AvifKit", from: "0.3.1")
]Setup Notes:
import Shared and use AvifConverter() directlyUsage:
import Shared
let converter = AvifConverter()
print("AVIF available:", converter.isAvifSupported()) // trueTroubleshooting:
rm -rf ~/Library/Caches/org.swift.swiftpm ~/Library/org.swift.swiftpm
rm -rf ~/Library/Developer/Xcode/DerivedDataDownload from GitHub Releases: v0.3.1
CocoaPods support is technically available but not recommended due to validation issues:
pod 'AvifKit', '~> 0.3.1'Important Notes:
pod install since app deployment targets (iOS 15.0+) override pod settingsRecommended alternatives:
shared-native/src/main/cpp/, the :shared-native module)Technical Details:
-O3 compiler flagsAvifConverter.ios.kt → libavif) — the same C API as Android, no Swift bridgeShared.framework (self-contained XCFramework)isAvifSupported() returns true)@Throws annotations — errors propagate as NSError to Swift's do/catchTechnical Details:
scripts/build-ios-libavif.sh; linked via cinterop linkerOpts
| Component | Status | Location | Notes |
|---|---|---|---|
| Core Library | ✅ Complete | shared/src/commonMain/ |
Cross-platform API |
| Android Native | ✅ Complete | shared-native/src/main/cpp/ |
JNI + libavif (:shared-native module) |
| iOS Native | ✅ Complete | shared/src/iosMain/swift/ |
Swift + libavif |
| Adaptive Compression | ✅ Complete | Both platforms | SMART & STRICT strategies |
| Orientation Support | ✅ Complete | Both platforms | EXIF (Android), UIImage (iOS) |
| Fallback Mode | ❌ Removed | Both platforms | Replaced with explicit AvifError exceptions |
| Distribution | ✅ Complete | Package.swift |
SPM support (CocoaPods coming soon) |
| Build Configuration | ✅ Complete | shared/build.gradle.kts |
Ready for publishing |
Library Size:
No Fallback Mode (v0.2.3+):
AvifError exceptionsPlatform API Differences:
android.graphics.Bitmap
UIImage
PlatformBitmap expect/actual patternBuild Requirements (for library authors only):
Check if native AVIF is available:
val converter = AvifConverter()
val isSupported = converter.isAvifSupported() // true on both platforms (codec statically linked)The library automatically uses fallback when native library is unavailable:
AvifError.EncodingFailed or AvifError.DecodingFailed with actionable error messagesNSError via @Throws — caught by Swift's do/catch
If you want to build the library from source or contribute to development:
# 1. Clone the repository
git clone https://github.com/alfikri-rizky/AvifKit.git
cd AvifKit
# 2. Run the preparation script (downloads libavif, builds everything)
./scripts/prepare-for-publish.sh
# 3. Build the project
./gradlew :shared:buildThe library uses a comprehensive publishing setup:
To Maven Central (publishes the library and its native companion — both required):
./gradlew :shared-native:publishToMavenCentral :shared:publishToMavenCentralTo local Maven (for testing):
./gradlew :shared-native:publishToMavenLocal :shared:publishToMavenLocalTo CocoaPods:
pod trunk push AvifKit.podspecscripts/setup-android-libavif.sh - Downloads libavif for Android developmentscripts/setup-ios-avif.sh - Sets up iOS dependencies (CocoaPods/SPM)scripts/prepare-for-publish.sh - Prepares everything for release (runs both setup scripts + builds)scripts/verify-integration.sh - Verifies the integration is working correctlyNote: End users of your published library don't need these scripts - they're only for development and publishing.
Undefined symbol: _avifEncoderCreate (the codec flags lived in our
build, not in the published klib). The static codec libs (libavif.a + libaom.a)
are now embedded into the cinterop klib via -staticLibrary, so a Gradle/Maven
consumer links cleanly with only the commonMain dependency — no SPM, no manual
Xcode setup. See docs/AVIFKIT_FEEDBACK.md.Shared modules → undefined symbols).AvifKitIos singleton than the consumer read from (full analysis in docs/IOS_CINTEROP_SOLUTION.md). One framework, one Kotlin runtime, one code path.Shared.framework; no avif.swift dependency, no Swift bridge, no auto-registration.AvifKit product internals (AVIFNativeConverter, AvifKitSetup, AvifKitAutoRegister) and the Kotlin AvifKitIos / IosAvifNativeHandler registry.AvifKitSetup.registerNativeHandler() call from your app's init().import Shared and AvifConverter() directly. (The SPM AvifKit product now simply re-vends the self-contained Shared XCFramework.)Shared iOS framework from static (isStatic = true) to dynamic (isStatic = false). v0.2.9's Package.swift cleanup was the right direction but not sufficient — SPM still produced two link edges to the static Shared.xcframework (one through the AvifKit Swift target, one through the binary product itself), leaving two copies of the Kotlin runtime in the consumer binary and two AvifKitIos singletons. A dynamic framework is dyld-loaded once per process, so every link edge resolves to the same image and the same singleton.import AvifKit (added in v0.2.9). No code changes required.AvifError.EncodingFailed: Native AVIF handler not available, even after v0.2.7's claimed fix. Root cause was the static Shared.xcframework being linked into the consumer binary along two paths (once as a direct product target, once via the AvifKit Swift target's dependency), which produced two copies of the Kotlin runtime — and therefore two separate AvifKitIos singletons. The Swift bridge registered into one; consumer call sites read from the other; the read always returned null."Shared" from the AvifKit library product in Package.swift. Shared is still a .binaryTarget and is still reachable, but only through AvifKit's dependency closure — eliminating the duplicate link path.@_exported import Shared in a new AvifKitExports.swift so consumers see all Kotlin types (AvifConverter, EncodingOptions, ImageInput, AvifError, Priority, KotlinByteArray, …) by writing import AvifKit only..shared → .companion change in AVIFNativeConverter.swift. The original v0.2.7 diagnosis was wrong: top-level Kotlin objects are exposed to Swift as .shared (confirmed in the generated framework header). .companion is only valid for a companion object declared inside a class.import Shared with import AvifKit in your Swift sources. No other call-site changes are required — every Kotlin type previously available under Shared is now re-exported through AvifKit.Package.swift, AvifKit.podspec, and the XCFramework linker (build.gradle.kts).filekit-core 0.14.1 → 0.12.0 and kotlinx-io-core 0.9.0 → 0.8.2 for Kotlin 2.2.x KLIB ABI compatibility (rolled forward from v0.2.7).AvifKitIos.shared → AvifKitIos.companion in AVIFNativeConverter.swift. Kotlin object singletons are exposed to Swift/ObjC as .companion, not .shared. This bug caused 100% iOS AVIF encoding/decoding failure in v0.2.6.@OptIn annotations for experimental Kotlin/Native APIs__attribute__((constructor)) auto-registration)AvifKitObjC target to satisfy SPM mixed-language constraint (Swift + ObjC cannot be in the same target folder)__attribute__((constructor)) — consumers no longer need manual AvifKitSetup.registerNativeHandler() calls@Throws(Exception::class) to all public methods — errors now propagate as NSError to Swift's do/catch instead of crashingAvifError.EncodingFailed / AvifError.DecodingFailed with actionable messagesAVIFNativeConverter.swift — demo app uses symlinks to shared/src/iosMain/swift/
libavif-Xcode to avif.swift
#if canImport conditionals that caused silent JPEG fallback in productionlibavif-Xcode (raw C API) with avif.swift — high-level Swift AVIF encoder/decoderAVIFNativeConverter.swift — removed ~250 lines of manual pixel buffer managementnormalizeOrientation() before encodinglibavif ~> 0.11 to avif ~> 2.1
SDWebImage/libavif-Xcode to awxkee/avif.swift 2.1.x
.up orientationEncodingFailed crash when using SPEED preset (speed ≥ 7 triggered incompatible REALTIME mode in libaom)AvifKitNativeHandler (Swift method signature mismatch)avifResultToString for native encoding failuresLearn more about Kotlin Multiplatform…