swiftlang/swift-java

[JavaKit] `@JavaImplementation` method returning a null instance from Swift to Java

Opened this issue · 1 comments

I'm trying to wrap RecyclerView.Adapter in Android and create the row view from Swift and pass it to Java via JNI. Unfortunately on the Java / Kotlin side the returned value is always null.

RecyclerViewAdapter.kt

// Create new views (invoked by the layout manager)
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerViewAdapter.ViewHolder {
        val viewHolder = onCreateViewHolderSwift(parent, viewType)
        checkNotNull(viewHolder)
        checkNotNull(viewHolder.itemView)
        return viewHolder
    }

RecyclerViewAdapter.swift

@JavaImplementation("com.pureswift.swiftandroid.RecyclerViewAdapter")
extension RecyclerViewAdapter {
    
    @JavaMethod
    public func onCreateViewHolderSwift(_ viewGroup: ViewGroup?, _ viewType: Int32) -> RecyclerViewAdapter.ViewHolder? {
        log("\(self).\(#function) \(viewType)")
        let viewHolder = callback.onCreateViewHolder(viewGroup!, viewType)
        log("\(self).\(#function) Created \(viewHolder.getClass().getName()) \(viewHolder.itemView.getClass().getName())")
        return viewHolder
    }

I've verified from the Swift side that the Java Object I'm returning is a valid instance, and print their Java class name. But the checkNotNull() still fails on the JVM side.

16:57:54.394  D  SwiftAndroidApp.RecyclerViewAdapter.onCreateViewHolderSwift(_:_:) Created com.pureswift.swiftandroid.RecyclerViewAdapter$ViewHolder android.widget.TextView
16:57:54.394  D  Shutting down VM
16:57:54.395  E  FATAL EXCEPTION: main
Process: com.pureswift.swiftandroid, PID: 7551
java.lang.IllegalStateException: Required value was null.

Android.zip

android-crash.log

Same Issue with Fragment:

class Fragment(val swiftObject: SwiftObject): android.app.Fragment() {

    @Deprecated("Deprecated in Java")
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val context = this.context
        checkNotNull(context)
        val view = this.onCreateViewSwift(inflater, container, savedInstanceState)
        checkNotNull(view)
        val linearLayout = LinearLayout(context)
        linearLayout.addView(view)
        return view
    }

    external fun onCreateViewSwift(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View
}
Caused by: java.lang.IllegalStateException: Required value was null.
	at com.pureswift.swiftandroid.Fragment.onCreateView(Fragment.kt:20)
	at android.app.Fragment.performCreateView(Fragment.java:2510)
	at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:1303)
@JavaClass("com.pureswift.swiftandroid.Fragment")
open class Fragment: AndroidApp.Fragment {
    
    @JavaMethod
    @_nonoverride public convenience init(swiftObject: SwiftObject?, environment: JNIEnvironment? = nil)
    
    @JavaMethod
    public func getSwiftObject() -> SwiftObject?
}

public extension Fragment {
    
    struct Callback {
        
        var onCreateView: ((AndroidView.LayoutInflater?, AndroidView.ViewGroup?, AndroidOS.Bundle?) -> (AndroidView.View))
    }
}

@JavaImplementation("com.pureswift.swiftandroid.Fragment")
extension Fragment {
    
    @JavaMethod
    func onCreateViewSwift(
        inflater: AndroidView.LayoutInflater?,
        container: AndroidView.ViewGroup?,
        savedInstanceState: AndroidOS.Bundle?
    ) -> AndroidView.View? {
        log("\(self).\(#function)")
        return callback.onCreateView(inflater, container, savedInstanceState)
    }
}