Adding native swift code to NativeScript application - ios

I'm trying to add native swift code to my NativeScript app. According to these instructions in the documentation I can just add a swift source file to App_Resources/iOS/src/ and then use any publicly exposed classes directly in my TypeScript code.
Unfortunately this just doesn't work. I'll just get Cannot find name 'TestClass' and that's it.
Steps to reproduce:
Get a fresh NS project with ios tns create my-app-name --template tns-template-blank-ts
Update: I actually created the App with vue init nativescript-vue/vue-cli-template testapp. That seems to have caused the problems.
Add a TestClass.swift to App_Resources/iOS/src/
import Foundation
public class TestClass: NSObject {
#objc public func echo(param: String) -> String {
return param
}
}
Instantiate it in any TypeScript source file let instance = new TestClass()
Do tns debug ios
Compilation will fail with Cannot find name 'TestClass'
I have also tried generating TypeScript typings with TNS_TYPESCRIPT_DECLARATIONS_PATH="$(pwd)/typings" tns build ios or or just delcaring it as any with declare let KeyCommander: any; to eliminate the possibility that this is a TS related problem. The first approach doesn't generate any typings for my custom class so the TypeScript code will still not compile. The second approach let's the TS code compile but crashes on execution with JS ERROR ReferenceError: Can't find variable: TestClass.
I have also verified that the swift file is indeed getting compiled by inserting a syntax error which will crash the build process.
My NativeScript version is 6.4.0.
What am I missing?
Update: I just realized I actually created the App with vue init nativescript-vue/vue-cli-template testapp. I verified that as mentioned Tyler Blake's answer in an app created with the tns cli the described process actually works. In an app I just freshly created with vue init it doesn't, the objc!nsswiftsupport.d.ts is not being generated.
The question now is: What's causing the difference?

I followed your steps and I was able to get the typings to generate in objc!nsswiftsupport.d.ts. After you generate typings do you have that file with these contents?
declare class TestClass extends NSObject {
static alloc(): TestClass; // inherited from NSObject
static new(): TestClass; // inherited from NSObject
echoWithParam(param: string): string;
}
This shows that NS is able to pick up the Swift code.
All you need to do now is add tns-platform-declarations then in the references.d.ts file, add a line that points to the objc!nsswiftsupport.d.ts file. Then you'll get intellisense in your TS code.
Something like this:
/// <reference path="./typings/objc!nsswiftsupport.d.ts" />
Hope this helps!

I was able to solve the problem by inspecting the differences between the templates created with tns-cli and vue init. The difference is that the vue init template ships with an outdated version of the nativescript platform. You can just simply change
"tns-ios": {
"version": "6.0.1"
}
to version 6.4.0 (which the version the tns-cli template comes with) and then the process will work as described in the documentation.

Related

Swift Package in workspace: import rule?

I create a swift package in my work space.
I followed this guide just to test things out:
https://sarunw.com/posts/how-to-modularize-existing-ios-projects-using-swift-package/
All went well.
One of the things I added to the package is:
public extension Color {
static let customRed:Color = Color(uiColor: UIColor(named: "customRed", in: .module, compatibleWith: nil)!)
}
I deleted the customRed from the Assets.xcassets in my main app after I added the Assets to the actual package.
Everything works fine now and the package uses the customRed as defined in the package Assets.xcassets.
I have a lot files that use that Color.customRed in the app and I was thinking I had to go to each file and add the import statement for the package at the top. So:
import MyColorPackage
Question: I don't understand why the app works fine without doing that. Files can use the Color.customRed call without adding the import MyColorPackage at the top of the file that uses it. How can files use that customRed without having the import MyColorPackage in the file? App runs fine without importing the module in the files that use the customRed. Why?
The reason for this is due to a longstanding swift bug so you’re not doing anything wrong per se. It has various forms, some fixed over the years, some not but in your case what happens is that the first file in your main project that imports MyColorPackage will cause the whole rest of the project to “see” that Color extension. In my experience this happens only with public extensions nowadays and your package happens to do just that - declare a public extension to SwiftUI’s Color
If you add some public entity in that package, say …
import SwiftUI
public enum MyColorTheme {
public static let myThemeButtonsColor = Color.green
}
… then you won’t be able to use MyColorTheme in any file that doesn’t import MyColorPackage, as per what is intuitively normal.
I would suggest to still add the missing imports whenever you use symbols from that package as this issue might be fixed in a future version and your project will fail to build
Reference: https://github.com/apple/swift/issues/46493

importing dart code from other projects

** This question is edited and cleaned up some **
I have two projects and I want to use code from one in the other; I seem to be having trouble putting the code in the right directory structure to make the import statements work.
Both projects are created and managed exclusively from the Dart Editor on a Mac, if that makes any differences.
Project Directory Structures
Project 1: a command line app which contains the code I want to share in the following directory structure:
/dart/command_line_app
/lib
shared_library.dart
/bin
command_line_app.dart
Project 2: a web app which wants to import the code in shared_libary.dart
/dart/web_application
/packages
/web
web_application.dart
In the file shared_libary.dart, I declare it to be a library can create a simple class that provides output when instantiated:
library shared_library;
class ShareMe
{
ShareMe()
{
print("Hello, ShareMe");
}
}
This compiles, and works inside the command_line project: command_line_app.dart has the following:
import 'package:command_line_app/shared_library.dart';
void main() {
ShareMe shareMe = new ShareMe();
print("Hello, World!");
}
This imports the code runs, printing both "Hello Share Me," and Hello World.
THE PROBLEM
I want to instantiate the ShareMe class inside web_application.dart. I'd thought I could do that by putting in the same import statement I put in my command_line code:
import 'package:command_line_app/shared_library.dart';
But, when I put the same import into the web_appliation, it gets the error
Target of URI does not exist 'package:command_line_app/shared_library.dart'
Other Things I've Tried
I was certain I'd solved the problem when I cntrl-Clicked properties on Web_application and selected Project References.
It brings up a window allowing me to select command_line_app with a check box, but when I do, I get an error:
Could not set the project description for 'web_application' because the project description file (.project) is out of sync with the file system.
Whatever that means.
When I cntrl-click the underlined error and try Quick Fix it offers me "resolve dependencies" which sounds promising, but after a few seconds, it comes back and informs me that
Pub get failed, [1] Resolving dependencies... (15.3s)
Could not find package command_line_app at https://pub.dartlang.org.
Depended on by:
- web_application 0.0.0
I hope this is clear-er and gives a better insight into both what I'm trying to do and what I'm missing.
EDIT
you need to add
dependencies:
command_line_app:
path: ../command_line_app
to your dependencies in web_application/pubspec.yaml.
EDIT END
When you want to make code reusable in different packages, you should put that code into the lib directory of that package and import it using import 'package:mypackage/myfile.dart';.
Another problem you may face is, that browser applications can't import packages that have a dart:io dependency. If you want to reuse code between command line and browser applications you should move them into the lib directory of another package my_shared_code where you put only code that doesn't depend on dart:io (for example some entity classes) and import this code from both app packages (browser and command line).

Could not find class 'weka.core.FastVector',

I am using weka for my project. but get the error info"could not find class weka.core.FastVector" on the line below. I have already added weka.jar from the build path of the project by adding external jar file. How should I solve this problem? Thanks a lot for your time on reviewing my question.
import weka.core.Attribute;
import weka.core.FastVector;
import weka.core.Instance;
import weka.core.Instances;
FastVector atts;
private void setUpARFF(){
atts = new FastVector();}
I know that FastVector was marked as obsolete a while back, perhaps they've finally removed it. Are you using the dev version of weka (or what version are you using)? FastVector can be replaced with ArrayList (in dev version) so use that instead.

How to build Unity3d Plugin for iOS

I have a very tiny Objective-C library built for iOS and I want to export it to Unity. I understand the basic process of writing a csharp wrapper that marshals all the invocations to native library, but I completely have no idea where to start. Could anyone please explain step-by-step how to create a unity package with my library so I could also distribute it to other developers.
Unity3d documentation is pretty brief and does not explain anything.
Thanks.
Okay, after playing few days with Unity3d on Mac I finally figured it out. All the code in this guide is dummy. I have written this stuff in 15 minutes or so, so don't be bothered by mistakes and typos.
1) Open Unity, create new project (File -> New Project) and save it somewhere
2) When the project is generated it has the following structure:
ProjectName/Assets (That's what you need)
ProjectName/Library (Nevermind what's there)
ProjectName/ProjectSettings (You don't care about it)
ProjectName/ProjectName.sln (MonoDevelop project)
3) Go to ProjectName/Assets and create the following folders: Plugins/iOS, so in the end you'll have a folder structure like this: ProjectName/Assets/Plugins/iOS
4) Put your compiled library (.a) file and necessary headers inside of ProjectName/Assets/Plugins/iOS or copy the source code of your library there (.mm, .h, .m, etc..). Remember, normally you can only access C-functions from C#, so you'll have to wrap your Objective-C stuff in C-code somehow, in my case all Objective-C objects were implemented in a form of Singleton so it wasn't hard to make a C-style wrapper around, for instance:
CWrapper.h:
extern "C" void MySDKFooBarCFunction();
CWrapper.mm
#import "CWrapper.h"
#import "MyObjectiveCLibrary.h" // your actual iOS library header
void MySDKFooBarCFunction() {
[MyObjectiveCLibrary doSomeStuff];
}
5) Then go to ProjectName/Assets and create a folder for CSharp wrapper class(es), call it whatever you want, for example: ProjectName/Assets/MySDK
6) Inside of MySDK folder create MySDK.cs file, the dummy example of C# wrapper would look like this:
using UnityEngine;
using System;
using System.Runtime.InteropServices;
public class MySDK
{
// import a single C-function from our plugin
[DllImport ("__Internal")]
private static extern void MySDKFooBarCFunction();
// wrap imported C-function to C# method
public static void FooBarCFunction() {
// it won't work in Editor, so don't run it there
if(Application.platform != RuntimePlatform.OSXEditor) {
MySDKFooBarCFunction();
}
}
}
7) Create a shell script to pack this stuff into .unitypackage and put it next to your project folder (not inside). Adjust EXPORT_PATH and PROJECT_PATH variables in the script for your needs.
#!/bin/sh
WORKDIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
UNITY_BIN="/Applications/Unity/Unity.app/Contents/MacOS/Unity"
EXPORT_PATH="${WORKDIR}/ProjectName.unitypackage"
PROJECT_PATH="${WORKDIR}/ProjectName"
ASSETS_PATH="Assets"
$UNITY_BIN -batchmode -quit \
-logFile export.log \
-projectPath $PROJECT_PATH \
-exportPackage $ASSETS_PATH $EXPORT_PATH
8) Run the created bash script to get your package build. All stuff from Assets will be included in XCode project for your Unity Project when you generate it via File -> Build Settings in Unity Editor. You can use generated package to distribute your code to other developers so they can simply include your library to their Unity projects by double clicking on the package file.
Don't forget to shutdown Unity Editor when you run this script, otherwise it may fail to build a package.
If you have some issues and package does not show up, this script always prints log to export.log
Next steps make sense only if you want to make a Demo unity project for your library (good for testing at least)
9) You can put created Unity project (ProjectName.unity) to Assets/MySDKDemo so you have a demo inside of your package.
10) Create a simple script for your Demo Unity3d scene at Assets/MySDKDemo/MySDKDemo.cs, for example:
using UnityEngine;
using System;
using System.Collections;
public class MySDKDemo : MonoBehaviour
{
private GUIStyle labelStyle = new GUIStyle();
private float centerX = Screen.width / 2;
// Use this for initialization
void Start ()
{
labelStyle.fontSize = 24;
labelStyle.normal.textColor = Color.black;
labelStyle.alignment = TextAnchor.MiddleCenter;
}
void OnGUI ()
{
GUI.Label(new Rect(centerX - 200, 20, 400, 35), "MySDK Demo", labelStyle);
if (GUI.Button(new Rect(centerX - 75, 80, 150, 35), "DoStuff"))
{
MySDK.FooBarCFunction();
}
}
}
11) Go to Unity Editor. Find the "Main Camera" in left sidebar in Unity Editor, select it and in the bottom of Inspector panel (right sidebar) click on AddComponent, select Scripts -> MySDKDemo script
12) Build the XCode project and run on device.
Few notes
1) Plugins don't work in Unity Editor, simply because they're not compiled in the real-time, well, not sure but probably until you use C# in your plugins, probably C# stuff gets linked immidiately and works in Editor environment.
2) This post does not cover marshaling, or data/memory management between native <-> managed code, as it is very well documented.
Interop with Native Libraries # Mono project
3) Callbacks from C# to C can be passed using C# delegates, on C-side you use standard functions declarations, on C# side you declare delegates with the same signature. It seems that booleans, integers and strings (C: char*) are marshalled flawlessly (I don't talk about memory management policy and who's responsible to release memory or return value policies).
However it will not work on iOS builds out-of-box due to platform limitations, but C#-to-C callbacks still can be implemented using MonoPInvokeCallbackAttribute, useful links on this topic:
Reverse Callbacks # Xamarin Docs
MonoPInvokeCallbackAttribute example # Xamarin Forums
Actually in Unity 4 there's AOT.MonoPInvokeCallbackAttribute already implemented, it's limited to static delegates that can be passed to unmanaged code, but still better than nothing.
4) There's a way to get Unity RootViewController using UnityGetGLViewController function. Just declare this function in your implementation file, i.e.:
extern UIViewController *UnityGetGLViewController();
And use UnityGetGLViewController() whenever you need to get an access to RootViewController.
5) There's much more magic and ugly stuff in details, keep your C interfaces as simple as possible otherwise marshalling can become your nightmare and also keep in mind that managed-to-unmanaged is generally expensive.
6) You definitely use some frameworks in your native code and you don't want linker problems. For example, if you use Keychain in your library then you need to include Security.framework into Xcode project.
I suggest to give a try to XUPorter, it helps Unity to integrate any additional dependencies into Xcode project.
Good luck!

XlsLib integration in IOS Application

I'm trying to integrate xlslib in my app. After compiling and installing lib, I have added this in my code. Then I got the error shown below:
format_t::format_t(CGlobalRecords& gRecords, const u16string& fmtstr) :
formatstr(fmtstr),
index(0),
m_usage_counter(0),
m_GlobalRecords(gRecords)
{
}
Errors:
format.cpp:125:52: Reference to 'u16string' is ambiguous
Out-of-line definition of 'format_t' does not match any declaration in
xlslib_core::format_t
while in format.h file, declaration as below
format_t(CGlobalRecords& gRecords, const u16string& fmtstr);
Can any one help to solve this issue?
Why didn't you post a bug report on the SourceForge page? In any case, the xlslib project has been updated and now offers the DHxlslibIOS library that you can incorporate as is into your project. You can download a zipped package here with the latest 2.4.0b1 source, or pull it from SVN. The framework is in the DHxls directory.

Resources