Testing & Validation

Your Swift validation logic is integrated with Kotlin - now it’s time to build the app and test it! You’ll verify that Swift correctly validates user input and see the complete system working together.

Building the Application

Build your app using Gradle. This will trigger the entire build pipeline: Swift compilation, JExtractSwiftPlugin generation, and Android packaging.

cd /path/to/Starter

# Build Swift code (triggers JExtractSwiftPlugin)
./gradlew buildSwiftAll

# Build the complete Android app
./gradlew assembleDebug

# Install on device/emulator
./gradlew installDebug

The build should complete successfully. You’ll see output showing:

  • Swift compilation for each ABI (ARM64, ARMv7, x86_64)
  • JExtractSwiftPlugin generating Java classes
  • Kotlin and Java compilation
  • APK creation

If you encounter errors, check:

  • JAVA_HOME is set to JDK 21
  • Package.swift has swift-java dependencies
  • swift-java.config exists with correct package name
  • settings.gradle.kts includes :taskmanager-lib
  • SwiftKitCore is published to local Maven

Running the App

Launch the app on your device or emulator:

adb shell am start -n com.kodeco.android.swiftsdkforandroid.taskmanager/.MainActivity

Or simply run the app from Android Studio. You’ll see the Task Manager home screen with an empty task list and a floating action button.

Test Case 1: Valid Task Creation

Let’s verify that valid tasks are accepted.

Tap the floating action button to open the create task dialog. Fill in:

  • Title: “Buy groceries” (13 characters)
  • Description: “Get milk, eggs, and bread from the store” (42 characters)
  • Priority: High

Tap Save.

Expected result: The task appears in the list with a high priority badge!

This task passes validation because:

  • Title length: 13 (between 3-50)
  • Description length: 42 (between 10-200)

The Swift validateTitle() and validateDescription() functions returned true, so Kotlin created the task.

Test Case 2: Title Too Short

Now test validation failure with an invalid title.

Tap the floating action button again. Fill in:

  • Title: “Hi” (2 characters)
  • Description: “This description is definitely long enough for validation” (57 characters)
  • Priority: Medium

Tap Save.

Expected result: An error message appears: “Title must be between 3 and 50 characters”

The task does NOT appear in the list.

What happened:

  1. Kotlin called TaskValidator.validateTitle("Hi")
  2. Generated Java called the native method
  3. JNI bridge passed “Hi” to Swift
  4. Swift trimmed and counted: "Hi".count = 2
  5. Swift returned false (less than minTitleLength of 3)
  6. Kotlin received false and returned Result.failure(...)
  7. UI displayed the error message

Your Swift validation is working!

Test Case 3: Title Too Long

Test the maximum title length constraint.

Create a task with:

  • Title: “This is an extremely long title that exceeds the maximum allowed length for task titles in the application” (108 characters)
  • Description: “Valid description with enough characters” (41 characters)
  • Priority: Low

Tap Save.

Expected result: Error: “Title must be between 3 and 50 characters”

Swift correctly rejected a title longer than 50 characters.

Test Case 4: Description Too Short

Test description validation.

Create a task with:

  • Title: “Valid title” (11 characters)
  • Description: “Too short” (9 characters)
  • Priority: High

Tap Save.

Expected result: Error: “Description must be between 10 and 200 characters”

The title passed validation, but the description failed because it’s shorter than 10 characters.

Test Case 5: Description Too Long

Create a task with:

  • Title: “Valid task”
  • Description: A 250-character string (exceeds 200)
  • Priority: Medium

Expected result: Error: “Description must be between 10 and 200 characters”

Swift rejected the description for being too long.

Test Case 6: Boundary Conditions

Test the exact boundaries:

Minimum Valid Title (3 characters):

  • Title: “Buy” - Accepted

Maximum Valid Title (50 characters):

  • Title: “Buy groceries and household items for the week” (50 characters) - Accepted

Minimum Valid Description (10 characters):

  • Description: “Buy things” (10 characters) - Accepted

Maximum Valid Description (200 characters):

  • Description: A string with exactly 200 characters - Accepted

All boundary conditions work correctly!

Understanding the Cross-Language Flow

When you tap Save with “Hi” as the title, here’s the complete data flow:

1. User Input (UI)
   CreateTaskDialog → onSave("Hi", "Valid description", Priority.HIGH)
   ↓
2. Kotlin (TaskRepository)
   addTask(title = "Hi", ...)
   if (!TaskValidator.validateTitle("Hi")) { ... }
   ↓
3. Generated Java (TaskValidator.java)
   public static boolean validateTitle(String title) {
       return TaskValidator.$validateTitle(title);  ← Calls native method
   }
   ↓
4. JNI Bridge (TaskValidator+SwiftJava.swift)
   @_cdecl("Java_com_kodeco_...")
   - Converts jstring to Swift String
   - Calls Swift function
   - Converts Swift Bool to jboolean
   ↓
5. Swift (TaskValidator.swift)
   public static func validateTitle(_ title: String) -> Bool {
       let trimmed = title.trimmingCharacters(in: .whitespacesAndNewlines)
       return trimmed.count >= 3 && trimmed.count <= 50  ← Returns false
   }
   ↓
6. Return Path
   Swift (false) → JNI (jboolean: 0) → Java (false) → Kotlin (false)
   ↓
7. Kotlin Handles Result
   if (!false) {  // true
       return Result.failure(Exception("Title must be between 3 and 50 characters"))
   }
   ↓
8. UI Displays Error
   CreateTaskDialog catches Result.failure and shows error message

All of this happens in milliseconds, and you wrote ZERO JNI code!

Viewing Generated Code

Want to see what JExtractSwiftPlugin created? Navigate to the generated files:

cd taskmanager-lib
find .build/plugins/outputs -name "*.java"

Open TaskValidator.java to see the clean Java wrapper:

package com.kodeco.android.taskmanagerkit;

public final class TaskValidator {
  static final String LIB_NAME = "TaskManagerKit";

  public static boolean validateTitle(String title) {
    return TaskValidator.$validateTitle(title);
  }
  private static native boolean $validateTitle(String title);

  public static boolean validateDescription(String description) {
    return TaskValidator.$validateDescription(description);
  }
  private static native boolean $validateDescription(String description);

  static {
    System.loadLibrary(LIB_NAME);
  }
}

This is idiomatic Java/Kotlin code that integrates seamlessly with your Android app!

Debugging Tips

If validation isn’t working:

  1. Check library loading: Look for logcat messages about loading libTaskManagerKit.so.
  2. Verify .so files exist: Check app/build/intermediates/jniLibs/ for your architecture.
  3. Rebuild Swift: Run ./gradlew clean buildSwiftAll to regenerate everything.
  4. Check JNI signatures: Generated +SwiftJava.swift should have matching function names.
  5. Verify Package.swift: Ensure swift-java dependencies are correct.

Performance Considerations

You might wonder about the performance of calling Swift from Kotlin. The JNI overhead is minimal:

  • Simple calls (like validation): < 1 microsecond overhead.
  • String marshaling: Efficient conversion between Java String and Swift String.
  • No copying: Strings are passed by reference when possible.

For validation logic, the overhead is negligible compared to UI rendering and user interaction time.

What You’ve Accomplished

In this lesson, you’ve:

  • Installed and configured Swift SDK for Android with swiftly.
  • Created a Swift package with Swift Package Manager.
  • Configured swift-java and JExtractSwiftPlugin for automatic Java bindings.
  • Written Swift validation logic in clean, idiomatic Swift.
  • Integrated Swift with Android through Gradle automation.
  • Used generated Java classes from Kotlin seamlessly.
  • Built and tested a hybrid Android app with Swift business logic.
  • Understood the complete cross-language data flow.

You now know how to leverage Swift’s type safety and expressiveness for Android business logic while keeping Kotlin for the UI!

Where to Go From Here?

You can access the completed project files from the Materials section in the right sidebar. Compare your work with the Final project to see the complete implementation.

What’s Next

In the next lesson, Platform Integration: Camera and Image Processing, you’ll expand your Task Manager with photo capture capabilities. You’ll learn:

  • How to integrate CameraX for photo capture in Kotlin.
  • How to pass image data from Kotlin to Swift through byte arrays.
  • How to implement image processing filters in Swift.
  • How to handle larger data marshaling with auto-generated bindings.
  • How to work with more complex types across the language boundary.

In Lesson 3, Data Persistence & Testing, you’ll add file-based persistence so tasks survive app restarts. You’ll also add full CRUD operations (Create, Read, Update, Delete) and learn cross-platform file handling considerations.

Additional Resources

Want to learn more about Swift for Android?

Alternative Approach: Manual JNI

For educational purposes, you might want to understand how JNI works under the hood. The manual JNI approach (what swift-java automates) involves:

  1. Writing @_cdecl exports with encoded function names like Java_com_package_Class_method.
  2. Manual string marshaling between Java and Swift using JNI functions.
  3. Creating JNI bridge classes in Kotlin that call native methods.
  4. Managing function table offsets and memory manually.

This is more complex and error-prone, which is why swift-java exists! The automatic approach is what production apps use.

Congratulations

You’ve successfully built a hybrid Android application that uses Swift for business logic and Kotlin for the UI. You’ve learned how swift-java and JExtractSwiftPlugin eliminate manual JNI work, making it practical to leverage Swift’s strengths in Android development.

This pattern opens up new possibilities: sharing business logic between iOS and Android, using Swift’s powerful type system for complex validation, and leveraging Swift’s safety features to prevent bugs.

We hope you enjoyed this lesson! If you have any questions or comments, please join the forum discussion below.

See forum comments
Download course materials from Github
Previous: Kotlin Integration & Gradle Build Automation Next: Conclusion