forked from mozilla/uniffi-rs
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add callback interfaces for Swift (mozilla#1066) r=travis
* Added swift callback support * Temporary fix for running callbacks in another thread * Generate all the required swift code * Make run_uniffi_bindgen_test build * [wip] add tests for callbacks (mozilla#1) Co-authored-by: Paul Griffin <[email protected]> * Fix nits * Fit new Swift callbacks into the new Unit of Code structure Re-work to fit in to the new structure Change to FfiConverter language cargo fmt Add docs to include swift * Backfill Kotlin callbacks to closer match the Swift implementation Kotlin relies on Handle instead of Long (or ULong) Align kotlin implementation with swift * Fixup markdown weirdness * Address self-review * Address reviewer comments Co-authored-by: Paul Griffin <[email protected]> Co-authored-by: Paul Griffin <[email protected]>
- Loading branch information
Showing
18 changed files
with
422 additions
and
74 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
#if canImport(callbacks) | ||
import callbacks | ||
#endif | ||
|
||
// Simple example just to see it work. | ||
// Pass in a string, get a string back. | ||
// Pass in nothing, get unit back. | ||
class OnCallAnsweredImpl : OnCallAnswered { | ||
var yesCount: Int = 0 | ||
var busyCount: Int = 0 | ||
var stringReceived = "" | ||
|
||
func hello() -> String { | ||
yesCount += 1 | ||
return "Hi hi \(yesCount)" | ||
} | ||
|
||
func busy() { | ||
busyCount += 1 | ||
} | ||
|
||
func textReceived(text: String) { | ||
stringReceived = text | ||
} | ||
} | ||
|
||
let cbObject = OnCallAnsweredImpl() | ||
let telephone = Telephone() | ||
|
||
telephone.call(domestic: true, callResponder: cbObject) | ||
assert(cbObject.busyCount == 0, "yesCount=\(cbObject.busyCount) (should be 0)") | ||
assert(cbObject.yesCount == 1, "yesCount=\(cbObject.yesCount) (should be 1)") | ||
|
||
telephone.call(domestic: true, callResponder: cbObject) | ||
assert(cbObject.busyCount == 0, "yesCount=\(cbObject.busyCount) (should be 0)") | ||
assert(cbObject.yesCount == 2, "yesCount=\(cbObject.yesCount) (should be 2)") | ||
|
||
telephone.call(domestic: false, callResponder: cbObject) | ||
assert(cbObject.busyCount == 1, "yesCount=\(cbObject.busyCount) (should be 1)") | ||
assert(cbObject.yesCount == 2, "yesCount=\(cbObject.yesCount) (should be 2)") | ||
|
||
let cbObject2 = OnCallAnsweredImpl() | ||
telephone.call(domestic: true, callResponder: cbObject2) | ||
assert(cbObject2.busyCount == 0, "yesCount=\(cbObject2.busyCount) (should be 0)") | ||
assert(cbObject2.yesCount == 1, "yesCount=\(cbObject2.yesCount) (should be 1)") | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,6 @@ | ||
[bindings.kotlin] | ||
package_name = "uniffi.callbacks" | ||
cdylib_name = "callbacks" | ||
|
||
[bindings.swift] | ||
cdylib_name = "callbacks" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
#if canImport(callbacks) | ||
import callbacks | ||
#endif | ||
|
||
// A bit more systematic in testing, but this time in English. | ||
// | ||
// 1. Pass in the callback as arguments. | ||
// Make the callback methods use multiple aruments, with a variety of types, and | ||
// with a variety of return types. | ||
let rustGetters = RustGetters() | ||
class SwiftGetters: ForeignGetters { | ||
func getBool(v: Bool, arg2: Bool) -> Bool { v != arg2 } | ||
func getString(v: String, arg2: Bool) -> String { arg2 ? "1234567890123" : v } | ||
func getOption(v: String?, arg2: Bool) -> String? { arg2 ? v?.uppercased() : v } | ||
func getList(v: [Int32], arg2: Bool) -> [Int32] { arg2 ? v : [] } | ||
} | ||
|
||
func test() { | ||
let callback = SwiftGetters() | ||
[true, false].forEach { v in | ||
let flag = true | ||
let expected = callback.getBool(v: v, arg2: flag) | ||
let observed = rustGetters.getBool(callback: callback, v: v, arg2: flag) | ||
assert(expected == observed, "roundtripping through callback: \(String(describing: expected)) != \(String(describing: observed))") | ||
} | ||
|
||
[[Int32(1), Int32(2)], [Int32(0), Int32(1)]].forEach { v in | ||
let flag = true | ||
let expected = callback.getList(v: v, arg2: flag) | ||
let observed = rustGetters.getList(callback: callback, v: v, arg2: flag) | ||
assert(expected == observed, "roundtripping through callback: \(String(describing: expected)) != \(String(describing: observed))") | ||
} | ||
|
||
["Hello", "world"].forEach { v in | ||
let flag = true | ||
let expected = callback.getString(v: v, arg2: flag) | ||
let observed = rustGetters.getString(callback: callback, v: v, arg2: flag) | ||
assert(expected == observed, "roundtripping through callback: \(String(describing: expected)) != \(String(describing: observed))") | ||
} | ||
|
||
["Some", nil].forEach { v in | ||
let flag = false | ||
let expected = callback.getOption(v: v, arg2: flag) | ||
let observed = rustGetters.getOption(callback: callback, v: v, arg2: flag) | ||
assert(expected == observed, "roundtripping through callback: \(String(describing: expected)) != \(String(describing: observed))") | ||
} | ||
|
||
assert(rustGetters.getStringOptionalCallback(callback: callback, v: "TestString", arg2: false) == "TestString") | ||
assert(rustGetters.getStringOptionalCallback(callback: nil, v: "TestString", arg2: false) == nil) | ||
|
||
// rustGetters.destroy() | ||
|
||
// 2. Pass the callback in as a constructor argument, to be stored on the Object struct. | ||
// This is crucial if we want to configure a system at startup, | ||
// then use it without passing callbacks all the time. | ||
|
||
class StoredSwiftStringifier: StoredForeignStringifier { | ||
func fromSimpleType(value: Int32) -> String { "swift: \(value)" } | ||
// We don't test this, but we're checking that the arg type is included in the minimal list of types used | ||
// in the UDL. | ||
// If this doesn't compile, then look at TypeResolver. | ||
func fromComplexType(values: [Double?]?) -> String { "swift: \(String(describing: values))" } | ||
} | ||
|
||
let swiftStringifier = StoredSwiftStringifier() | ||
let rustStringifier = RustStringifier(callback: swiftStringifier) | ||
([1, 2] as [Int32]).forEach { v in | ||
let expected = swiftStringifier.fromSimpleType(value: v) | ||
let observed = rustStringifier.fromSimpleType(value: v) | ||
assert(expected == observed, "callback is sent on construction: \(expected) != \(observed)") | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,8 @@ | ||
uniffi_macros::build_foreign_language_testcases!( | ||
"src/callbacks.udl", | ||
["tests/bindings/test_callbacks.kts"] | ||
[ | ||
"tests/bindings/test_callbacks.kts", | ||
"tests/bindings/test_callbacks.swift", | ||
//"tests/bindings/test_callbacks.py", // see https://github.com/mozilla/uniffi-rs/pull/1068 | ||
] | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.