Skip to content

Commit 5e137cc

Browse files
committed
Updated demo
1 parent 017c0d8 commit 5e137cc

File tree

9 files changed

+431
-78
lines changed

9 files changed

+431
-78
lines changed
Lines changed: 8 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,9 @@
11
package com.pureswift.swiftandroid
22

33
import android.os.Bundle
4+
import android.util.Log
5+
import android.view.View
46
import androidx.activity.ComponentActivity
5-
import androidx.activity.compose.setContent
6-
import androidx.activity.enableEdgeToEdge
7-
import androidx.compose.foundation.layout.fillMaxSize
8-
import androidx.compose.foundation.layout.padding
9-
import androidx.compose.material3.Scaffold
10-
import androidx.compose.material3.Text
11-
import androidx.compose.runtime.Composable
12-
import androidx.compose.ui.Modifier
13-
import androidx.compose.ui.tooling.preview.Preview
14-
import com.pureswift.swiftandroid.ui.theme.SwiftAndroidTheme
157

168
class MainActivity : ComponentActivity() {
179

@@ -21,42 +13,14 @@ class MainActivity : ComponentActivity() {
2113

2214
override fun onCreate(savedInstanceState: Bundle?) {
2315
super.onCreate(savedInstanceState)
24-
val hello = MainActivityHello()
25-
enableEdgeToEdge()
26-
setContent {
27-
SwiftAndroidTheme {
28-
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
29-
Greeting(
30-
name = hello.sayHelloName(),
31-
modifier = Modifier.padding(innerPadding)
32-
)
33-
}
34-
}
35-
}
16+
onCreateSwift(savedInstanceState)
17+
//enableEdgeToEdge()
3618
}
37-
}
3819

39-
class MainActivityHello {
20+
external fun onCreateSwift(savedInstanceState: Bundle?)
4021

41-
init {
42-
NativeLibrary.shared()
22+
fun setRootView(view: View) {
23+
Log.d("MainActivity", "AndroidSwiftUI.MainActivity.setRootView(_:)")
24+
setContentView(view)
4325
}
44-
45-
external fun sayHelloName(): String
4626
}
47-
48-
@Composable
49-
fun Greeting(name: String, modifier: Modifier = Modifier) {
50-
Text(
51-
text = "Hello $name!",
52-
modifier = modifier
53-
)
54-
}
55-
56-
@Preview(showBackground = true)
57-
@Composable
58-
fun GreetingPreview() {
59-
SwiftAndroidTheme {
60-
Greeting(MainActivityHello().sayHelloName())
61-
}
62-
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.pureswift.swiftandroid
22

3-
class Runnable(): java.lang.Runnable {
3+
class Runnable(val block: SwiftObject): java.lang.Runnable {
44

55
external override fun run()
6-
}
6+
}

Demo/app/src/main/swift/Application.swift

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,25 @@ public class Application: AndroidApp.Application {
1818
public extension Application {
1919

2020
@JavaMethod
21-
public func onCreateSwift() {
22-
23-
print("\(#function)")
21+
func onCreateSwift() {
22+
log("\(self).\(#function)")
2423

2524
sayHello()
2625
}
2726

2827
@JavaMethod
29-
public func onTerminateSwift() {
30-
31-
print("\(#function)")
28+
func onTerminateSwift() {
29+
log("\(self).\(#function)")
3230
}
3331
}
3432

35-
internal extension Application {
33+
extension Application {
34+
35+
static var logTag: String { "Application" }
36+
37+
static let log = try! JavaClass<AndroidUtil.Log>()
3638

37-
func print(_ string: String) {
38-
let log = try! JavaClass<AndroidUtil.Log>()
39-
_ = log.v("Application", string)
39+
func log(_ string: String) {
40+
_ = Self.log.d(Self.logTag, string)
4041
}
4142
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//
2+
// TextView.swift
3+
// SwiftAndroidApp
4+
//
5+
// Created by Alsey Coleman Miller on 6/11/25.
6+
//
7+
8+
import AndroidKit
9+
10+
extension TextView {
11+
12+
var text: String {
13+
get {
14+
getText().toString()
15+
}
16+
set {
17+
setText(JavaString(newValue).as(CharSequence.self))
18+
}
19+
}
20+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
//
2+
// JavaRetainedValue.swift
3+
// AndroidSwiftUI
4+
//
5+
// Created by Alsey Coleman Miller on 6/9/25.
6+
//
7+
8+
import JavaKit
9+
import JavaRuntime
10+
11+
/// Java class that retains a Swift value for the duration of its lifetime.
12+
@JavaClass("com.pureswift.swiftandroid.SwiftObject")
13+
open class SwiftObject: JavaObject {
14+
15+
@JavaMethod
16+
@_nonoverride public convenience init(swiftObject: Int64, type: String, environment: JNIEnvironment? = nil)
17+
18+
@JavaMethod
19+
open func getSwiftObject() -> Int64
20+
21+
@JavaMethod
22+
open func getType() -> String
23+
}
24+
25+
@JavaImplementation("com.pureswift.swiftandroid.SwiftObject")
26+
extension SwiftObject {
27+
28+
@JavaMethod
29+
public func toStringSwift() -> String {
30+
"\(valueObject().value)"
31+
}
32+
33+
@JavaMethod
34+
public func finalizeSwift() {
35+
// release owned swift value
36+
release()
37+
}
38+
}
39+
40+
extension SwiftObject {
41+
42+
convenience init<T>(_ value: T, environment: JNIEnvironment? = nil) {
43+
let box = JavaRetainedValue(value)
44+
let type = box.type
45+
self.init(swiftObject: box.id, type: type, environment: environment)
46+
// retain value
47+
retain(box)
48+
}
49+
50+
func valueObject() -> JavaRetainedValue {
51+
let id = getSwiftObject()
52+
guard let object = Self.retained[id] else {
53+
fatalError()
54+
}
55+
return object
56+
}
57+
}
58+
59+
private extension SwiftObject {
60+
61+
static var retained = [JavaRetainedValue.ID: JavaRetainedValue]()
62+
63+
func retain(_ value: JavaRetainedValue) {
64+
Self.retained[value.id] = value
65+
}
66+
67+
func release() {
68+
let id = getSwiftObject()
69+
Self.retained[id] = nil
70+
}
71+
}
72+
73+
/// Swift Object retained until released by Java object.
74+
final class JavaRetainedValue: Identifiable {
75+
76+
var value: Any
77+
78+
var type: String {
79+
String(describing: Swift.type(of: value))
80+
}
81+
82+
var id: Int64 {
83+
Int64(ObjectIdentifier(self).hashValue)
84+
}
85+
86+
init<T>(_ value: T) {
87+
self.value = value
88+
}
89+
}
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
//
2+
// ListViewAdapter.swift
3+
// AndroidSwiftUI
4+
//
5+
// Created by Alsey Coleman Miller on 6/9/25.
6+
//
7+
8+
import Foundation
9+
import AndroidKit
10+
11+
@JavaClass("com.pureswift.swiftandroid.ListViewAdapter", extends: ListAdapter.self)
12+
open class ListViewAdapter: JavaObject {
13+
14+
@JavaMethod
15+
@_nonoverride public convenience init(swiftObject: SwiftObject!, environment: JNIEnvironment? = nil)
16+
17+
@JavaMethod
18+
func getContext() -> SwiftObject!
19+
}
20+
21+
@JavaImplementation("com.pureswift.swiftandroid.ListViewAdapter")
22+
extension ListViewAdapter {
23+
24+
@JavaMethod
25+
func hasStableIds() -> Bool {
26+
log("\(self).\(#function)")
27+
return true
28+
}
29+
30+
@JavaMethod
31+
func isEmpty() -> Bool {
32+
log("\(self).\(#function)")
33+
return context.items.isEmpty
34+
}
35+
36+
@JavaMethod
37+
func getCount() -> Int32 {
38+
log("\(self).\(#function)")
39+
return Int32(context.items.count)
40+
}
41+
42+
@JavaMethod
43+
func getItem(position: Int32) -> JavaObject? {
44+
log("\(self).\(#function) \(position)")
45+
return JavaString(context.items[Int(position)])
46+
}
47+
48+
@JavaMethod
49+
func getItemId(position: Int32) -> Int64 {
50+
log("\(self).\(#function)")
51+
return Int64(position)
52+
}
53+
54+
@JavaMethod
55+
func getItemViewType(position: Int32) -> Int32 {
56+
log("\(self).\(#function)")
57+
return 0
58+
}
59+
60+
@JavaMethod
61+
func getViewTypeCount() -> Int32 {
62+
log("\(self).\(#function)")
63+
return 1
64+
}
65+
66+
@JavaMethod
67+
func getView(position: Int32, convertView: AndroidView.View?, parent: ViewGroup?) -> AndroidView.View? {
68+
log("\(self).\(#function) \(position)")
69+
guard let parent else {
70+
assertionFailure()
71+
return nil
72+
}
73+
let view = TextView(parent.getContext())
74+
let item = context.items[Int(position)]
75+
view.text = item
76+
return view
77+
}
78+
79+
@JavaMethod
80+
func areAllItemsEnabled() -> Bool {
81+
log("\(self).\(#function)")
82+
return true
83+
}
84+
85+
@JavaMethod
86+
func isEnabled(position: Int32) -> Bool {
87+
log("\(self).\(#function) \(position)")
88+
return true
89+
}
90+
91+
@JavaMethod
92+
func registerDataSetObserver(observer: JavaObject?) {
93+
log("\(self).\(#function)")
94+
95+
}
96+
97+
@JavaMethod
98+
func unregisterDataSetObserver(observer: JavaObject?) {
99+
log("\(self).\(#function)")
100+
101+
}
102+
}
103+
104+
public extension ListViewAdapter {
105+
106+
struct Context {
107+
108+
let items: [String]
109+
}
110+
111+
var context: Context {
112+
get {
113+
getContext().valueObject().value as! Context
114+
}
115+
set {
116+
getContext().valueObject().value = newValue
117+
}
118+
}
119+
120+
convenience init(_ context: Context, environment: JNIEnvironment? = nil) {
121+
self.init(swiftObject: SwiftObject(context), environment: environment)
122+
}
123+
}
124+
125+
extension ListViewAdapter {
126+
127+
static var logTag: String { "ListViewAdapter" }
128+
129+
static let log = try! JavaClass<AndroidUtil.Log>()
130+
131+
func log(_ string: String) {
132+
_ = Self.log.d(Self.logTag, string)
133+
}
134+
135+
func logError(_ string: String) {
136+
_ = Self.log.e(Self.logTag, string)
137+
}
138+
}

0 commit comments

Comments
 (0)