diff --git a/.gitignore b/.gitignore
index 89d806e..821a0c9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -33,6 +33,9 @@ xcuserdata
 # Packages/
 .build/
 
+# temporary workaround for swift pm bug
+Package.resolved
+
 # CocoaPods
 #
 # We recommend against adding the Pods directory to your .gitignore. However
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0edff8e..5f1caa5 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,45 +1,4 @@
-set(sources
-    JNI.swift
-    JNIArrays.swift.gyb
-    JNIClassManipulation.swift
-    JNIExceptions.swift
-    JNIFields.swift.gyb
-    JNIMethods.swift.gyb
-    JNIObjects.swift
-    JNIRefs.swift
-    JNIStrings.swift
-    SwiftJNI.swift
-)
+cmake_minimum_required(VERSION 3.16)
+project(SwiftJNI LANGUAGES C Swift)
 
-set(output_dir "${SWIFTLIB_DIR}/jni")
-
-# Set correct paths to glibc headers
-set(JNI_INCLUDE_PATH "${SWIFT_SDK_ANDROID_PATH}/usr/include")
-if (NOT EXISTS "${JNI_INCLUDE_PATH}/jni.h")
-  message(FATAL_ERROR "JNI header was not found at ${JNI_INCLUDE_PATH}.")
-endif()
-
-# Generate module.map
-configure_file(module.map.in "${CMAKE_CURRENT_BINARY_DIR}/module.map" @ONLY)
-
-add_custom_command_target(unused_var
-    COMMAND
-        "${CMAKE_COMMAND}" "-E" "make_directory" "${output_dir}"
-    COMMAND
-        "${CMAKE_COMMAND}" "-E" "copy_if_different"
-        "${CMAKE_CURRENT_BINARY_DIR}/module.map"
-        "${output_dir}/module.map"
-    CUSTOM_TARGET_NAME "copy_jni_module"
-    OUTPUT "${output_dir}/module.map" "${output_dir}"
-    DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/module.map"
-    COMMENT "Copying JNI module to ${output_dir}")
-
-swift_install_in_component(stdlib
-    FILES "${output_dir}/module.map"
-    DESTINATION "lib/swift/jni")
-
-add_swift_library(swiftJNI IS_SDK_OVERLAY
-    ${sources}
-    FILE_DEPENDS copy_jni_module "${output_dir}"
-    TARGET_SDKS ANDROID
-    INSTALL_IN_COMPONENT stdlib-experimental)
+add_subdirectory(Sources/JNI)
diff --git a/JNI.swift b/JNI.swift
deleted file mode 100644
index 6f0a045..0000000
--- a/JNI.swift
+++ /dev/null
@@ -1,67 +0,0 @@
-@_exported import CJNI // Clang module
-
-public class JNI {
-    /// Our reference to the Java Virtual Machine, to be set on init
-    let _jvm: UnsafeMutablePointer<JavaVM>
-
-    /// Ensure the _env pointer we have is always attached to the JVM
-    var _env: UnsafeMutablePointer<JNIEnv> {
-        let jvm = _jvm.memory.memory
-
-        // The type `JNIEnv` is defined as a non-mutable pointer,
-        // so use this mutable _tmpPointer as an intermediate:
-        var _tmpPointer = UnsafeMutablePointer<Void>()
-        let threadStatus = jvm.GetEnv(_jvm, &_tmpPointer, jint(JNI_VERSION_1_6))
-        var _env = UnsafeMutablePointer<JNIEnv>(_tmpPointer)
-
-        switch threadStatus {
-        case JNI_OK: break // if we're already attached, do nothing
-        case JNI_EDETACHED:
-            // We weren't attached to the Java UI thread
-            jvm.AttachCurrentThread(_jvm, &_env, nil)
-        case JNI_EVERSION:
-            fatalError("This version of JNI is not supported")
-        default: break
-        }
-
-        return _env
-    }
-
-    // Normally we init the jni global ourselves in JNI_OnLoad
-    public init?(jvm: UnsafeMutablePointer<JavaVM>) {
-        if jvm == nil { return nil }
-        self._jvm = jvm
-    }
-}
-
-public extension JNI {
-    public func GetVersion() -> jint {
-        let env = self._env
-        return env.memory.memory.GetVersion(env)
-    }
-
-    public func GetJavaVM(vm: UnsafeMutablePointer<UnsafeMutablePointer<JavaVM>>) -> jint {
-        let env = self._env
-        return env.memory.memory.GetJavaVM(env, vm)
-    }
-
-    public func RegisterNatives(targetClass: jclass, _ methods: UnsafePointer<JNINativeMethod>, _ nMethods: jint) -> jint {
-        let env = self._env
-        return env.memory.memory.RegisterNatives(env, targetClass, methods, nMethods)
-    }
-
-    public func UnregisterNatives(targetClass: jclass) -> jint {
-        let env = self._env
-        return env.memory.memory.UnregisterNatives(env, targetClass)
-    }
-
-    public func MonitorEnter(obj: jobject) -> jint {
-        let env = self._env
-        return env.memory.memory.MonitorEnter(env, obj)
-    }
-
-    public func MonitorExit(obj: jobject) -> jint {
-        let env = self._env
-        return env.memory.memory.MonitorExit(env, obj)
-    }
-}
diff --git a/JNIArrays.swift.gyb b/JNIArrays.swift.gyb
deleted file mode 100644
index 2f114d7..0000000
--- a/JNIArrays.swift.gyb
+++ /dev/null
@@ -1,106 +0,0 @@
-import CJNI
-
-%{
-    jTypes = [
-        
-        # You can only set / get one jobject array element at a time
-        # So we handle that case manually, and automate the rest...
-        # ('Object', 'jobject'), 
-
-        ('Boolean', 'jboolean'),
-        ('Byte', 'jbyte'),
-        ('Char', 'jchar'),
-        ('Short', 'jshort'),
-        ('Int', 'jint'),
-        ('Long', 'jlong'),
-        ('Float', 'jfloat'),
-        ('Double', 'jdouble')
-    ]
-}%
-
-public extension JNI {
-
-    public func GetArrayLength(array: jarray) -> jsize {
-        let env = self._env
-        return env.memory.memory.GetArrayLength(env, array)
-    }
-
-% # You can only set / get one object element at a time
-    public func NewObjectArray(length: jsize, _ elementClass: jclass, _ initialElement: jobject) -> jobjectArray {
-        let env = self._env
-        return env.memory.memory.NewObjectArray(env, length, elementClass, initialElement)
-    }
-
-    public func GetObjectArrayElement(array: jobjectArray, _ index: jsize) -> jobject {
-        let env = self._env
-        return env.memory.memory.GetObjectArrayElement(env, array, index)
-    }
-
-    public func SetObjectArrayElement(array: jobjectArray, _ index: jsize, _ value: jobject) {
-        let env = self._env
-        env.memory.memory.SetObjectArrayElement(env, array, index, value)
-    }
-
-
-% # But we can automate the rest
-% for (TypeName, type) in jTypes:
-
-    public func New${TypeName}Array(length: jsize) -> ${type}Array {
-        let env = self._env
-        return env.memory.memory.New${TypeName}Array(env, length)
-    }
-
-    public func Get${TypeName}ArrayElements(array: ${type}Array, _ isCopy: UnsafeMutablePointer<jboolean>) -> UnsafeMutablePointer<${type}> {
-       let env = self._env
-       return env.memory.memory.Get${TypeName}ArrayElements(env, array, isCopy)
-    }
-
-    public func Release${TypeName}ArrayElements(array: ${type}Array, _ elems: UnsafeMutablePointer<${type}>, _ mode: jint) {
-       let env = self._env
-       env.memory.memory.Release${TypeName}ArrayElements(env, array, elems, mode)
-    }
-
-    public func Get${TypeName}ArrayRegion(array: ${type}Array, _ start: jsize, _ len: jsize, _ buf: UnsafeMutablePointer<${type}>) {
-        let env = self._env
-        env.memory.memory.Get${TypeName}ArrayRegion(env, array, start, len, buf)
-    }
-
-    public func Set${TypeName}ArrayRegion(array: ${type}Array, _ start: jsize, _ len: jsize, _ buf: UnsafePointer<${type}>) {
-        let env = self._env
-        env.memory.memory.Set${TypeName}ArrayRegion(env, array, start, len, buf)
-    }
-
-% end
-}
-
-/// High performance JNI extensions
-public extension JNI {
-    public func GetPrimitiveArrayCritical(array: jarray, _ isCopy: UnsafeMutablePointer<jboolean>) -> UnsafeMutablePointer<Void> {
-        let env = self._env
-        return env.memory.memory.GetPrimitiveArrayCritical(env, array, isCopy)
-    }
-
-    public func ReleasePrimitiveArrayCritical(array: jarray, _ carray: UnsafeMutablePointer<Void>, _ mode: jint) {
-        let env = self._env
-        env.memory.memory.ReleasePrimitiveArrayCritical(env, array, carray, mode)
-    }
-
-    public func NewDirectByteBuffer(address: UnsafeMutablePointer<Void>, _ capacity: jlong) -> jobject {
-        let env = self._env
-        return env.memory.memory.NewDirectByteBuffer(env, address, capacity)
-    }
-
-    public func GetDirectBufferAddress(buf: jobject) -> UnsafeMutablePointer<Void> {
-        let env = self._env
-        return env.memory.memory.GetDirectBufferAddress(env, buf)
-    }
-
-    public func GetDirectBufferCapacity(buf: jobject) -> jlong {
-        let env = self._env
-        return env.memory.memory.GetDirectBufferCapacity(env, buf)
-    }
-}
-
-// ${'Local Variables'}:
-// eval: (read-only-mode 1)
-// End:
diff --git a/JNIClassManipulation.swift b/JNIClassManipulation.swift
deleted file mode 100644
index fee6c5a..0000000
--- a/JNIClassManipulation.swift
+++ /dev/null
@@ -1,43 +0,0 @@
-import CJNI
-
-public extension JNI {
-	public func DefineClass(name: String, _ loader: jobject, _ buffer: UnsafePointer<jbyte>, _ bufferLength: jsize) -> jclass {
-	    let env = self._env
-	    return env.memory.memory.DefineClass(env, name, loader, buffer, bufferLength)
-	}
-
-	public func FindClass(name: String) -> jclass {
-	    let env = self._env
-	    return env.memory.memory.FindClass(env, name)
-	}
-
-	public func FromReflectedMethod(method: jobject) -> jmethodID {
-	    let env = self._env
-	    return env.memory.memory.FromReflectedMethod(env, method)
-	}
-
-	public func FromReflectedField(field: jobject) -> jfieldID {
-	    let env = self._env
-	    return env.memory.memory.FromReflectedField(env, field)
-	}
-
-	public func ToReflectedMethod(targetClass: jclass, _ methodID: jmethodID, _ isStatic: jboolean) -> jobject {
-	    let env = self._env
-	    return env.memory.memory.ToReflectedMethod(env, cls, methodID, isStatic)
-	}
-
-	public func GetSuperclass(targetClass: jclass) -> jclass {
-	    let env = self._env
-	    return env.memory.memory.GetSuperclass(env, targetClass)
-	}
-
-	public func IsAssignableFrom(classA: jclass, _ classB: jclass) -> jboolean {
-	    let env = self._env
-	    return env.memory.memory.IsAssignableFrom(env, classA, classB)
-	}
-
-	public func ToReflectedField(targetClass: jclass, _ fieldID: jfieldID, _ isStatic: jboolean) -> jobject {
-	    let env = self._env
-	    return env.memory.memory.ToReflectedField(env, cls, fieldID, isStatic)
-	}
-}
\ No newline at end of file
diff --git a/JNIExceptions.swift b/JNIExceptions.swift
deleted file mode 100644
index 6d3884b..0000000
--- a/JNIExceptions.swift
+++ /dev/null
@@ -1,38 +0,0 @@
-import CJNI
-
-public extension JNI {
-    public func ExceptionCheck() -> jboolean {
-        let env = self._env
-        return env.memory.memory.ExceptionCheck(env)
-    }
-
-    public func ExceptionDescribe() {
-        let env = self._env
-        env.memory.memory.ExceptionDescribe(env)
-    }
-
-    public func ExceptionClear() {
-        let env = self._env
-        env.memory.memory.ExceptionClear(env)
-    }
-
-    public func ExceptionOccurred() -> jthrowable {
-        let env = self._env
-        return env.memory.memory.ExceptionOccurred(env)
-    }
-
-    public func Throw(obj: jthrowable) -> jint {
-        let env = self._env
-        return env.memory.memory.Throw(env, obj)
-    }
-
-    public func ThrowNew(targetClass: jclass, _ message: String) -> jint {
-        let env = self._env
-        return env.memory.memory.ThrowNew(env, targetClass, message)
-    }
-
-    public func FatalError(msg: String) {
-        let env = self._env
-        env.memory.memory.FatalError(env, msg)
-    }
-}
diff --git a/JNIFields.swift.gyb b/JNIFields.swift.gyb
deleted file mode 100644
index 1c8ea4a..0000000
--- a/JNIFields.swift.gyb
+++ /dev/null
@@ -1,56 +0,0 @@
-// Hash maps and fields
-
-import CJNI
-
-%{
-  jTypes = [
-    ('Object', 'jobject'),
-    ('Boolean', 'jboolean'),
-    ('Byte', 'jbyte'),
-    ('Char', 'jchar'),
-    ('Short', 'jshort'),
-    ('Int', 'jint'),
-    ('Long', 'jlong'),
-    ('Float', 'jfloat'),
-    ('Double', 'jdouble')
-  ]
-}%
-
-public extension JNI {
-
-    public func GetFieldID(targetClass: jclass, _ name: String, _ sig: String) -> jfieldID {
-        let env = self._env
-        return env.memory.memory.GetFieldID(env, targetClass, name, sig)
-    }
-
-    public func GetStaticFieldID(targetClass: jclass, _ name: String, _ sig: String) -> jfieldID {
-        let env = self._env
-        return env.memory.memory.GetStaticFieldID(env, targetClass, name, sig)
-    }
-
-% for (TypeName, Type) in jTypes:
-    public func Get${TypeName}Field(obj: jobject, _ fieldID: jfieldID) -> ${Type} {
-        let env = self._env
-        return env.memory.memory.Get${TypeName}Field(env, obj, fieldID)
-    }
-
-    public func Set${TypeName}Field(obj: jobject, _ fieldID: jfieldID, _ value: ${Type}) {
-        let env = self._env
-        env.memory.memory.Set${TypeName}Field(env, obj, fieldID, value)
-    }
-
-    public func GetStatic${TypeName}Field(targetClass: jclass, _ fieldID: jfieldID) -> ${Type} {
-        let env = self._env
-        return env.memory.memory.GetStatic${TypeName}Field(env, targetClass, fieldID)
-    }
-
-    public func SetStatic${TypeName}Field(targetClass: jclass, _ fieldID: jfieldID, _ value: ${Type}) {
-        let env = self._env
-        env.memory.memory.SetStatic${TypeName}Field(env, targetClass, fieldID, value)
-    }
-% end
-}
-
-// ${'Local Variables'}:
-// eval: (read-only-mode 1)
-// End:
diff --git a/JNIMethods.swift.gyb b/JNIMethods.swift.gyb
deleted file mode 100644
index 31a4b3f..0000000
--- a/JNIMethods.swift.gyb
+++ /dev/null
@@ -1,120 +0,0 @@
-//===--- JNIMethods.swift.gyb ---------------------------------*- swift -*-===//
-//
-// This source file is part of the Swift.org open source project
-//
-// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See http://swift.org/LICENSE.txt for license information
-// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-//
-//===----------------------------------------------------------------------===//
-
-import CJNI
-
-%{
-  jTypes = [
-    ('Void', 'Void'), # returning Void is also valid in swift
-    ('Object', 'jobject'),
-    ('Boolean', 'jboolean'),
-    ('Byte', 'jbyte'),
-    ('Char', 'jchar'),
-    ('Short', 'jshort'),
-    ('Int', 'jint'),
-    ('Long', 'jlong'),
-    ('Float', 'jfloat'),
-    ('Double', 'jdouble')
-  ]
-}%
-
-
-public extension JNI {
-
-% # Standard Java method calls
-
-  public func GetMethodID(targetClass: jclass, methodName name: String, methodSignature sig: String) -> jmethodID? {
-    let env = self._env
-    let result = env.memory.memory.GetMethodID(env, targetClass, name, sig)
-    return (result == nil) ? .None : result
-  }
-
-%for (TypeName, type) in jTypes:
-
-  public func Call${TypeName}Method(obj: jobject, _ methodID: jmethodID, _ args: jvalue...) -> ${type} {
-    return Call${TypeName}MethodA(obj, _ methodID: methodID, _ args: args)
-  }
-
-  @available(*, unavailable, message="CVaListPointers are not supported, use `Call${TypeName}Method` or `Call${TypeName}MethodA` instead")
-  public func Call${TypeName}MethodV(obj: jobject, _ methodID: jmethodID, _ args: CVaListPointer) -> ${type} {
-    // let env = self._env
-    // return env.memory.memory.Call${TypeName}MethodV(env, obj, methodID, args)
-    return ${type}()
-  }
-
-  public func Call${TypeName}MethodA(obj: jobject, _ methodID: jmethodID, _ args: [jvalue]) -> ${type} {
-    let env = self._env
-    var mutableArgs = args
-    return env.memory.memory.Call${TypeName}MethodA(env, obj, methodID, &mutableArgs)
-  }
-
-%end #standard methods
-
-
-
-% # Nonvirtual Java method calls
-%for (TypeName, type) in jTypes:
-
-  public func CallNonvirtual${TypeName}Method(obj: jobject, _ targetClass: jclass, _ methodID: jmethodID, _ args: jvalue...) -> ${type} {
-    return self.CallNonvirtual${TypeName}MethodA(obj, _ targetClass: targetClass, _ methodID: methodID, _ args: args)
-  }
-
-  @available(*, unavailable, message="CVaListPointers are not supported, use `CallNonvirtual${TypeName}Method` or `CallNonvirtual${TypeName}MethodA` instead")
-  public func CallNonvirtual${TypeName}MethodV(obj: jobject, _ targetClass: jclass, _ methodID: jmethodID, _ args: CVaListPointer) -> ${type} {
-    // let env = self._env
-    // return env.memory.memory.CallNonvirtual${TypeName}MethodV(env, obj, methodID, args)
-    return ${type}()
-  }
-
-  public func CallNonvirtual${TypeName}MethodA(obj: jobject, _ targetClass: jclass, _ methodID: jmethodID, _ args: [jvalue]) -> ${type} {
-    let env = self._env
-    var mutableArgs = args
-    return env.memory.memory.CallNonvirtual${TypeName}MethodA(env, obj, targetClass, methodID, &mutableArgs)
-  }
-
-%end #nonvirtual methods
-
-
-
-%# Static Java method calls
-
-  public func GetStaticMethodID(targetClass: jclass, _ name: String, _ sig: String) -> jmethodID {
-    let env = self._env
-    return env.memory.memory.GetStaticMethodID(env, targetClass, name, sig)
-  }
-
-%for (TypeName, type) in jTypes:
-
-  public func CallStatic${TypeName}Method(targetClass: jclass, _ methodID: jmethodID, _ args: jvalue...) -> ${type} {
-    return CallStatic${TypeName}MethodA(targetClass, _ methodID: methodID, _ args: args)
-  }
-
-  @available(*, unavailable, message="CVaListPointers are not supported, use `CallStatic${TypeName}Method` or `CallStatic${TypeName}MethodA` instead")
-  public func CallStatic${TypeName}MethodV(targetClass: jclass, _ methodID: jmethodID, _ args: CVaListPointer) -> ${type} {
-    // let env = self._env
-    // return env.memory.memory.CallStatic${TypeName}MethodV(env, class, methodID, args)
-    return ${type}()
-  }
-
-  public func CallStatic${TypeName}MethodA(targetClass: jclass, _ methodID: jmethodID, _ args: [jvalue]) -> ${type} {
-    let env = self._env
-    var mutableArgs = args
-    return env.memory.memory.CallStatic${TypeName}MethodA(env, targetClass, methodID, &mutableArgs)
-  }
-
-%end #static methods
-}
-
-// ${'Local Variables'}:
-// eval: (read-only-mode 1)
-// End:
-
diff --git a/JNIObjects.swift b/JNIObjects.swift
deleted file mode 100644
index 7cc8979..0000000
--- a/JNIObjects.swift
+++ /dev/null
@@ -1,33 +0,0 @@
-import CJNI
-
-public extension JNI {
-
-	public func AllocObject(targetClass: jclass) -> jobject {
-	    let env = self._env
-	    return env.memory.memory.AllocObject(env, targetClass)
-	}
-
-	public func NewObject(targetClass: jclass, _ methodID: jmethodID, _ args: jvalue...) -> jobject {
-	    return self.NewObjectA(targetClass, _ methodID: methodID, _ args: args)
-	}
-
-    @available(*, unavailable, message="CVaListPointer unavailable, use NewObject or NewObjectA")
-	public func NewObjectV(targetClass: jclass, _ methodID: jmethodID, _ args: CVaListPointer) -> jobject {
-	    // let env = self._env
-	    // return env.memory.memory.NewObjectV(env, targetClass, methodID, args)
-        return jobject()
-	}
-
-	public func NewObjectA(targetClass: jclass, _ methodID: jmethodID, _ args: [jvalue]) -> jobject {
-	    let env = self._env
-        var mutableArgs = args
-	    return env.memory.memory.NewObjectA(env, targetClass, methodID, &mutableArgs)
-	}
-
-	public func GetObjectClass(obj: jobject) -> jclass? {
-	    let env = self._env
-        let result = env.memory.memory.GetObjectClass(env, obj)
-        return (result != nil) ? result : .None
-	}
-	
-}
diff --git a/JNIRefs.swift b/JNIRefs.swift
deleted file mode 100644
index 581920c..0000000
--- a/JNIRefs.swift
+++ /dev/null
@@ -1,64 +0,0 @@
-import CJNI
-
-public extension JNI {
-    public func NewGlobalRef(obj: jobject) -> jobject {
-        let env = self._env
-        return env.memory.memory.NewGlobalRef(env, obj)
-    }
-
-    public func DeleteGlobalRef(globalRef: jobject) {
-        let env = self._env
-        env.memory.memory.DeleteGlobalRef(env, globalRef)
-    }
-
-    public func NewLocalRef(ref: jobject) -> jobject {
-        let env = self._env
-        return env.memory.memory.NewLocalRef(env, ref)
-    }
-    
-    public func DeleteLocalRef(localRef: jobject) {
-        let env = self._env
-        env.memory.memory.DeleteLocalRef(env, localRef)
-    }
-
-    public func PushLocalFrame(capacity: jint) -> jint {
-        let env = self._env
-        return env.memory.memory.PushLocalFrame(env, capacity)
-    }
-
-    public func PopLocalFrame(result: jobject) -> jobject {
-        let env = self._env
-        return env.memory.memory.PopLocalFrame(env, result)
-    }
-
-    public func EnsureLocalCapacity(capacity: jint) -> jint {
-        let env = self._env
-        return env.memory.memory.EnsureLocalCapacity(env, capacity)
-    }
-
-    public func IsSameObject(ref1: jobject, _ ref2: jobject) -> jboolean {
-        let env = self._env
-        return env.memory.memory.IsSameObject(env, ref1, ref2)
-    }
-
-    public func IsInstanceOf(obj: jobject, _ targetClass: jclass) -> jboolean {
-        let env = self._env
-        return env.memory.memory.IsInstanceOf(env, obj, targetClass)
-    }
-
-    public func NewWeakGlobalRef(obj: jobject) -> jweak {
-        let env = self._env
-        return env.memory.memory.NewWeakGlobalRef(env, obj)
-    }
-
-    public func DeleteWeakGlobalRef(obj: jweak) {
-        let env = self._env
-        env.memory.memory.DeleteWeakGlobalRef(env, obj)
-    }
-
-    /* added in 1: JNI.6 */
-    public func GetObjectRefType(obj: jobject) -> jobjectRefType {
-        let env = self._env
-        return env.memory.memory.GetObjectRefType(env, obj)
-    }
-}
\ No newline at end of file
diff --git a/JNIStrings.swift b/JNIStrings.swift
deleted file mode 100644
index d85abf7..0000000
--- a/JNIStrings.swift
+++ /dev/null
@@ -1,63 +0,0 @@
-import CJNI
-
-public extension JNI {
-	public func NewString(unicodeChars: UnsafePointer<jchar>, _ len: jsize) -> jstring {
-	    let env = self._env
-	    return env.memory.memory.NewString(env, unicodeChars, len)
-	}
-
-	public func GetStringLength(string: jstring) -> jsize {
-	    let env = self._env
-	    return env.memory.memory.GetStringLength(env, string)
-	}
-
-	public func GetStringChars(string: jstring, _ isCopy: UnsafeMutablePointer<jboolean>) -> UnsafePointer<jchar> {
-	    let env = self._env
-	    return env.memory.memory.GetStringChars(env, string, isCopy)
-	}
-
-	public func ReleaseStringChars(string: jstring, _ chars: UnsafePointer<jchar>) {
-	    let env = self._env
-	    env.memory.memory.ReleaseStringChars(env, string, chars)
-	}
-
-	public func NewStringUTF(bytes: String) -> jstring {
-	    let env = self._env
-	    return env.memory.memory.NewStringUTF(env, bytes)
-	}
-
-	public func GetStringUTFLength(string: jstring) -> jsize {
-	    let env = self._env
-	    return env.memory.memory.GetStringUTFLength(env, string)
-	}
-
-	public func GetStringUTFChars(string: jstring, _ isCopy: UnsafeMutablePointer<jboolean>) -> String {
-	    let env = self._env
-	    return String(env.memory.memory.GetStringUTFChars(env, string, isCopy))
-	}
-
-	public func ReleaseStringUTFChars(string: jstring, _ utf: String) {
-	    let env = self._env
-	    env.memory.memory.ReleaseStringUTFChars(env, string, utf)
-	}
-
-	public func GetStringRegion(str: jstring, _ start: jsize, _ len: jsize, _ buf: UnsafeMutablePointer<jchar>) {
-	    let env = self._env
-	    env.memory.memory.GetStringRegion(env, str, start, len, buf)
-	}
-
-	public func GetStringUTFRegion(str: jstring, _ start: jsize, _ len: jsize, _ buf: UnsafeMutablePointer<CChar>) {
-	    let env = self._env
-	    env.memory.memory.GetStringUTFRegion(env, str, start, len, buf)
-	}
-
-	public func GetStringCritical(string: jstring, _ isCopy: UnsafeMutablePointer<jboolean>) -> UnsafePointer<jchar> {
-	    let env = self._env
-	    return env.memory.memory.GetStringCritical(env, string, isCopy)
-	}
-
-	public func ReleaseStringCritical(string: jstring, _ carray: UnsafePointer<jchar>) {
-	    let env = self._env
-	    env.memory.memory.ReleaseStringCritical(env, string, carray)
-	}
-}
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index 8dada3e..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,201 +0,0 @@
-                                 Apache License
-                           Version 2.0, January 2004
-                        http://www.apache.org/licenses/
-
-   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
-   1. Definitions.
-
-      "License" shall mean the terms and conditions for use, reproduction,
-      and distribution as defined by Sections 1 through 9 of this document.
-
-      "Licensor" shall mean the copyright owner or entity authorized by
-      the copyright owner that is granting the License.
-
-      "Legal Entity" shall mean the union of the acting entity and all
-      other entities that control, are controlled by, or are under common
-      control with that entity. For the purposes of this definition,
-      "control" means (i) the power, direct or indirect, to cause the
-      direction or management of such entity, whether by contract or
-      otherwise, or (ii) ownership of fifty percent (50%) or more of the
-      outstanding shares, or (iii) beneficial ownership of such entity.
-
-      "You" (or "Your") shall mean an individual or Legal Entity
-      exercising permissions granted by this License.
-
-      "Source" form shall mean the preferred form for making modifications,
-      including but not limited to software source code, documentation
-      source, and configuration files.
-
-      "Object" form shall mean any form resulting from mechanical
-      transformation or translation of a Source form, including but
-      not limited to compiled object code, generated documentation,
-      and conversions to other media types.
-
-      "Work" shall mean the work of authorship, whether in Source or
-      Object form, made available under the License, as indicated by a
-      copyright notice that is included in or attached to the work
-      (an example is provided in the Appendix below).
-
-      "Derivative Works" shall mean any work, whether in Source or Object
-      form, that is based on (or derived from) the Work and for which the
-      editorial revisions, annotations, elaborations, or other modifications
-      represent, as a whole, an original work of authorship. For the purposes
-      of this License, Derivative Works shall not include works that remain
-      separable from, or merely link (or bind by name) to the interfaces of,
-      the Work and Derivative Works thereof.
-
-      "Contribution" shall mean any work of authorship, including
-      the original version of the Work and any modifications or additions
-      to that Work or Derivative Works thereof, that is intentionally
-      submitted to Licensor for inclusion in the Work by the copyright owner
-      or by an individual or Legal Entity authorized to submit on behalf of
-      the copyright owner. For the purposes of this definition, "submitted"
-      means any form of electronic, verbal, or written communication sent
-      to the Licensor or its representatives, including but not limited to
-      communication on electronic mailing lists, source code control systems,
-      and issue tracking systems that are managed by, or on behalf of, the
-      Licensor for the purpose of discussing and improving the Work, but
-      excluding communication that is conspicuously marked or otherwise
-      designated in writing by the copyright owner as "Not a Contribution."
-
-      "Contributor" shall mean Licensor and any individual or Legal Entity
-      on behalf of whom a Contribution has been received by Licensor and
-      subsequently incorporated within the Work.
-
-   2. Grant of Copyright License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      copyright license to reproduce, prepare Derivative Works of,
-      publicly display, publicly perform, sublicense, and distribute the
-      Work and such Derivative Works in Source or Object form.
-
-   3. Grant of Patent License. Subject to the terms and conditions of
-      this License, each Contributor hereby grants to You a perpetual,
-      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
-      (except as stated in this section) patent license to make, have made,
-      use, offer to sell, sell, import, and otherwise transfer the Work,
-      where such license applies only to those patent claims licensable
-      by such Contributor that are necessarily infringed by their
-      Contribution(s) alone or by combination of their Contribution(s)
-      with the Work to which such Contribution(s) was submitted. If You
-      institute patent litigation against any entity (including a
-      cross-claim or counterclaim in a lawsuit) alleging that the Work
-      or a Contribution incorporated within the Work constitutes direct
-      or contributory patent infringement, then any patent licenses
-      granted to You under this License for that Work shall terminate
-      as of the date such litigation is filed.
-
-   4. Redistribution. You may reproduce and distribute copies of the
-      Work or Derivative Works thereof in any medium, with or without
-      modifications, and in Source or Object form, provided that You
-      meet the following conditions:
-
-      (a) You must give any other recipients of the Work or
-          Derivative Works a copy of this License; and
-
-      (b) You must cause any modified files to carry prominent notices
-          stating that You changed the files; and
-
-      (c) You must retain, in the Source form of any Derivative Works
-          that You distribute, all copyright, patent, trademark, and
-          attribution notices from the Source form of the Work,
-          excluding those notices that do not pertain to any part of
-          the Derivative Works; and
-
-      (d) If the Work includes a "NOTICE" text file as part of its
-          distribution, then any Derivative Works that You distribute must
-          include a readable copy of the attribution notices contained
-          within such NOTICE file, excluding those notices that do not
-          pertain to any part of the Derivative Works, in at least one
-          of the following places: within a NOTICE text file distributed
-          as part of the Derivative Works; within the Source form or
-          documentation, if provided along with the Derivative Works; or,
-          within a display generated by the Derivative Works, if and
-          wherever such third-party notices normally appear. The contents
-          of the NOTICE file are for informational purposes only and
-          do not modify the License. You may add Your own attribution
-          notices within Derivative Works that You distribute, alongside
-          or as an addendum to the NOTICE text from the Work, provided
-          that such additional attribution notices cannot be construed
-          as modifying the License.
-
-      You may add Your own copyright statement to Your modifications and
-      may provide additional or different license terms and conditions
-      for use, reproduction, or distribution of Your modifications, or
-      for any such Derivative Works as a whole, provided Your use,
-      reproduction, and distribution of the Work otherwise complies with
-      the conditions stated in this License.
-
-   5. Submission of Contributions. Unless You explicitly state otherwise,
-      any Contribution intentionally submitted for inclusion in the Work
-      by You to the Licensor shall be under the terms and conditions of
-      this License, without any additional terms or conditions.
-      Notwithstanding the above, nothing herein shall supersede or modify
-      the terms of any separate license agreement you may have executed
-      with Licensor regarding such Contributions.
-
-   6. Trademarks. This License does not grant permission to use the trade
-      names, trademarks, service marks, or product names of the Licensor,
-      except as required for reasonable and customary use in describing the
-      origin of the Work and reproducing the content of the NOTICE file.
-
-   7. Disclaimer of Warranty. Unless required by applicable law or
-      agreed to in writing, Licensor provides the Work (and each
-      Contributor provides its Contributions) on an "AS IS" BASIS,
-      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
-      implied, including, without limitation, any warranties or conditions
-      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
-      PARTICULAR PURPOSE. You are solely responsible for determining the
-      appropriateness of using or redistributing the Work and assume any
-      risks associated with Your exercise of permissions under this License.
-
-   8. Limitation of Liability. In no event and under no legal theory,
-      whether in tort (including negligence), contract, or otherwise,
-      unless required by applicable law (such as deliberate and grossly
-      negligent acts) or agreed to in writing, shall any Contributor be
-      liable to You for damages, including any direct, indirect, special,
-      incidental, or consequential damages of any character arising as a
-      result of this License or out of the use or inability to use the
-      Work (including but not limited to damages for loss of goodwill,
-      work stoppage, computer failure or malfunction, or any and all
-      other commercial damages or losses), even if such Contributor
-      has been advised of the possibility of such damages.
-
-   9. Accepting Warranty or Additional Liability. While redistributing
-      the Work or Derivative Works thereof, You may choose to offer,
-      and charge a fee for, acceptance of support, warranty, indemnity,
-      or other liability obligations and/or rights consistent with this
-      License. However, in accepting such obligations, You may act only
-      on Your own behalf and on Your sole responsibility, not on behalf
-      of any other Contributor, and only if You agree to indemnify,
-      defend, and hold each Contributor harmless for any liability
-      incurred by, or claims asserted against, such Contributor by reason
-      of your accepting any such warranty or additional liability.
-
-   END OF TERMS AND CONDITIONS
-
-   APPENDIX: How to apply the Apache License to your work.
-
-      To apply the Apache License to your work, attach the following
-      boilerplate notice, with the fields enclosed by brackets "{}"
-      replaced with your own identifying information. (Don't include
-      the brackets!)  The text should be enclosed in the appropriate
-      comment syntax for the file format. We also recommend that a
-      file or class name and description of purpose be included on the
-      same "printed page" as the copyright notice for easier
-      identification within third-party archives.
-
-   Copyright {yyyy} {name of copyright owner}
-
-   Licensed under the Apache License, Version 2.0 (the "License");
-   you may not use this file except in compliance with the License.
-   You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-   Unless required by applicable law or agreed to in writing, software
-   distributed under the License is distributed on an "AS IS" BASIS,
-   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-   See the License for the specific language governing permissions and
-   limitations under the License.
diff --git a/Package.swift b/Package.swift
new file mode 100644
index 0000000..9524845
--- /dev/null
+++ b/Package.swift
@@ -0,0 +1,13 @@
+// swift-tools-version:5.0
+
+import PackageDescription
+
+let package = Package(
+    name: "JNI",
+    products: [
+        .library(name: "JNI", targets: ["JNI"])
+    ],
+    targets: [
+        .target(name: "JNI"),
+    ]
+)
diff --git a/README.md b/README.md
index 0877500..6e091e6 100644
--- a/README.md
+++ b/README.md
@@ -10,6 +10,6 @@ The naming has been kept as close to the C-functions as possible, including omit
 
 Internally the module uses JNI_OnLoad (which could be problematic if the user wants to use their own) to initialise an implicitly unwrapped global, `jni`. 
 
-This global can be then used from Swift code (from any thread) to run standard JNI functions like jni.GetIntArrayRegion(jarray), to return an array of Swift `Int`s
+This global can be then used from Swift code (from any thread) to run standard JNI functions like jni.GetIntArrayRegion(JavaArray), to return an array of Swift `Int`s
 
 See the article here for why (and a bit of how) this project came to be: https://medium.com/@ephemer/using-jni-in-swift-to-put-an-app-into-the-android-play-store-732e542a99dd#.rtviqfdlu
diff --git a/Sources/JNI/Array+JavaParameterConvertible.swift b/Sources/JNI/Array+JavaParameterConvertible.swift
new file mode 100644
index 0000000..404eb38
--- /dev/null
+++ b/Sources/JNI/Array+JavaParameterConvertible.swift
@@ -0,0 +1,36 @@
+//
+//  Array+JavaParameterConvertible.swift
+//  JNI
+//
+//  Created by flowing erik on 19.07.17.
+//
+
+extension String {
+    func replacingFullstopsWithSlashes() -> String {
+        return String(self.map { $0 == "." ? "/" : $0 })
+    }
+}
+
+extension Array where Element == JavaParameterConvertible {
+    public func asJavaParameters() -> [JavaParameter] {
+        return self.map { $0.toJavaParameter() }
+    }
+
+    /// Returns the String of ordererd arguments for use in JNI method signatures.
+    /// For example, the `"II"` in `(II)V`
+    private func argumentSignature() -> String {
+        return self.reduce("", { $0 + type(of: $1).asJNIParameterString })
+    }
+
+    /// Returns the String of ordered arguments enclosed in brackets, followed by the `returnType`'s type string, or 'V'
+    /// (Void) if nil is provided. e.g. Returns "(II)V" for `[JavaInt(1), JavaInt(99)].methodSignature(returnType: nil)`
+    public func methodSignature(returnType: JavaParameterConvertible.Type?) -> String {
+        let returnTypeString = returnType?.asJNIParameterString ?? "V"
+        return "(" + self.argumentSignature() + ")" + returnTypeString
+    }
+
+    public func methodSignature(customReturnType: String) -> String {
+        let returnTypeString = customReturnType.replacingFullstopsWithSlashes()
+        return "(" + self.argumentSignature() + ")" + returnTypeString
+    }
+}
diff --git a/Sources/JNI/CMakeLists.txt b/Sources/JNI/CMakeLists.txt
new file mode 100644
index 0000000..1d0c283
--- /dev/null
+++ b/Sources/JNI/CMakeLists.txt
@@ -0,0 +1,24 @@
+set(CMAKE_Swift_MODULE_DIRECTORY ${CMAKE_BINARY_DIR}/swift)
+
+add_library(JNI SHARED
+    isMainThread.swift
+    Array+JavaParameterConvertible.swift
+    JavaParameterConvertible+Objects.swift
+    JavaParameterConvertible+Primitives.swift
+    JavaParameterConvertible.swift
+    JNI.swift
+    JNIClassManipulation.swift
+    JNIExceptions.swift
+    JNIFields.swift
+    JNIMethods.swift
+    JNIObjects.swift
+    JNIRefs.swift
+    JNIStrings.swift
+    SwiftJNI.swift
+)
+
+set_target_properties(JNI PROPERTIES
+    INTERFACE_LINK_DIRECTORIES $<TARGET_LINKER_FILE_DIR:JNI>
+    INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_Swift_MODULE_DIRECTORY})
+
+# TODO Add ${SwiftJNI_SOURCE_DIR}/Sources/CJNI to interface include dirs?
diff --git a/Sources/JNI/JNI.swift b/Sources/JNI/JNI.swift
new file mode 100644
index 0000000..a571d11
--- /dev/null
+++ b/Sources/JNI/JNI.swift
@@ -0,0 +1,72 @@
+import Android
+
+public class JNI {
+    /// Our reference to the Java Virtual Machine, to be set on init
+    let _jvm: UnsafeMutablePointer<JavaVM?>
+
+    /// Ensure the _env pointer we have is always attached to the JVM
+    var _env: UnsafeMutablePointer<JNIEnv?> {
+        let jvm = _jvm.pointee!.pointee
+
+        // The type `JNIEnv` is defined as a non-mutable pointer,
+        // so use this mutable _tmpPointer as an intermediate:
+        var _tmpPointer: UnsafeMutableRawPointer?
+        let threadStatus = jvm.GetEnv(_jvm, &_tmpPointer, JavaInt(JNI_VERSION_1_6))
+        var _env = _tmpPointer?.bindMemory(to: Optional<JNIEnv>.self, capacity: 1)
+
+        switch threadStatus {
+        case JNI_OK: break // if we're already attached, do nothing
+        case JNI_EDETACHED:
+            // We weren't attached to the Java UI thread
+            _ = jvm.AttachCurrentThread(_jvm, &_env, nil)
+        case JNI_EVERSION:
+            fatalError("This version of JNI is not supported")
+        default: break
+        }
+
+        return _env!
+    }
+
+
+    // Normally we init the jni global ourselves in JNI_OnLoad
+    public init?(jvm: UnsafeMutablePointer<JavaVM?>) {
+        self._jvm = jvm
+    }
+}
+
+public extension JNI {
+    func GetVersion() -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.GetVersion(env)
+    }
+
+    func GetJavaVM(vm: UnsafeMutablePointer<UnsafeMutablePointer<JavaVM?>?>) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.GetJavaVM(env, vm)
+    }
+
+    func RegisterNatives(targetClass: JavaClass, _ methods: UnsafePointer<JNINativeMethod>, _ nMethods: JavaInt) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.RegisterNatives(env, targetClass, methods, nMethods)
+    }
+
+    func UnregisterNatives(targetClass: JavaClass) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.UnregisterNatives(env, targetClass)
+    }
+
+    func MonitorEnter(obj: JavaObject) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.MonitorEnter(env, obj)
+    }
+
+    func MonitorExit(obj: JavaObject) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.MonitorExit(env, obj)
+    }
+
+    func GetDirectBufferAddress(buffer: JavaObject) -> UnsafeMutableRawPointer? {
+        let env = self._env
+        return env.pointee!.pointee.GetDirectBufferAddress(env, buffer)
+    }
+}
diff --git a/Sources/JNI/JNIClassManipulation.swift b/Sources/JNI/JNIClassManipulation.swift
new file mode 100644
index 0000000..00dd77f
--- /dev/null
+++ b/Sources/JNI/JNIClassManipulation.swift
@@ -0,0 +1,38 @@
+public extension JNI {
+    func FindClass(name: String) throws -> JavaClass {
+        let env = self._env
+        let result = env.pointee!.pointee.FindClass(env, name.replacingFullstopsWithSlashes())
+        try checkAndThrowOnJNIError()
+        return result!
+    }
+
+    func FromReflectedMethod(method: JavaObject) -> JavaMethodID {
+        let env = self._env
+        return env.pointee!.pointee.FromReflectedMethod(env, method)!
+    }
+
+    func FromReflectedField(field: JavaObject) -> JavaFieldID {
+        let env = self._env
+        return env.pointee!.pointee.FromReflectedField(env, field)!
+    }
+
+    func ToReflectedMethod(targetClass: JavaClass, _ methodID: JavaMethodID, _ isStatic: JavaBoolean) -> JavaObject {
+        let env = self._env
+        return env.pointee!.pointee.ToReflectedMethod(env, targetClass, methodID, isStatic)!
+    }
+
+    func GetSuperclass(targetClass: JavaClass) -> JavaClass {
+        let env = self._env
+        return env.pointee!.pointee.GetSuperclass(env, targetClass)!
+    }
+
+    func IsAssignableFrom(classA: JavaClass, _ classB: JavaClass) -> JavaBoolean {
+        let env = self._env
+        return env.pointee!.pointee.IsAssignableFrom(env, classA, classB)
+    }
+
+    func ToReflectedField(targetClass: JavaClass, _ fieldID: JavaFieldID, _ isStatic: JavaBoolean) -> JavaObject {
+        let env = self._env
+        return env.pointee!.pointee.ToReflectedField(env, targetClass, fieldID, isStatic)!
+    }
+}
diff --git a/Sources/JNI/JNIExceptions.swift b/Sources/JNI/JNIExceptions.swift
new file mode 100644
index 0000000..7d5aba4
--- /dev/null
+++ b/Sources/JNI/JNIExceptions.swift
@@ -0,0 +1,36 @@
+public extension JNI {
+    func ExceptionCheck() -> Bool {
+        let env = self._env
+        return env.pointee!.pointee.ExceptionCheck(env) == .true
+    }
+
+    func ExceptionDescribe() {
+        let env = self._env
+        env.pointee!.pointee.ExceptionDescribe(env)
+    }
+
+    func ExceptionClear() {
+        let env = self._env
+        env.pointee!.pointee.ExceptionClear(env)
+    }
+
+    func ExceptionOccurred() -> JavaThrowable {
+        let env = self._env
+        return env.pointee!.pointee.ExceptionOccurred(env)!
+    }
+
+    func Throw(obj: JavaThrowable) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.Throw(env, obj)
+    }
+
+    func ThrowNew(targetClass: JavaClass, _ message: String) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.ThrowNew(env, targetClass, message)
+    }
+
+    func FatalError(msg: String) {
+        let env = self._env
+        env.pointee?.pointee.FatalError(env, msg)
+    }
+}
diff --git a/Sources/JNI/JNIFields.swift b/Sources/JNI/JNIFields.swift
new file mode 100644
index 0000000..328926a
--- /dev/null
+++ b/Sources/JNI/JNIFields.swift
@@ -0,0 +1,128 @@
+//
+//  JNIFields.swift
+//  JNI
+//
+//  Created by flowing erik on 01.08.17.
+//
+
+public extension JNI {
+     func GetStaticField<T: JavaInitializableFromField & JavaParameterConvertible>(_ fieldName: String, on javaClass: JavaClass) throws -> T {
+        let env = self._env
+        let fieldID = env.pointee!.pointee.GetStaticFieldID(env, javaClass, fieldName, T.asJNIParameterString)
+        try checkAndThrowOnJNIError()
+        return try T.fromStaticField(fieldID!, of: javaClass)
+    }
+
+    func GetField<T: JavaInitializableFromField & JavaParameterConvertible>(_ fieldName: String, from javaObject: JavaObject) throws -> T {
+        let env = self._env
+        let javaClass = try GetObjectClass(obj: javaObject)
+        defer {
+            jni.DeleteLocalRef(javaClass)
+        }
+        let fieldID = env.pointee!.pointee.GetFieldID(env, javaClass, fieldName, T.asJNIParameterString)
+        try checkAndThrowOnJNIError()
+        return try T.fromField(fieldID!, on: javaObject)
+    }
+
+    func GetField(_ fieldName: String, fieldJavaClassName: String, from javaObject: JavaObject) throws -> JavaObject {
+        let env = self._env
+        let javaClass = try GetObjectClass(obj: javaObject)
+        defer {
+            jni.DeleteLocalRef(javaClass)
+        }
+        let fieldID = env.pointee!.pointee.GetFieldID(env, javaClass, fieldName, "L\(fieldJavaClassName);")
+        try checkAndThrowOnJNIError()
+        return try JavaObject.fromField(fieldID!, on: javaObject)
+    }
+}
+
+
+public extension JNI {
+    // MARK: Fields
+
+    func GetBooleanField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaBoolean {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetBooleanField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetIntField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaInt {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetIntField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetFloatField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaFloat {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetFloatField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetLongField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaLong {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetLongField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetDoubleField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaDouble {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetDoubleField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetObjectField(of javaObject: JavaObject, id: JavaFieldID) throws -> JavaObject {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetObjectField(_env, javaObject, id)
+        try checkAndThrowOnJNIError()
+        return result!
+    }
+
+    // MARK: Static Fields
+
+    func GetStaticBooleanField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaBoolean {
+        let _env = self._env
+        let result =  _env.pointee!.pointee.GetStaticBooleanField(_env, javaClass, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetStaticIntField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaInt {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetStaticIntField(_env, javaClass, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetStaticFloatField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaFloat {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetStaticFloatField(_env, javaClass, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetStaticLongField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaLong {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetStaticLongField(_env, javaClass, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetStaticDoubleField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaDouble {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetStaticDoubleField(_env, javaClass, id)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetStaticObjectField(of javaClass: JavaClass, id: JavaFieldID) throws -> JavaObject {
+        let _env = self._env
+        guard let result = _env.pointee!.pointee.GetStaticObjectField(_env, javaClass, id) else { throw JNIError() }
+        try checkAndThrowOnJNIError()
+        return result
+    }
+}
diff --git a/Sources/JNI/JNIMethods.swift b/Sources/JNI/JNIMethods.swift
new file mode 100644
index 0000000..38eb026
--- /dev/null
+++ b/Sources/JNI/JNIMethods.swift
@@ -0,0 +1,217 @@
+enum SwiftJNIError: Error {
+    case invalidMethodID(_ methodSignature: String)
+}
+
+public extension JNI {
+    // MARK: Static Methods
+
+    func callStatic(_ methodName: String, on javaClass: JavaClass, arguments: [JavaParameterConvertible] = []) throws {
+        let methodSignature = arguments.methodSignature(returnType: nil)
+        let methodID = try jni.GetStaticMethodID(for: javaClass, methodName: methodName, methodSignature: methodSignature)
+
+        try jni.CallStaticVoidMethod(javaClass: javaClass, method: methodID, parameters: arguments.asJavaParameters())
+    }
+
+    func callStatic<T: JavaInitializableFromMethod & JavaParameterConvertible>(_ methodName: String, on javaClass: JavaClass, arguments: [JavaParameterConvertible] = []) throws -> T {
+        let methodSignature = arguments.methodSignature(returnType: T.self)
+        let methodID = try jni.GetStaticMethodID(for: javaClass, methodName: methodName, methodSignature: methodSignature)
+
+        return try T.fromStaticMethod(calling: methodID, on: javaClass, args: arguments.asJavaParameters())
+    }
+
+    func callStatic(_ methodName: String, on javaClass: JavaClass, arguments: [JavaParameterConvertible] = [], returningObjectType objectType: String) throws -> JavaObject {
+        let methodSignature = arguments.methodSignature(customReturnType: "L" + objectType + ";")
+        let methodID = try jni.GetStaticMethodID(for: javaClass, methodName: methodName, methodSignature: methodSignature)
+
+        return try jni.CallStaticObjectMethod(methodID, on: javaClass, parameters: arguments.asJavaParameters())
+    }
+
+    // MARK: Instance/Object Methods
+
+    func call(_ methodName: String, on object: JavaObject, arguments: [JavaParameterConvertible] = []) throws {
+        let methodSignature = arguments.methodSignature(returnType: nil)
+        let methodID = try jni.GetMethodID(for: object, methodName: methodName, methodSignature: methodSignature)
+
+        return try jni.CallVoidMethod(methodID, on: object, parameters: arguments.asJavaParameters())
+    }
+
+    func call<T: JavaInitializableFromMethod & JavaParameterConvertible>(_ methodName: String, on object: JavaObject, arguments: [JavaParameterConvertible] = []) throws -> T {
+        let methodSignature = arguments.methodSignature(returnType: T.self)
+        let methodID = try jni.GetMethodID(for: object, methodName: methodName, methodSignature: methodSignature)
+
+        return try T.fromMethod(calling: methodID, on: object, args: arguments.asJavaParameters())
+    }
+
+    func call(_ methodName: String, on object: JavaObject, arguments: [JavaParameterConvertible] = [], returningObjectType objectType: String) throws -> JavaObject {
+        let methodSignature = arguments.methodSignature(customReturnType: "L" + objectType + ";")
+        let methodID = try jni.GetMethodID(for: object, methodName: methodName, methodSignature: methodSignature)
+
+        return try jni.CallObjectMethod(methodID, on: object, parameters: arguments.asJavaParameters())
+    }
+
+    func call(_ methodName: String, on object: JavaObject, with arguments: [JavaParameterConvertible]) throws -> [String] {
+        let methodSignature = arguments.methodSignature(customReturnType: "[" + String.asJNIParameterString)
+        let methodID = try jni.GetMethodID(for: object, methodName: methodName, methodSignature: methodSignature)
+        let returnedArray = try jni.CallObjectMethod(methodID, on: object, parameters: arguments.asJavaParameters())
+
+        return try jni.GetStrings(from: returnedArray)
+    }
+}
+
+// --------------------------------------------------------------------------------------------------------------------
+// Lower-level implementations
+// --------------------------------------------------------------------------------------------------------------------
+
+
+// MARK: Getting Method IDs
+
+public extension JNI {
+    func GetMethodID(for object: JavaObject, methodName: String, methodSignature: String) throws -> JavaMethodID {
+        let _env = self._env
+        let objectClass = _env.pointee!.pointee.GetObjectClass(_env, object)
+        try checkAndThrowOnJNIError()
+
+        let result = _env.pointee!.pointee.GetMethodID(_env, objectClass!, methodName, methodSignature)
+        _env.pointee!.pointee.DeleteLocalRef(_env, objectClass)
+        try checkAndThrowOnJNIError()
+
+        return result!
+    }
+
+    func GetStaticMethodID(for javaClass: JavaClass, methodName: String, methodSignature: String) throws -> JavaMethodID {
+        let _env = self._env
+        guard let result = _env.pointee!.pointee.GetStaticMethodID(_env, javaClass, methodName, methodSignature) else {
+            throw SwiftJNIError.invalidMethodID(methodSignature)
+        }
+
+        try checkAndThrowOnJNIError()
+        return result
+    }
+}
+
+// MARK: Call instance methods
+
+public extension JNI {
+    func CallVoidMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws {
+        let _env = self._env
+        var methodArgs = parameters
+        _env.pointee!.pointee.CallVoidMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+    }
+
+    func CallBooleanMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaBoolean {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallBooleanMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallIntMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaInt {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallIntMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallFloatMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaFloat {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallFloatMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallLongMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaLong {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallLongMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallDoubleMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaDouble {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallDoubleMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallObjectMethod(_ method: JavaMethodID, on object: JavaObject, parameters: [JavaParameter]) throws -> JavaObject {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallObjectMethodA(_env, object, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result!
+    }
+}
+
+
+// MARK: Static methods
+
+public extension JNI {
+    func CallStaticObjectMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> JavaObject {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticObjectMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result! // we checked for error in the line above
+    }
+
+    func CallStaticBooleanMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> Bool {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticBooleanMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result == .true
+    }
+
+    func CallStaticIntMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> JavaInt {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticIntMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallStaticLongMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> JavaLong {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticLongMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallStaticFloatMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> JavaFloat {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticFloatMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallStaticDoubleMethod(_ method: JavaMethodID, on javaClass: JavaClass, parameters: [JavaParameter]) throws -> JavaDouble {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticDoubleMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallStaticBooleanMethod(javaClass: JavaClass, method: JavaMethodID, parameters: [JavaParameter]) throws -> JavaBoolean {
+        let _env = self._env
+        var methodArgs = parameters
+        let result = _env.pointee!.pointee.CallStaticBooleanMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func CallStaticVoidMethod(javaClass: JavaClass, method: JavaMethodID, parameters: [JavaParameter]) throws {
+        let _env = self._env
+        var methodArgs = parameters
+        _env.pointee!.pointee.CallStaticVoidMethodA(_env, javaClass, method, &methodArgs)
+        try checkAndThrowOnJNIError()
+    }
+}
diff --git a/Sources/JNI/JNIObjects.swift b/Sources/JNI/JNIObjects.swift
new file mode 100644
index 0000000..88a84b1
--- /dev/null
+++ b/Sources/JNI/JNIObjects.swift
@@ -0,0 +1,165 @@
+import Dispatch
+
+/// Designed to simplify calling a constructor and methods on a JavaClass
+/// Subclass this and add the methods appropriate to the object you are constructing.
+open class JNIObject: JavaParameterConvertible, Sendable {
+    open class var className: String {
+        return "java.lang.object"
+    }
+
+    public class var asJNIParameterString: String {
+        "L\(Self.className.replacingFullstopsWithSlashes());"
+    }
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(l: self.instance)
+    }
+
+    private static var classInstances = [String: JavaClass]()
+
+    public static var javaClass: JavaClass {
+        func getClassInstance() -> JavaClass {
+            if let classInstance = classInstances[className] {
+                return classInstance
+            }
+
+            let javaClassLocalRef = try! jni.FindClass(name: className.replacingFullstopsWithSlashes())
+            try! checkAndThrowOnJNIError()
+            let classInstance = jni.NewGlobalRef(javaClassLocalRef)!
+            classInstances[className] = classInstance
+
+            return classInstance
+        }
+
+        if isMainThread {
+            return getClassInstance()
+        } else {
+            return DispatchQueue.main.sync {
+                return getClassInstance()
+            }
+        }
+
+    }
+
+    public let instance: JavaObject
+
+    required public init(_ instance: JavaObject) throws {
+        guard let globalInstanceRef = jni.NewGlobalRef(instance) else {
+            throw Error.couldntCreateGlobalRef
+        }
+
+        try checkAndThrowOnJNIError()
+        self.instance = globalInstanceRef
+    }
+
+    @available(*, deprecated, message: "Override Self.className instead and use the initializers that don't take className as an argument")
+    convenience public init(_ className: String, arguments: [JavaParameterConvertible] = []) throws {
+        let className = className.replacingFullstopsWithSlashes()
+        let javaClassLocalRef = try jni.FindClass(name: className)
+
+        guard let instanceLocalRef = try jni.callConstructor(on: javaClassLocalRef, arguments: arguments) else {
+            throw Error.couldntCallConstructor
+        }
+
+        try self.init(instanceLocalRef)
+    }
+
+    convenience public init(arguments: JavaParameterConvertible...) throws {
+        guard let instanceLocalRef = try jni.callConstructor(on: type(of: self).javaClass, arguments: arguments) else {
+            throw Error.couldntCallConstructor
+        }
+
+        try self.init(instanceLocalRef)
+    }
+
+    deinit {
+        jni.DeleteGlobalRef(instance)
+    }
+
+    public func call(_ methodName: String, arguments: [JavaParameterConvertible] = []) throws {
+        try jni.call(methodName, on: self.instance, arguments: arguments)
+    }
+
+    public func call<T: JavaInitializableFromMethod & JavaParameterConvertible>(
+        _ methodName: String,
+        arguments: [JavaParameterConvertible] = []
+    ) throws -> T {
+        return try jni.call(methodName, on: self.instance, arguments: arguments)
+    }
+
+    public func getField<T: JavaInitializableFromField & JavaParameterConvertible>(
+        _ fieldName: String
+    ) throws -> T {
+        return try jni.GetField(fieldName, from: self.instance)
+    }
+
+    public func getField(_ fieldName: String, fieldJavaClassName: String) throws -> JavaObject {
+        return try jni.GetField(fieldName, fieldJavaClassName: fieldJavaClassName, from: self.instance)
+    }
+
+    public static func callStatic(_ methodName: String, arguments: [JavaParameterConvertible] = []) throws {
+        try jni.callStatic(methodName, on: self.javaClass, arguments: arguments)
+    }
+
+    public static func callStatic<T: JavaInitializableFromMethod & JavaParameterConvertible>(
+        _ methodName: String,
+        arguments: [JavaParameterConvertible] = []
+    ) throws -> T {
+        return try jni.callStatic(methodName, on: self.javaClass, arguments: arguments)
+    }
+}
+
+extension JNIObject {
+    enum Error: Swift.Error {
+        case couldntCreateGlobalRef
+        case couldntCallConstructor
+    }
+}
+
+extension JNI {
+    func callConstructor(
+        on targetClass: JavaClass,
+        arguments: [JavaParameterConvertible] = []
+    ) throws -> JavaObject? {
+        let methodID = _env.pointee!.pointee.GetMethodID(_env, targetClass, "<init>", arguments.methodSignature(returnType: nil))
+        try checkAndThrowOnJNIError()
+
+        return try jni.NewObject(targetClass: targetClass, methodID!, arguments.asJavaParameters())
+    }
+}
+
+public extension JNI {
+    func NewObject(targetClass: JavaClass, _ methodID: JavaMethodID, _ args: [JavaParameter]) throws -> JavaObject? {
+        let env = self._env
+        var mutableArgs = args
+        let newObject = env.pointee!.pointee.NewObjectA(env, targetClass, methodID, &mutableArgs)
+        try checkAndThrowOnJNIError()
+
+        return newObject
+    }
+
+    func GetObjectClass(obj: JavaObject) throws -> JavaClass {
+        let env = self._env
+        let result = env.pointee!.pointee.GetObjectClass(env, obj)
+        try checkAndThrowOnJNIError()
+        return result!
+    }
+}
+
+extension JNIObject: JavaInitializableFromMethod, JavaInitializableFromField {
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Self {
+        return try Self(jni.GetStaticObjectField(of: javaClass, id: fieldID))
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Self {
+        return try Self(jni.CallObjectMethod(methodID, on: object, parameters: args))
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Self {
+        return try Self(jni.CallStaticObjectMethod(methodID, on: javaClass, parameters: args))
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Self {
+        return try Self(jni.GetObjectField(of: javaObject, id: fieldID))
+    }
+}
\ No newline at end of file
diff --git a/Sources/JNI/JNIRefs.swift b/Sources/JNI/JNIRefs.swift
new file mode 100644
index 0000000..511ed4b
--- /dev/null
+++ b/Sources/JNI/JNIRefs.swift
@@ -0,0 +1,62 @@
+public extension JNI {
+    func NewGlobalRef(_ object: JavaObject) -> JavaObject? {
+        let env = self._env
+        return env.pointee!.pointee.NewGlobalRef(env, object)
+    }
+
+    func DeleteGlobalRef(_ globalRef: JavaObject) {
+        let env = self._env
+        env.pointee!.pointee.DeleteGlobalRef(env, globalRef)
+    }
+
+    func NewLocalRef(_ ref: JavaObject) -> JavaObject? {
+        let env = self._env
+        return env.pointee!.pointee.NewLocalRef(env, ref)
+    }
+
+    func DeleteLocalRef(_ localRef: JavaObject) {
+        let env = self._env
+        env.pointee!.pointee.DeleteLocalRef(env, localRef)
+    }
+
+    func PushLocalFrame(_ capacity: JavaInt) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.PushLocalFrame(env, capacity)
+    }
+
+    func PopLocalFrame(_ result: JavaObject) -> JavaObject {
+        let env = self._env
+        return env.pointee!.pointee.PopLocalFrame(env, result)!
+    }
+
+    func EnsureLocalCapacity(_ capacity: JavaInt) -> JavaInt {
+        let env = self._env
+        return env.pointee!.pointee.EnsureLocalCapacity(env, capacity)
+    }
+
+    func IsSameObject(_ ref1: JavaObject, _ ref2: JavaObject) -> JavaBoolean {
+        let env = self._env
+        return env.pointee!.pointee.IsSameObject(env, ref1, ref2)
+    }
+
+    func IsInstanceOf(_ obj: JavaObject, _ targetClass: JavaClass) -> JavaBoolean {
+        let env = self._env
+        return env.pointee!.pointee.IsInstanceOf(env, obj, targetClass)
+    }
+
+    func NewWeakGlobalRef(_ obj: JavaObject) -> JavaWeakReference {
+        let env = self._env
+        return env.pointee!.pointee.NewWeakGlobalRef(env, obj)!
+    }
+
+    func DeleteWeakGlobalRef(_ obj: JavaWeakReference) {
+        let env = self._env
+        env.pointee!.pointee.DeleteWeakGlobalRef(env, obj)
+    }
+
+    /* added in 1: JNI.6 */
+    func GetObjectRefType(_ obj: JavaObject) -> JavaObjectRefType {
+        let env = self._env
+        return env.pointee!.pointee.GetObjectRefType(env, obj)
+    }
+}
diff --git a/Sources/JNI/JNIStrings.swift b/Sources/JNI/JNIStrings.swift
new file mode 100644
index 0000000..8058977
--- /dev/null
+++ b/Sources/JNI/JNIStrings.swift
@@ -0,0 +1,72 @@
+public extension String {
+    init(javaString: JavaString) throws {
+        let env = jni._env
+        let chars = env.pointee!.pointee.GetStringUTFChars(env, javaString, nil)
+        defer { env.pointee!.pointee.ReleaseStringUTFChars(env, javaString, chars) }
+        try checkAndThrowOnJNIError()
+
+        self.init(cString: chars!)
+    }
+}
+
+public extension JNI {
+    func NewString(unicodeChars: UnsafePointer<JavaChar>, _ length: JavaSize) -> JavaString {
+        let env = self._env
+        return env.pointee!.pointee.NewString(env, unicodeChars, length)!
+    }
+
+    func GetStringLength(_ jString: JavaString) -> JavaSize {
+        let env = self._env
+        return env.pointee!.pointee.GetStringLength(env, jString)
+    }
+
+    func GetStringChars(_ jString: JavaString, _ isCopy: UnsafeMutablePointer<JavaBoolean>) -> UnsafePointer<JavaChar> {
+        let env = self._env
+        return env.pointee!.pointee.GetStringChars(env, jString, isCopy)!
+    }
+
+    func ReleaseStringChars(_ jString: JavaString, _ chars: UnsafePointer<JavaChar>) {
+        let env = self._env
+        env.pointee!.pointee.ReleaseStringChars(env, jString, chars)
+    }
+
+    func NewStringUTF(_ string: String) -> JavaString {
+        let env = self._env
+        return env.pointee!.pointee.NewStringUTF(env, string)!
+    }
+
+    func GetStringUTFLength(_ jString: JavaString) -> JavaSize {
+        let env = self._env
+        return env.pointee!.pointee.GetStringUTFLength(env, jString)
+    }
+
+    func GetStringUTFChars(_ jString: JavaString, _ isCopy: UnsafeMutablePointer<JavaBoolean>) -> String {
+        let env = self._env
+        return String(describing: env.pointee!.pointee.GetStringUTFChars(env, jString, isCopy))
+    }
+
+    func ReleaseStringUTFChars(_ jString: JavaString, _ utf: String) {
+        let env = self._env
+        env.pointee!.pointee.ReleaseStringUTFChars(env, jString, utf)
+    }
+
+    func GetStringRegion(_ jString: JavaString, _ start: JavaSize, _ length: JavaSize, _ buf: UnsafeMutablePointer<JavaChar>) {
+        let env = self._env
+        env.pointee!.pointee.GetStringRegion(env, jString, start, length, buf)
+    }
+
+    func GetStringUTFRegion(_ jString: JavaString, _ start: JavaSize, _ length: JavaSize, _ buf: UnsafeMutablePointer<CChar>) {
+        let env = self._env
+        env.pointee!.pointee.GetStringUTFRegion(env, jString, start, length, buf)
+    }
+
+    func GetStringCritical(_ jString: JavaString, _ isCopy: UnsafeMutablePointer<JavaBoolean>) -> UnsafePointer<JavaChar> {
+        let env = self._env
+        return env.pointee!.pointee.GetStringCritical(env, jString, isCopy)!
+    }
+
+    func ReleaseStringCritical(_ jString: JavaString, _ cArray: UnsafePointer<JavaChar>) {
+        let env = self._env
+        env.pointee!.pointee.ReleaseStringCritical(env, jString, cArray)
+    }
+}
diff --git a/Sources/JNI/JavaParameterConvertible+Objects.swift b/Sources/JNI/JavaParameterConvertible+Objects.swift
new file mode 100644
index 0000000..bced34e
--- /dev/null
+++ b/Sources/JNI/JavaParameterConvertible+Objects.swift
@@ -0,0 +1,96 @@
+// JavaObject
+
+extension JavaObject: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    private static let javaClassname = "java/lang/Object"
+    public static let asJNIParameterString = "L\(javaClassname);"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(l: self)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> JavaObject {
+        return try jni.GetStaticObjectField(of: javaClass, id: fieldID)
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> JavaObject {
+        return try jni.CallObjectMethod(methodID, on: object, parameters: args)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> JavaObject {
+        return try jni.CallStaticObjectMethod(methodID, on: javaClass, parameters: args)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> JavaObject {
+        return try jni.GetObjectField(of: javaObject, id: fieldID)
+    }
+}
+
+
+// String
+
+extension String: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    private static let javaClassname = "java/lang/String"
+    public static let asJNIParameterString = "L\(javaClassname);"
+
+    public func toJavaParameter() -> JavaParameter {
+        let stringAsObject = jni.NewStringUTF(self)
+        return JavaParameter(l: stringAsObject)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> String {
+        let jobject: JavaObject = try jni.GetStaticObjectField(of: javaClass, id: fieldID)
+        return try String(javaString: jobject)
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> String {
+        let jObject = try jni.CallObjectMethod(methodID, on: object, parameters: args)
+        return try String(javaString: jObject)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> String {
+        let jObject = try jni.CallStaticObjectMethod(methodID, on: javaClass, parameters: args)
+        return try String(javaString: jObject)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> String {
+        let javaStringObject = try jni.GetObjectField(of: javaObject, id: fieldID)
+        return try String(javaString: javaStringObject)
+    }
+}
+
+
+// Context
+
+public struct JavaContext: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    private let object: JavaObject
+    public init(_ object: JavaObject) {
+        self.object = object
+    }
+
+    private static let javaClassname = "android/content/Context"
+    public static let asJNIParameterString = "L\(javaClassname);"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(l: self.object)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> JavaContext {
+        let jobject: JavaObject = try jni.GetStaticObjectField(of: javaClass, id: fieldID)
+        return self.init(jobject)
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> JavaContext {
+        let jObject = try jni.CallObjectMethod(methodID, on: object, parameters: args)
+        return self.init(jObject)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> JavaContext {
+        let jObject = try jni.CallStaticObjectMethod(methodID, on: javaClass, parameters: args)
+        return self.init(jObject)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> JavaContext {
+        let javaStringObject = try jni.GetObjectField(of: javaObject, id: fieldID)
+        return self.init(javaStringObject)
+    }
+}
diff --git a/Sources/JNI/JavaParameterConvertible+Primitives.swift b/Sources/JNI/JavaParameterConvertible+Primitives.swift
new file mode 100644
index 0000000..be701b2
--- /dev/null
+++ b/Sources/JNI/JavaParameterConvertible+Primitives.swift
@@ -0,0 +1,158 @@
+// Bool
+
+extension Bool: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "Z"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(z: (self) ? 1 : 0)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Bool {
+        return try jni.GetStaticBooleanField(of: javaClass, id: fieldID) == .true
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Bool {
+        return try jni.CallBooleanMethod(methodID, on: object, parameters: args) == .true
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Bool {
+        return try jni.CallStaticBooleanMethod(javaClass: javaClass, method: methodID, parameters: args) == .true
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Bool {
+        return try jni.GetBooleanField(of: javaObject, id: fieldID) == .true
+    }
+}
+
+
+// Int
+extension Int: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "I"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(i: JavaInt(self))
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Int {
+        return try Int(jni.GetStaticIntField(of: javaClass, id: fieldID))
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Int {
+        return try Int(jni.CallIntMethod(methodID, on: object, parameters: args))
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Int {
+        return try Int(jni.CallStaticIntMethod(methodID, on: javaClass, parameters: args))
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Int {
+        return try Int(jni.GetIntField(of: javaObject, id: fieldID))
+    }
+}
+
+
+// JavaInt
+
+extension JavaInt: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "I"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(i: self)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> JavaInt {
+        return try jni.GetStaticIntField(of: javaClass, id: fieldID)
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> JavaInt {
+        return try jni.CallIntMethod(methodID, on: object, parameters: args)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> JavaInt {
+        return try jni.CallStaticIntMethod(methodID, on: javaClass, parameters: args)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> JavaInt {
+        return try jni.GetIntField(of: javaObject, id: fieldID)
+    }
+}
+
+
+// Double
+
+extension Double: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "D"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(d: JavaDouble(self))
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Double {
+        return try Double(jni.GetStaticDoubleField(of: javaClass, id: fieldID))
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Double {
+        return try jni.CallDoubleMethod(methodID, on: object, parameters: args)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Double {
+        return try jni.CallStaticDoubleMethod(methodID, on: javaClass, parameters: args)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Double {
+        return try jni.GetDoubleField(of: javaObject, id: fieldID)
+    }
+}
+
+
+// Float
+
+extension Float: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "F"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(f: JavaFloat(self))
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Float {
+        return try Float(jni.GetStaticFloatField(of: javaClass, id: fieldID))
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Float {
+        return try jni.CallFloatMethod(methodID, on: object, parameters: args)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Float {
+        return try jni.CallStaticFloatMethod(methodID, on: javaClass, parameters: args)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Float {
+        return try jni.GetFloatField(of: javaObject, id: fieldID)
+    }
+}
+
+// JavaLong aka Int64
+
+extension JavaLong: JavaParameterConvertible, JavaInitializableFromMethod, JavaInitializableFromField {
+    public static let asJNIParameterString = "J"
+
+    public func toJavaParameter() -> JavaParameter {
+        return JavaParameter(j: self)
+    }
+
+    public static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> JavaLong {
+        return try JavaLong(jni.GetStaticLongField(of: javaClass, id: fieldID))
+    }
+
+    public static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> JavaLong {
+        return try jni.CallLongMethod(methodID, on: object, parameters: args)
+    }
+
+    public static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> JavaLong {
+        return try jni.CallStaticLongMethod(methodID, on: javaClass, parameters: args)
+    }
+
+    public static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> JavaLong {
+        return try jni.GetLongField(of: javaObject, id: fieldID)
+    }
+}
diff --git a/Sources/JNI/JavaParameterConvertible.swift b/Sources/JNI/JavaParameterConvertible.swift
new file mode 100644
index 0000000..0f96d85
--- /dev/null
+++ b/Sources/JNI/JavaParameterConvertible.swift
@@ -0,0 +1,22 @@
+//
+//  JavaParameterConvertible.swift
+//  JNI
+//
+//  Created by flowing erik on 19.07.17.
+//
+
+public protocol JavaParameterConvertible {
+    typealias JavaMethod = ((JavaParameterConvertible...) throws -> Self)
+    static var asJNIParameterString: String { get }
+    func toJavaParameter() -> JavaParameter
+}
+
+public protocol JavaInitializableFromMethod {
+    static func fromMethod(calling methodID: JavaMethodID, on object: JavaObject, args: [JavaParameter]) throws -> Self
+    static func fromStaticMethod(calling methodID: JavaMethodID, on javaClass: JavaClass, args: [JavaParameter]) throws -> Self
+}
+
+public protocol JavaInitializableFromField {
+    static func fromStaticField(_ fieldID: JavaFieldID, of javaClass: JavaClass) throws -> Self
+    static func fromField(_ fieldID: JavaFieldID, on javaObject: JavaObject) throws -> Self
+}
diff --git a/Sources/JNI/SwiftJNI.swift b/Sources/JNI/SwiftJNI.swift
new file mode 100644
index 0000000..2bea416
--- /dev/null
+++ b/Sources/JNI/SwiftJNI.swift
@@ -0,0 +1,242 @@
+@_exported import jni
+
+public var jni: JNI! // this gets set "OnLoad" so should always exist
+
+#if !STATIC_SWIFT_STDLIB
+@_cdecl("JNI_OnLoad")
+public func JNI_Onload(_ vm: UnsafeMutablePointer<JavaVM?>, _ reserved: UnsafeMutableRawPointer?) -> JavaInt {
+    return SwiftJNI_OnLoad(vm, reserved)
+}
+#endif
+
+// Can be called manually from another call to JNI_OnLoad
+// e.g. from the user's JNI_OnLoad function defined in the same static library
+public func SwiftJNI_OnLoad(_ vm: UnsafeMutablePointer<JavaVM?>, _ reserved: UnsafeMutableRawPointer?) -> JavaInt {
+    guard let localJNI = JNI(jvm: vm) else {
+         fatalError("Couldn't initialise JNI")
+    }
+
+    jni = localJNI // set the global for use elsewhere
+    return JNI_VERSION_1_6
+}
+
+public func JNI_DetachCurrentThread() {
+    _ = jni._jvm.pointee!.pointee.DetachCurrentThread(jni._jvm)
+}
+
+public extension JavaBoolean {
+    static let `true` = JavaBoolean(JNI_TRUE)
+    static let `false` = JavaBoolean(JNI_FALSE)
+}
+
+// SwiftJNI Public API
+public extension JNI {
+    func RegisterNatives(javaClass: JavaClass, methods: [JNINativeMethod]) -> Bool {
+        let _env = self._env
+        let env = _env.pointee!.pointee
+        let result = env.RegisterNatives(_env, javaClass, methods, JavaInt(methods.count))
+        return (result == 0)
+    }
+
+    func ThrowNew(message: String) {
+        let _env = self._env
+        let env = _env.pointee!.pointee
+        _ = env.ThrowNew(_env, env.FindClass(_env, "java/lang/Exception"), message)
+    }
+
+    // MARK: Arrays
+
+    func GetLength(_ array: JavaArray) -> Int {
+        let _env = self._env
+        let result = _env.pointee!.pointee.GetArrayLength(_env, array)
+        return Int(result)
+    }
+
+    func NewIntArray(count: Int) throws -> JavaArray? {
+        let _env = self._env
+        let result = _env.pointee!.pointee.NewIntArray(_env, jsize(count))
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func NewByteArray(count: Int) throws -> JavaByteArray? {
+        let _env = self._env
+        let result = _env.pointee!.pointee.NewByteArray(_env, jsize(count))
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func NewBooleanArray(count: Int) throws -> JavaBooleanArray? {
+        let _env = self._env
+        let result = _env.pointee!.pointee.NewBooleanArray(_env, jsize(count))
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetBooleanArrayRegion(array: JavaBooleanArray, startIndex: Int = 0, numElements: Int = -1) -> [Bool] {
+        let _env = self._env
+        var count = numElements
+
+        if numElements < 0 {
+            count = GetLength(array)
+        }
+
+        var result = [JavaBoolean](repeating: 0, count: count)
+        _env.pointee!.pointee.GetBooleanArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
+
+        return result.map { $0 == .true }
+    }
+
+    func SetBooleanArrayRegion(array: JavaBooleanArray, startIndex: Int = 0, from sourceElements: [Bool]) {
+        let _env = self._env
+        var newElements = sourceElements.map { $0 ? JavaBoolean.true : .false } // make mutable copy
+        _env.pointee!.pointee.SetBooleanArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
+    }
+
+    func GetByteArrayRegion(array: JavaByteArray, startIndex: Int = 0, numElements: Int = -1) -> [UInt8] {
+        let _env = self._env
+        var count = numElements
+
+        if numElements < 0 {
+            count = GetLength(array)
+        }
+
+        var result = [JavaByte](repeating: 0, count: count)
+        _env.pointee!.pointee.GetByteArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
+
+        // Conversion from Int8 (JavaByte) to UInt8: bitPattern-constructor ensures
+        // that negative Int8 values do not cause a crash when trying convert them to UInt8
+        return result.map { UInt8(bitPattern: $0) }
+    }
+
+    func SetByteArrayRegion(array: JavaByteArray, startIndex: Int = 0, from sourceElements: Array<UInt8>) {
+        let _env = self._env
+        var newElements = sourceElements.map { JavaByte(bitPattern: $0) } // make mutable copy
+        _env.pointee!.pointee.SetByteArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
+    }
+
+    func GetIntArrayRegion(array: JavaIntArray, startIndex: Int = 0, numElements: Int = -1) -> [JavaInt] {
+        let _env = self._env
+        var count = numElements
+
+        if numElements < 0 {
+            count = GetLength(array)
+        }
+
+        var result = [JavaInt](repeating: 0, count: count)
+        _env.pointee!.pointee.GetIntArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
+        return result
+    }
+
+    func SetIntArrayRegion(array: JavaIntArray, startIndex: Int = 0, from sourceElements: [Int]) {
+        let _env = self._env
+        var newElements = sourceElements.map { JavaInt($0) } // make mutable copy
+        _env.pointee!.pointee.SetIntArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
+    }
+
+    func NewFloatArray(count: Int) throws -> JavaArray? {
+        let _env = self._env
+        let result = _env.pointee!.pointee.NewFloatArray(_env, jsize(count))
+        try checkAndThrowOnJNIError()
+        return result
+    }
+
+    func GetFloatArrayRegion(array: JavaFloatArray, startIndex: Int = 0, numElements: Int = -1) -> [Float] {
+        let _env = self._env
+        var count = numElements
+
+        if numElements < 0 {
+            count = GetLength(array)
+        }
+
+        var result = [JavaFloat](repeating: 0, count: count)
+        _env.pointee!.pointee.GetFloatArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
+        return result.map { Float($0) }
+    }
+
+    func SetFloatArrayRegion(array: JavaFloatArray, startIndex: Int = 0, from sourceElements: [Float]) {
+        let _env = self._env
+        var newElements = sourceElements.map { JavaFloat($0) } // make mutable copy
+        _env.pointee!.pointee.SetFloatArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
+    }
+
+    func GetStrings(from array: JavaObjectArray) throws -> [String] {
+        let _env = self._env
+        let count = jni.GetLength(array)
+
+        let strings: [String] = try (0 ..< count).map { i in
+            let jString: JavaString? = _env.pointee!.pointee.GetObjectArrayElement(_env, array, jsize(i))
+            let chars = _env.pointee!.pointee.GetStringUTFChars(_env, jString, nil)
+            try checkAndThrowOnJNIError()
+            defer { _env.pointee!.pointee.ReleaseStringUTFChars(_env, jString, chars) }
+
+            return String(cString: chars!)
+        }
+
+        return strings
+    }
+
+    func GetObjectArrayElement(in array: JavaObjectArray, at index: Int) throws -> JavaObject {
+        let _env = self._env
+        let count = jni.GetLength(array)
+        if (index >= count) {
+            throw JNIError()
+        }
+        let jObj = _env.pointee!.pointee.GetObjectArrayElement(_env, array, jsize(index))
+        try checkAndThrowOnJNIError()
+        return jObj!
+    }
+}
+
+func checkAndThrowOnJNIError() throws {
+    if jni.ExceptionCheck() { throw JNIError() }
+}
+
+/// Prints information about the error to the console and clears the pending exception so we can continue making JNI calls
+struct JNIError: Error {
+    init() {
+        jni.ExceptionDescribe()
+        jni.ExceptionClear()
+    }
+}
+
+#if os(Android)
+@discardableResult
+@_silgen_name("__android_log_write")
+public func androidPrint(_ prio: Int32, _ tag: UnsafePointer<CChar>, _ text: UnsafePointer<CChar>) -> Int32
+
+func print(_ string: String) {
+    androidPrint(5, "SwiftJNI", string)
+}
+#endif
+
+public typealias JavaBoolean = jboolean
+public typealias JavaByte = jbyte
+public typealias JavaChar = jchar
+public typealias JavaShort = jshort
+public typealias JavaInt = jint
+public typealias JavaLong = jlong
+public typealias JavaFloat = jfloat
+public typealias JavaDouble = jdouble
+public typealias JavaSize = jint
+
+public typealias JavaParameter = jvalue
+public typealias JavaObjectRefType = jobjectRefType
+public typealias JavaFieldID = jfieldID
+public typealias JavaMethodID = jmethodID
+
+public typealias JavaObject = UnsafeMutableRawPointer
+public typealias JavaClass = JavaObject
+public typealias JavaString = JavaObject
+public typealias JavaArray = JavaObject
+public typealias JavaObjectArray = JavaArray
+public typealias JavaBooleanArray = JavaArray
+public typealias JavaByteArray = JavaArray
+public typealias JavaCharArray = JavaArray
+public typealias JavaShortArray = JavaArray
+public typealias JavaIntArray = JavaArray
+public typealias JavaLongArray = JavaArray
+public typealias JavaFloatArray = JavaArray
+public typealias JavaDoubleArray = JavaArray
+public typealias JavaThrowable = JavaObject
+public typealias JavaWeakReference = JavaObject
diff --git a/Sources/JNI/isMainThread.swift b/Sources/JNI/isMainThread.swift
new file mode 100644
index 0000000..2319620
--- /dev/null
+++ b/Sources/JNI/isMainThread.swift
@@ -0,0 +1,8 @@
+import Android
+
+@_silgen_name("syscall")
+public func syscallNonVariadic(_ number: Int) -> Int
+
+public var isMainThread: Bool {
+    return syscallNonVariadic(Int(SYS_gettid)) == getpid()
+}
\ No newline at end of file
diff --git a/SwiftJNI.swift b/SwiftJNI.swift
deleted file mode 100644
index 61c4a63..0000000
--- a/SwiftJNI.swift
+++ /dev/null
@@ -1,216 +0,0 @@
-import CJNI
-
-public var jni: JNI! // this gets set "OnLoad" so should always exist
-
-@_silgen_name("JNI_OnLoad")
-public func JNI_OnLoad(jvm: UnsafeMutablePointer<JavaVM>, reserved: UnsafeMutablePointer<Void>) -> jint {
-
-    guard let localJNI = JNI(jvm: jvm) else {
-         fatalError("Couldn't initialise JNI")
-    }
-
-    jni = localJNI // set the global for use elsewhere
-
-    return JNI_VERSION_1_6
-}
-
-extension jboolean : BooleanLiteralConvertible {
-    public init(booleanLiteral value: Bool) {
-        self = value ? jboolean(JNI_TRUE) : jboolean(JNI_FALSE)
-    }
-}
-
-// SwiftJNI Public API
-public class SwiftJNI : JNI {
-    public func RegisterNatives(jClass: jclass, methods: [JNINativeMethod]) -> Bool {
-        let _env = self._env
-		let env = _env.memory.memory
-        let result = env.RegisterNatives(_env, jClass, methods, jint(methods.count))
-        return (result == 0)
-    }
-
-    public func ThrowNew(message: String) {
-        let _env = self._env
-        let env = _env.memory.memory
-        env.ThrowNew(_env, env.FindClass(_env, "java/lang/Exception"), message)
-    }
-
-    /// - Note: This shouldn't need to be cleaned up because we're not taking ownership of the reference
-    public func GetStringUTFChars(string: jstring) -> UnsafePointer<CChar> {
-        let _env = self._env
-        var didCopyStringChars = jboolean() // XXX: this gets set below, check it!
-        return _env.memory.memory.GetStringUTFChars(_env, string, &didCopyStringChars)
-    }
-
-    // MARK: References
-
-    public func NewGlobalRef(object: jobject) -> jobject? {
-        let _env = self._env
-        let result = _env.memory.memory.NewGlobalRef(_env, object)
-        return (result != nil) ? result : .None
-    }
-
-    // MARK: Classes and Methods
-
-    public func FindClass(className: String) -> jclass? {
-        let _env = self._env
-        let result = _env.memory.memory.FindClass(_env, className)
-        return (result != nil) ? result : .None
-    }
-
-    public func GetMethodID(javaClass: jclass, methodName: UnsafePointer<CChar>, methodSignature: UnsafePointer<CChar>) -> jmethodID? {
-        let _env = self._env
-        let result = _env.memory.memory.GetMethodID(_env, javaClass, methodName, methodSignature)
-        return (result != nil) ? result : .None
-    }
-
-    // TODO: make parameters take [JValue], being a swifty version of [jvalue] with reference counting etc.
-    public func CallVoidMethodA(object: jobject, methodID method: jmethodID, parameters: [jvalue]) {
-        let _env = self._env
-        var methodArgs = parameters
-        _env.memory.memory.CallVoidMethodA(_env, object, method, &methodArgs)
-    }
-
-    // MARK: Arrays
-
-    public func GetArrayLength(array: jarray) -> Int {
-        let _env = self._env
-        let result = _env.memory.memory.GetArrayLength(_env, array)
-        return Int(result)
-    }
-
-    public func NewIntArray(count: Int) -> jarray? {
-        let _env = self._env
-        let result = _env.memory.memory.NewIntArray(_env, jsize(count))
-        return (result != nil) ? result : .None
-    }
-
-    public func GetIntArrayRegion(array: jintArray, startIndex: Int = 0, numElements: Int = -1) -> [Int] {
-        let _env = self._env
-        var count = numElements
-
-        if numElements < 0 {
-            count = GetArrayLength(array)
-        }
-
-        var result = [jint](count: count, repeatedValue: 0)
-        _env.memory.memory.GetIntArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
-        return result.map { Int($0) }
-    }
-
-    public func SetIntArrayRegion(array: jintArray, startIndex: Int = 0, from sourceElements: [Int]) {
-        let _env = self._env
-        var newElements = sourceElements.map { jint($0) } // make mutable copy
-        _env.memory.memory.SetIntArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
-    }
-
-    public func NewFloatArray(count: Int) -> jarray? {
-        let _env = self._env
-        let result = _env.memory.memory.NewFloatArray(_env, jsize(count))
-        return (result != nil) ? result : .None
-    }
-
-    public func GetFloatArrayRegion(array: jfloatArray, startIndex: Int = 0, numElements: Int = -1) -> [Float] {
-        let _env = self._env
-        var count = numElements
-
-        if numElements < 0 {
-            count = GetArrayLength(array)
-        }
-
-        var result = [jfloat](count: count, repeatedValue: 0)
-        _env.memory.memory.GetFloatArrayRegion(_env, array, jsize(startIndex), jsize(count), &result)
-        return result.map { Float($0) }
-    }
-
-    public func SetFloatArrayRegion(array: jfloatArray, startIndex: Int = 0, from sourceElements: [Float]) {
-        let _env = self._env
-        var newElements = sourceElements.map { jfloat($0) } // make mutable copy
-        _env.memory.memory.SetFloatArrayRegion(_env, array, jsize(startIndex), jsize(newElements.count), &newElements)
-    }
-}
-
-
-/**
- Allows a (Void) Java method to be called from Swift. Takes a global jobj (a class instance), a method name and its signature. The resulting callback can be called via javaCallback.call(param1, param2...), or javaCallback.apply([params]). Each param must be a jvalue.
- 
- Needs more error checking and handling. The basis is there, but from memory I had issues with either the optional or the throwing on Android.
-*/
-public struct JavaCallback {
-    private let jobj: jobject // must be a JNI Global Reference
-    private let methodID: jmethodID
-
-    // Eventually we should check how many parameters are required by the method signature
-    // And also which return type is expected (to allow calling non-Void methods)
-    // For now this implementation remains unsafe
-    //let expectedParameterCount: Int
-
-    /// Errors describing the various things that can go wrong when calling a Java method via JNI.
-    /// - __InvalidParameters__: One character per method parameter is required. For example, with a methodSignature of "(FF)V", you need to pass two floats as parameters.
-    /// - __InvalidMethod__: Couldn't get the requested method from the jobject provided (are you calling with the right jobject instance / calling on the correct class?)
-    /// - __IncorrectMethodSignature__: The JNI is separated into Java method calls to functions with various return types. So if you perform `callJavaMethod`, you need to accept the return value with the corresponding type. *XXX: currently only Void methods are implemented*.
-    
-    enum Error: ErrorType {
-        case JNINotReady
-        case InvalidParameters
-        case IncorrectMethodSignature
-        case InvalidClass
-        case InvalidMethod
-    }
-
-    /**
-    - Parameters:
-    - globalJobj: The class instance you want to perform the method on. Note this must be a jni GlobalRef, otherwise your callback will either crash or just silently not work.
-    - methodName: A `String` with the name of the Java method to call
-    - methodSignature: A `String` containing the method's exact signature. Although it is possible to call non-Void Java methods via the JNI, that is not yet implemented in the the current Swift binding. This means that, for now, `methodSignature` must end with `"V"` i.e., return `Void`
-    - **`"(F)V"`** would reference a method that accepts one Float and returns Void.
-    - **Z** boolean
-    - **B** byte
-    - **C** char
-    - **S** short
-    - **I** int
-    - **J** long
-    - **F** float
-    - **D** double
-    - **Lfully-qualified-class;** fully-qualified-class
-    - **[type** type[]
-    - **(arg-types)ret-type** method type
-    - e.g. **`"([I)V"`** would accept one array of Ints and return Void.
-    - parameters: Any number of jvalues (*must have the same number and type as the* `methodSignature` *you're trying to call*)
-    - Throws: `JavaCallback.Error`
-    */
-    public init (_ globalJobj: jobject, methodName: String, methodSignature: String) {
-        // At the moment we can only call Void methods, fail if user tries to return something else
-        guard let returnType = methodSignature.characters.last where returnType == "V"/*oid*/ else {
-            // LOG JavaMethodCallError.IncorrectMethodSignature
-            fatalError("JavaMethodCallError.IncorrectMethodSignature")
-        }
-
-        // With signature "(FF)V", parameters count should be 2, ignoring the two brackets and the V
-        // XXX: This test isn't robust, but it will prevent simple user errors
-        // Doesn't work with more complex object types, arrays etc. we should determine the signature based on parameters.
-
-        // TODO: Check methodSignature here and determine expectedParameterCount
-
-        guard
-            let javaClass = jni.GetObjectClass(globalJobj),
-            let methodID = jni.GetMethodID(javaClass, methodName: methodName, methodSignature: methodSignature)
-            else {
-                // XXX: We should throw here and keep throwing til it gets back to Java
-                fatalError("Failed to make JavaCallback")
-        }
-
-        self.jobj = globalJobj
-        self.methodID = methodID
-    }
-
-    public func apply(args: [jvalue]) {
-        jni.CallVoidMethodA(jobj, methodID: methodID, args: args)
-    }
-
-    /// Send variadic parameters to the func that takes an array
-    public func call(args: jvalue...) {
-        self.apply(args)
-    }
-}
-
diff --git a/module.map.in b/module.map.in
deleted file mode 100644
index f18ff87..0000000
--- a/module.map.in
+++ /dev/null
@@ -1,17 +0,0 @@
-//===--- module.map -------------------------------------------------------===//
-//
-// This source file is part of the Swift.org open source project
-//
-// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
-// Licensed under Apache License v2.0 with Runtime Library Exception
-//
-// See http://swift.org/LICENSE.txt for license information
-// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
-//
-//===----------------------------------------------------------------------===//
-
-module CJNI [system] {
-  header "@JNI_INCLUDE_PATH@/jni.h"
-  export *
-}
-