Can I pass array or dictionary object from dart to native extension by Dart_Handle? If it is possible, how to do that?
I don't know about the dictionary object but you can do it with array/list. You can see here that the Dart_CObject struct can be of the type Dart_CObject_kArray which you can get with call as_array on the Dart_Object:
struct {
intptr_t length;
struct _Dart_CObject** values;
} as_array;
https://github.com/dart-lang/sdk/blob/master/runtime/include/dart_native_api.h#L68
You can then access the values inside the list as normal array (e.g. get first element where "message" are of the type Dart_CObject*):
Dart_CObject* param0 = message->value.as_array.values[0];
I have made a project where I uses lists as argument to native code. The relevant part for you can be found here:
https://github.com/julemand101/lirc_client/blob/master/lib/src/lirc_extension.cc#L126
Related
I am attempting to loop through a set of posts and getting their media by looping through the counts of images.
This doesn't work - images being set to 0
for images in JSON(response.value!)["post"]["media"] {
print(JSON(response.value!)["post"]["media"][images]["image_url"])
}
But this does
for images in JSON(response.value!)["post"]["media"] {
print(JSON(response.value!)["post"]["media"][0]["image_url"])
}
The error I get is
Cannot convert value of type '(String, JSON)' to expected argument type '[JSONSubscriptType]'
It appears that usinga variable from the loop won't work, but a direct Int does. I've also tried wrapping images as an Int
The working code reveals that the object for key media is an array of dictionaries.
So in the loop images (actually image) represents one item of the array media in each iteration.
Assuming JSON is related to SwiftyJSON – which has become obsolete in favor of Codable by the way – you have to write
for image in JSON(response.value!)["post"]["media"].arrayValue {
print(image["image_url"])
}
I'm working on a KMM app. The shared module has a helper class, that relies on different native libraries for the android part and for the iOS part. This is implemented with the already know "expected/actual" pattern.
As said, the iOS actual class makes use of an iOS framework, that performs some calculations, and returns an array of objects. The ios framework that creates the list of objects works correctly (it was tested with unit tests). A simplified example follows below.
This is the class of the objects that are inside of the array:
public class ChildNodeIos: NSObject{
public let content:String
public let isTextNode:Bool
public init(content:String,isTextNode:Bool=false){
self.content=content
self.isTextNode=isTextNode
}
}
The helper class on the iOS side that returns the list of objects would be something like that:
#objc public class IOSCoolHelper: NSObject {
#objc public func getChildNodes(message: String) -> [ChildNodeIos] {
//build the array of child nodes here and return them
}
}
In the kotlin shared module, inside the iOS expected class, the function is called like the following:
#Serializable
data class ChildNodeKN(val content :String,val isTextNode :Boolean=false)
import com.mydomain.iosframeworks.IosCoolHelper
actual class CoolHelper actual constructor(private val someStuff: String) : ICoolHelper {
actual override fun getChildNodes(message: String): List<ChildNodeKN> {
val iosHelper= IOSCoolHelper()
val swiftarray:List<ChildNodeIos> = iosHelper.getChildNodes(message)
//I was expecting to do something like that but it does not work (the content of "array is always empty"):
val kotlinList:List<ChildNodeKN> = swiftarray as List<ChildNodeIos>
return kotlinList
}
}
}
Or maybe if the list of swift objects can not be direct be casted to the equivalent kotlin object list, I was expecting to be able to iterate over the swift list and convert it to the kotlin list, something like that:
val kotlinList=mutableListOf<ChildNodeKN>()
swiftArray.foreach{
kotlinList.add(ChildNodeKN(it.content,it.isTextNode))
}
But again, the content of the swift Array is empty. Doing a lot of tests (I can no reproduce them now), I managed to access something inside the array, but it was not an object of type ChildNodeIos, nor something I could read on the kotlin side.
Well, the question is, how to receive on the kotlin side, a list with more or less complex objects inside, that was generated on the iOS side?
I have to say, that this swift helper class has many other functions that return primitive values (strings, booleans, or int), and that is working very well.
I suppose a workaround would be instead an array with objects, to return an array with primitive types and two dimensions from the Swift side, but I would like to work with an array of objects if it is possible.
Thank you for your help
I managed to find the solution by myself. The problem was the declaration of the Swift class of the object contained in the list. I forgot the #objc declaration for the properties of the class, because if that I was not able to read the objects inside the returned array.
public class ChildNodeIos: NSObject{
#objc public let content:String
#objc public let isTextNode:Bool
public init(content:String,isTextNode:Bool=false){
self.content=content
self.isTextNode=isTextNode
}
}
And then, on the Kotlin side, I did not achieve to cast it directly to a list, but with a foreach loop it is very easy to write the iOS objects in Kotlin objects:
As you can see in https://api.dart.dev/stable/2.7.1/dart-convert/jsonDecode.html, it has no type and no documentation. I don't know which methods I can invoke on the result neither I don't know which type to but on a parameter that should be a json object.
Why is Dart like this? And what are the advantages?
It does have documentation, and you are linking to it.
If you want it to have more documentation, then that is reasonable. The returned value is admittedly not documented very well.
The function jsonDecode is a shorthand for json.decode, which again forwards to JsonDecoder.convert.
It returns a "JSON value" object which depends on the JSON source that it decodes.
A "JSON value" can be any of:
* null
* an int
* a double
* a String
* a bool (true or false)
* a List<dynamic> containing zero or more JSON values.
* a Map<String, dynamic> mapping keys to JSON values.
Those are also the same values that are accepted by the JsonEncoder which converts object structures to JSON strings.
Since these types have no common superclass other than Object, the function cannot have a return type which is more specific than dynamic or Object.
The chosen return type is dynamic because the dynamic type allows the receiver to optimistically call any member on the value. They might know that the value will always be a map, so they can just do jsonParse(jsonSource)["key"] to look up a value. Obviously, if the source was not a JSON object, that call will fail.
If you don't know which type the result is, you have to check:
var data = jsonDecode(jsonSource);
if (data is Map<String, dynamic>) {
something something data["key"] something
} else if (data is List<dynamic>) {
something something list[2] something
} else ... etc ...
A valid JSON file is actually a valid Dart expression too. The value returned by jsonDecode is similar to the value you would get if you wrote the JSON code directly as Dart code (in Dart 1 it was exactly the same, in Dart 2, the Dart code might infer a more precise type for maps and lists).
I'm new to Dart and was wondering over how the .cast() method works with dynamic types and lists.
This is a working example from the Flutter documentation on how to parse JSON manually in Dart:
List<Photo> parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
where responseBody is some JSON array previously fetched from a HTTP endpoint.
I don't understand why the result of json.decode(responseBody) is cast to Map<String, dynamic> when logically it should be List<Map<String, dynamic>>. I've debugged the code and in fact the variable parsed is a list subtype.
What am I getting wrong here?
Thanks in advance.
It looks like it is correct. cast is a method of Iterable. The type in angle brackets is the type of each element in the iterable.
https://api.dart.dev/stable/2.7.1/dart-core/Iterable/cast.html
according to this page it is possible to add an entire dictionary to another
http://code.tutsplus.com/tutorials/an-introduction-to-swift-part-1--cms-21389
but running the code gave me compilation error
var dictionary = ["cat": 2,"dog":4,"snake":8]; // mutable dictionary
dictionary["lion"] = 7; // add element to dictionary
dictionary += ["bear":1,"mouse":6]; // add dictionary to dictionary
error :
[string: Int] is not identical to UInt8
is there a right way to do this functionality in swift ?
of i should add them 1 by 1 ?
The page you are referring to is wrong, += is not a valid operator for a dictionary, although it is for arrays. If you'd like to see all the defined += operators, you can write import Swift at the top of your playground and command+click on Swift, then search for +=. This will take you to the file where all of the major Swift types and functions are defined.
The page you linked to also includes some other erroneous information on quick glance in the array section where it says you can do this: array += "four". So, don't trust this page too much. I believe you used to be able to append elements like this to an array in earlier versions of Swift, but it was changed.
The good news is that with Swift you can define your own custom operators! The following is quick implementation that should do what you want.
func +=<U,T>(inout lhs: [U:T], rhs: [U:T]) {
for (key, value) in rhs {
lhs[key] = value
}
}
Almost invariably when swift complains something is not like UInt8, there's a casting error in your code that may not be obvious, especially in a complex expression.
The problem in this case is that the + and += operators are not defined for that data type. A very nifty way to join arrays is described here:
How do you add a Dictionary of items into another Dictionary