Can I read json file from resources in shared kotlin multiparform code? - kotlin-multiplatform

I have a shared module in kotlin multiplatform project which contains shared code for all platforms.
I wish to put there a json file (string-resources.json) for examle, I've put it to
shared\src\commonMain\resources\string-resources.json
Now I need to read contents on this file, so it would work on every platform (android, ios, etc...). So something like this:
class JsonStringResource {
companion object {
fun get(key: String) {
val classLoader = JsonStringResource::class.java.classLoader
val resource = classLoader.getResource("strings-test.json").getText()
println(resource)
}
}
}
Is it possible? The code above doesn't work since getResource always returns null (even if I tried putting it as expected-actual class and putting above implementation to Android only)

Related

How to read an array of objectes generated in a Swift framework library, in a KMM (kotlin native) shared module

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:

How to read file from an imported library

I have two packages: webserver and utils which provides assets to webserver.
The webserver needs access to static files inside utils. So I have this setup:
utils/
lib/
static.html
How can I access the static.html file in one of my dart scripts in webserver?
EDIT: What I tried so far, is to use mirrors to get the path of the library, and read it from there. The problem with that approach is, that if utils is included with package:, the url returned by currentMirrorSystem().findLibrary(#utils).uri is a package uri, that can't be transformed to an actual file entity.
Use the Resource class, a new class in Dart SDK 1.12.
Usage example:
var resource = new Resource('package:myapp/myfile.txt');
var contents = await resource.loadAsString();
print(contents);
This works on the VM, as of 1.12.
However, this doesn't directly address your need to get to the actual File entity, from a package: URI. Given the Resource class today, you'd have to route the bytes from loadAsString() into the HTTP server's Response object.
I tend to use Platform.script or mirrors to find the main package top folder (i.e. where pubspec.yaml is present) and find imported packages exported assets. I agree this is not a perfect solution but it works
import 'dart:io';
import 'package:path/path.dart';
String getProjectTopPath(String resolverPath) {
String dirPath = normalize(absolute(resolverPath));
while (true) {
// Find the project root path
if (new File(join(dirPath, "pubspec.yaml")).existsSync()) {
return dirPath;
}
String newDirPath = dirname(dirPath);
if (newDirPath == dirPath) {
throw new Exception("No project found for path '$resolverPath");
}
dirPath = newDirPath;
}
}
String getPackagesPath(String resolverPath) {
return join(getProjectTopPath(resolverPath), 'packages');
}
class _TestUtils {}
main(List<String> arguments) {
// User Platform.script - does not work in unit test
String currentScriptPath = Platform.script.toFilePath();
String packagesPath = getPackagesPath(currentScriptPath);
// Get your file using the package name and its relative path from the lib folder
String filePath = join(packagesPath, "utils", "static.html");
print(filePath);
// use mirror to find this file path
String thisFilePath = (reflectClass(_TestUtils).owner as LibraryMirror).uri.toString();
packagesPath = getPackagesPath(thisFilePath);
filePath = join(packagesPath, "utils", "static.html");
print(filePath);
}
To note that since recently Platform.script is not reliable in unit test when using the new test package so you might use the mirror tricks that I propose above and explained here: https://github.com/dart-lang/test/issues/110

How to use Namespaces in Swift?

The documentation only mentions nested types, but it's not clear if they can be used as namespaces. I haven't found any explicit mentioning of namespaces.
I would describe Swift's namespacing as aspirational; it's been given a lot of advertising that doesn't correspond to any meaningful reality on the ground.
For example, the WWDC videos state that if a framework you're importing has a class MyClass and your code has a class MyClass, those names do not conflict because "name mangling" gives them different internal names. In reality, however, they do conflict, in the sense that your own code's MyClass wins, and you can't specify "No no, I mean the MyClass in the framework" — saying TheFramework.MyClass doesn't work (the compiler knows what you mean, but it says it can't find such a class in the framework).
My experience is that Swift therefore is not namespaced in the slightest. In turning one of my apps from Objective-C to Swift, I created an embedded framework because it was so easy and cool to do. Importing the framework, however, imports all the Swift stuff in the framework - so presto, once again there is just one namespace and it's global. And there are no Swift headers so you can't hide any names.
EDIT: In seed 3, this feature is now starting to come online, in the following sense: if your main code contains MyClass and your framework MyFramework contains MyClass, the former overshadows the latter by default, but you can reach the one in the framework by using the syntax MyFramework.MyClass. Thus we do in fact have the rudiments of a distinct namespace!
EDIT 2: In seed 4, we now have access controls! Plus, in one of my apps I have an embedded framework and sure enough, everything was hidden by default and I had to expose all the bits of the public API explicitly. This is a big improvement.
Answered by SevenTenEleven in the Apple dev forum:
Namespaces are not per-file; they're per-target (based on the
"Product Module Name" build setting). So you'd end up with something
like this:
import FrameworkA
import FrameworkB
FrameworkA.foo()
All Swift declarations are considered to be part of
some module, so even when you say "NSLog" (yes, it still exists)
you're getting what Swift thinks of as "Foundation.NSLog".
Also Chris Lattner tweeted about namespacing.
Namespacing is implicit in Swift, all classes (etc) are implicitly
scoped by the module (Xcode target) they are in. no class prefixes
needed
Seems to be very different what I have been thinking.
While doing some experimentation with this I ended up creating these "namespaced" classes in their own files by extending the root "package". Not sure if this is against best practices or if it has any implications I'm mot aware of(?)
AppDelegate.swift
var n1 = PackageOne.Class(name: "Package 1 class")
var n2 = PackageTwo.Class(name: "Package 2 class")
println("Name 1: \(n1.name)")
println("Name 2: \(n2.name)")
PackageOne.swift
import Foundation
struct PackageOne {
}
PackageTwo.swift
import Foundation
struct PackageTwo {
}
PackageOneClass.swift
extension PackageOne {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
PackageTwoClass.swift
extension PackageTwo {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Edit:
Just found out that creating "subpackages" in above code wont work if using separate files. Maybe someone can hint on why that would be the case?
Adding following files to the above:
PackageOneSubPackage.swift
import Foundation
extension PackageOne {
struct SubPackage {
}
}
PackageOneSubPackageClass.swift
extension PackageOne.SubPackage {
class Class {
var name: String
init(name:String) {
self.name = name
}
}
}
Its throwing a compiler error:
'SubPackage' is not a member type of 'PackageOne'
If I move the code from PackageOneSubPackageClass.swift to PackageOneSubPackage.swift it works. Anyone?
Edit 2:
Fiddling around with this still and found out (in Xcode 6.1 beta 2) that by defining the packages in one file they can be extended in separate files:
public struct Package {
public struct SubPackage {
public struct SubPackageOne {
}
public struct SubPackageTwo {
}
}
}
Here are my files in a gist:
https://gist.github.com/mikajauhonen/d4b3e517122ad6a132b8
I believe this is achieved using:
struct Foo
{
class Bar
{
}
}
Then it can be accessed using:
var dds = Foo.Bar();
Namespaces are useful when you need to define class with the same name as class in existing framework.
Suppose your app has MyApp name, and you need to declare your custom UICollectionViewController.
You don't need to prefix and subclass like this:
class MAUICollectionViewController: UICollectionViewController {}
Do it like this:
class UICollectionViewController {} //no error "invalid redeclaration o..."
Why?. Because what you've declared is declared in current module, which is your current target. And UICollectionViewController from UIKit is declared in UIKit module.
How to use it within current module?
var customController = UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
How to distinguish them from another module?
var customController = MyApp.UICollectionViewController() //your custom class
var uikitController = UIKit.UICollectionViewController() //class from UIKit
Swift uses modules much like in python (see here and here) and as #Kevin Sylvestre suggested you can also use the nested types as namespaces.
And to extend the answer from #Daniel A. White, in WWDC they were talking about the modules in swift.
Also here is explained:
Inferred types make code cleaner and less prone to mistakes, while
modules eliminate headers and provide namespaces.
You can use extension to use the mentioned structs approach for namespacing without having to indent all of your code towards the right. I've been toying with this a bit and I'm not sure I'd go as far as creating Controllers and Views namespaces like in the example below, but it does illustrate how far it can go:
Profiles.swift:
// Define the namespaces
struct Profiles {
struct Views {}
struct ViewControllers {}
}
Profiles/ViewControllers/Edit.swift
// Define your new class within its namespace
extension Profiles.ViewControllers {
class Edit: UIViewController {}
}
// Extend your new class to avoid the extra whitespace on the left
extension Profiles.ViewControllers.Edit {
override func viewDidLoad() {
// Do some stuff
}
}
Profiles/Views/Edit.swift
extension Profiles.Views {
class Edit: UIView {}
}
extension Profiles.Views.Edit {
override func drawRect(rect: CGRect) {
// Do some stuff
}
}
I haven't used this in an app since I haven't needed this level of separation yet but I think it's an interesting idea. This removes the need for even class suffixes such as the ubiquitous *ViewController suffix which is annoyingly long.
However, it doesn't shorten anything when it's referenced such as in method parameters like this:
class MyClass {
func doSomethingWith(viewController: Profiles.ViewControllers.Edit) {
// secret sauce
}
}
Even though it is possible to implement namespaces using Framework and Libraries but the best solution is to use local packages using Swift Package Manager. Besides having access modifiers, this approach has some other benefits. As in Swift Package Manager, the files are managed based on the directory system, not their target member ship, you won't have to struggle with merge conflicts that arise frequently in teamworks. Furthermore, there is no need to set file memberships.
To check how to use local Swift packages refer to the following link:
Organizing Your Code with Local Packages
In case anyone was curious, as of June 10th 2014, this is a known bug in Swift:
From SevenTenEleven
"Known bug, sorry! rdar://problem/17127940 Qualifying Swift types by their module name doesn't work."

Conditional imports / code for Dart packages

Is there any way to conditionally import libraries / code based on environment flags or target platforms in Dart? I'm trying to switch out between dart:io's ZLibDecoder / ZLibEncoder classes and zlib.js based on the target platform.
There is an article that describes how to create a unified interface, but I'm unable to visualize that technique not creating duplicate code and redundant tests to test that duplicate code. game_loop employs this technique, but uses separate classes (GameLoopHtml and GameLoopIsolate) that don't seem to share anything.
My code looks a bit like this:
class Parser {
Layer parse(String data) {
List<int> rawBytes = /* ... */;
/* stuff you don't care about */
return new Layer(_inflateBytes(rawBytes));
}
String _inflateBytes(List<int> bytes) {
// Uses ZLibEncoder on dartvm, zlib.js in browser
}
}
I'd like to avoid duplicating code by having two separate classes -- ParserHtml and ParserServer -- that implement everything identically except for _inflateBytes.
EDIT: concrete example here: https://github.com/radicaled/citadel/blob/master/lib/tilemap/parser.dart. It's a TMX (Tile Map XML) parser.
You could use mirrors (reflection) to solve this problem. The pub package path is using reflection to access dart:io on the standalone VM or dart:html in the browser.
The source is located here. The good thing is, that they use #MirrorsUsed, so only the required classes are included for the mirrors api. In my opinion the code is documented very good, it should be easy to adopt the solution for your code.
Start at the getters _io and _html (stating at line 72), they show that you can load a library without that they are available on your type of the VM. Loading just returns false if the library it isn't available.
/// If we're running in the server-side Dart VM, this will return a
/// [LibraryMirror] that gives access to the `dart:io` library.
///
/// If `dart:io` is not available, this returns null.
LibraryMirror get _io => currentMirrorSystem().libraries[Uri.parse('dart:io')];
// TODO(nweiz): when issue 6490 or 6943 are fixed, make this work under dart2js.
/// If we're running in Dartium, this will return a [LibraryMirror] that gives
/// access to the `dart:html` library.
///
/// If `dart:html` is not available, this returns null.
LibraryMirror get _html =>
currentMirrorSystem().libraries[Uri.parse('dart:html')];
Later you can use mirrors to invoke methods or getters. See the getter current (starting at line 86) for an example implementation.
/// Gets the path to the current working directory.
///
/// In the browser, this means the current URL. When using dart2js, this
/// currently returns `.` due to technical constraints. In the future, it will
/// return the current URL.
String get current {
if (_io != null) {
return _io.classes[#Directory].getField(#current).reflectee.path;
} else if (_html != null) {
return _html.getField(#window).reflectee.location.href;
} else {
return '.';
}
}
As you see in the comments, this only works in the Dart VM at the moment. After issue 6490 is solved, it should work in Dart2Js, too. This may means that this solution isn't applicable for you at the moment, but would be a solution later.
The issue 6943 could also be helpful, but describes another solution that is not implemented yet.
Conditional imports are possible based on the presence of dart:html or dart:io, see for example the import statements of resource_loader.dart in package:resource.
I'm not yet sure how to do an import conditional on being on the Flutter platform.

Dart 2 libraries in one lib folder causing type 'X' is not a subtype of type 'X'

I have a following problem:
In my application, I have web and lib folders.
Lib folder is supposed to contain utility libraries.
Example:
lib/my_lib.dart
library my_lib;
part 'src/person.dart';
lib/my_lib1.dart
library my_lib1;
import 'my_lib.dart';
part 'src/other.dart';
In my_lib1, I want to use classes defined in my_lib
the classes are as follows:
lib/src/person.dart
part of my_lib;
class Person {
}
lib/src/other.dart
part of my_lib1;
class Other {
Person p;
Other(this.p) {
print(p);
}
}
Now, in web/testpackage.dart
import 'package:TestPackage/my_lib.dart';
import 'package:TestPackage/my_lib1.dart';
void main() {
Person p = new Person();
Other o = new Other(p);
}
Fails with:
Exception: type 'Person' is not a subtype of type 'Person' of 'p'.
Other.Other (package:testpackage/src/other.dart:7:14)
How should I structure my project to prevent that?
My libraries are local to the app, and I don't really want to develop them separately for my toy project.
Problem in that the your library my_lib is a publiclibrary and anyone (and you, of course) can use it elsewhere outside of lib directory.
In this case it must be imported (becuase it's a public library) always as the package library.
To solve this this problem you must change your source code.
From this lib/my_lib1.dart
library my_lib1;
import 'my_lib.dart';
part 'src/other.dart';
To this lib/my_lib1.dart
library my_lib1;
import 'package:TestPackage/my_lib.dart';
part 'src/other.dart';

Resources