Nativescript - platform specific code - nativescript

I've got an iOS specific code (class definition) and would like to include in a separate ios specific file.
So I created two files
utils2.ios.ts <-- added my code here
utils2.android.ts
And then from another file, I did:
import utils2 = require("./utils2");
But I'm getting an error message that the module is not found.
I had to go this route because I've got a class definition in
the code and if I have just one file, I get a run-time error on Android.
Does typescript support platform specific files?
Or another option - is something like this possible in ts file
if ios
do this
end
Similar to C preprocesser

You can get by with just a utils.ts file and inside it you can do platform specific logic that you mention. Your issue about the module not found is likely just a path issue to that module, not that the module isn't actually there. Also note that Typescript doesn't really "support" platform specific files. Nativescript takes those .android.ts and .ios.ts files and at runtime loads the file you need on the platform running. So just FYI that's not really TS related :)
Nativescript provides a platform module in the tns-core-modules. Import the { isAndroid } or { isIOS } from the platform module in your code and use it as the
if (isAndroid) {
// do android only here
} else {
// ios
}

Related

NativeScript: use custom objective-c imported class into Xcode project in NativeScript

basically I have some Objective-c class already existing and I want to use them in my NativeScript projet. Currently I have added those files to the Xcode project target and I want to be able to call my objective-c code from nativeScript js. I've read the doc but I don't understand it. It seems so complicated. basically currently all I want to do is be able to present my custom view controller by calling probably something along
const vc = MyCustomViewController.alloc.initWithNibName("xib file")
page.frame.ios.controller.present(vc,true, nil)
Am I obligated to create a plugin for that? Am I obligated to use my objective-c class to build a framework in Xcode and then import the framework?
So I found out.
Actually what you need to do is first to compile your native iOS code into a framework. As per the documentation all your classes must inherit NSObject and all your function must be marked with #objc to be exposed to the objective-c Nativescript side if you write in swift. You will notice as well that in a framework your bundle is not the main bundle. In this example you can see how you can retrieve the bundle from the framework and load a xib from it.
Then you need to add your framework file to a Nativescript plugin. For that, you want to add the framework to the plugin's iOS folder yourPlugin/platform/iOS/yourFramework.framework.
then, you need to add your plugin to your app. You can add your local plugin by using the next command line. Notice the path end with the /src folder.
tns plugin add /path/to/yourplugin/src
Now, you can then call your native functions and classes without even importing them. Of course this works only on iOS. If you run your app on android, calling those methods will crash.
To show this viewController on your Nativescript side you will need to call the following code. By the way You can find documentation elsewhere to get a reference to the current page or frame object.
const controller = page.frame.ios.controller
const vc = IOMediaViewController.create()
vc.modalPresentationStyle = UIModalPresentationFullScreen
controller.presentViewControllerAnimatedCompletion(vc,true,null)

NativeScript: CocoaPod that works in Pod example does not expose symbols in plugin project

I am trying to understand the best ways in which to bring native library code into a NativeScript plugin for iOS. I've had success in past bringing in a CocoaPod and accessing the symbols from that. So I want to create my own.
I follow the process for pod lib create TestPod to generate my library project. (https://guides.cocoapods.org/making/using-pod-lib-create.html)
The 'library' is a trivial test: it simply creates a class with a function that returns
a recognizable greeting string.
The associated "Example app" demo produces a text label that displays this string.
This all works as expected, all running in pure iOS world (written in Swift).
At the Nativescript side, I'm using the Nativescript Plugin Seed and I'm declaring my Podfile in the src/platforms/iOS folder as directed. My "plugin" (trivial as it is as a test), has the iOS-specific parts in the pluginName.ios.js file. I have a separate test method in here that verifies I can reach and use native iOS platform symbols (e.g. NSMutableString) and that works as expected. I want to do the same thing with native code imported from my library.
But when I bring the Podfile into Nativescript, it builds okay, but I'm not able to see any of the symbols as I would expect to see.
I generate typings and I don't find them either, but I do find a "TestPod.d.ts" typings file that declares some version info and a mysterious class named "UITest" that I did not define and bears no resemblance to my "SimpleTest" class, which I can't find anywhere.
I'm sure I'm missing something here that is probably obvious to the knowledgeable. But I'm unable to guess what it is. Any ideas?
import Foundation
#objc public class SimpleTest : NSObject {
public func announce() -> String {
return "Greetings from Swift code in a library"
}
}
I'm calling it in the plugin using the following:
public testNativeLib() : string {
let str:string;
try {
const testClass = new SimpleTest()
str = testClass.announce()
} catch(e) {
str = e.message;
}
return str;
}
and what I get returned for str is the catch case error message: "Can't find variable: SimpleTest"
Without seeing the source to the pod file; the only guess would be that you didn't use the #objc on anything that you wanted exposed. Without you exposing anything with #objc (or descending from a native objc class, so it is tagged by swift automagically) NativeScript cannot see it.
Please note their are some types in Swift that currently cannot be consumed by NativeScript (Or ObjC) and you have to write some wrapper code in swift around it using types that are compatible with ObjC so that NativeScript can use it.
Please see: https://docs.nativescript.org/guides/ios-source-code for more info on requirements...
The code needs to be:
import Foundation
public class SimpleTest : NSObject {
#objc public func announce() -> String {
return "Greetings from Swift code in a library"
}
}
Move the #objc to the actual property/function you want exposed. The NSObject will already cause the class to be exposed if it has exposed members.
The metadata generated:
A complete demo using a swift source file in a plugin is now located at
https://github.com/NathanaelA/demo-swift-plugin
If I have full control over the source, then I won't normally add another moving piece (i.e. cocoapod) to the mix; just let xcode compile the swift code and expose it, no need to add any additional places something can break.
However, if you want to see how to do an actual swift CocoaPod plugin; checkout any of these plugin repos that use swift code and cocoapod:
https://github.com/tomvardasca/nativescript-crypto
https://github.com/arpit2438735/nativescript-tglib
https://github.com/Daltron/NotificationBanner
In the case of an actual cocoapod; you need to have a valid Podfile, and a valid podspec file! Once you have those; when you build the application, you can open up xcode and verify that the cocoapod is linked in. If it isn't linked in then your podfile/podspec is messed up and has to do with a issue with Cocoapod and it is not a NativeScript issue. So in that case; you need to follow some Cocoapod tutorials to get it to work.
Please note; nuking your platform folder frequently while you are testing with cocoapods is highly recommended. Occasionally Nativescript does not detect the changes to Native code properly and so it then doesn't rebuild the xcode project/workspace files. So nuking your platforms folder will of course force it to rebuild them. If you don't you WILL waste a lot of time while messing around with cocoapods.
When running the sample project you will see this:
In addition, in the demo, the metadata generated from the code I saved in the demo/metadata folder so you can look at it.

Xamarin Android Layout Resources in library project not included in Application

I have a Xamarin Android library project which is referenced by an Android application project.
The library project has layout files which need to be used by a library project component. The Resource.designer.cs file seems to be generated properly. The layouts are marked as Android Resources. Yet at runtime the resources are not there. Trying to access them just returns 0.
After reading all the SO questions on the topic, I am convinced this should work, but so far it doesn't. Ideas?
Create the Android Class Library.
Create a layout folder in this class library and create a layout in this folder.
Add the code in your xamarin android app.
var btn_Load = FindViewById<Button>(Resource.Id.btn_LoadLibraryLayout);
btn_Load.Click += delegate
{
SetContentView(ClassLibrary1.Resource.Layout.library_layout);
};
I create a button to open the layout in class library.
Result:
You could download from the ClassLibrary_layout folder of GitHub for reference.
https://github.com/WendyZang/Test.git
Updated:
If you want to get the resource identifier, you need the package name of this class library. First, we could not get the package name in AndroidManifest.xml file or properties, because the class library does not have it.
You could get it from the steps below.
Create a activity in class library. And get the package name with the code.
var PackageNname = Application.Context.PackageName;
You could also get the resource identifier in this activity.
var resId2 = Resources.GetIdentifier("library_layout", "layout", PackageNname);
Result:
The paskage name is same to the app1 I provided.
Package Name: com.companyname.app1
You could also use the code directly in app1 activity.
int resId2 = Resources.GetIdentifier("library_layout", "layout", "com.companyname.app1");

Appcelerator Hyperloop - using 3rd party swift library

I'm trying to use Hyperloop in order to use a third-party open source library. The library I want to use is Sweet Alert iOS.
The library has a swift file. I have put this file inside an src directory inside my project's main folder (does it have to be inside src folder? can I use other folder in my project? what about sub-directories?).
According to the Titanium documentation:
Any *.swift files found in your src directories will automatically be compiled
In my app when I do something like this:
var UIView = require('UIKit/UIView');
Ti.API.info('UIView => ' + UIView);
It works. But if I try to require any of the classes in the swift file I get an error that it can't find architecture x86_64 module.
var sweetAlert = require('SweetAlert');
var cancelAnimatedView = require('CancelAnimatedView');
When looking at the SweetAlert source code, I also notice that SweetAlert class is open while other classes are just regular classes:
open class SweetAlert
I'm not a swift developer so I don't really know what it means.
What am I missing here?
EDIT
Some progress... So I found that I need to have an appc.js file that's defining the src frame work and a name for MyFramework. And that the require I do should match the filename of the swift file. Now when I compile I see some more interesting output about the swift compilation, but getting an error:
[INFO] Generating metabase for swift MyFramework /Users/ophir/Documents/Appcelerator_Studio_Workspace/HyperloopTest1/src/SweetAlert.swift
2017-06-29T14:43:44.061Z | ERROR | An uncaught exception was thrown!
Cannot read property '1' of null
2017-06-29T14:43:44.064Z | ERROR | Cannot read property '1' of null
EDIT 2
I've cleared all the script in the swift file, and narrowed it down to the following failing script:
import Foundation
import UIKit
import QuartzCore
public enum AlertStyle {
case success,error,warning,none
case customImag(imageFile:String)
}
class SweetAlert: UIViewController {
}
class AnimatableView: UIView {
func animate(){
}
}
If I remove the func animate() { } it will not fail. Even if this function is inside that one SweetAlert class then it will fail. And from what I've read this is a pretty standard swift code.
So this makes me wonder - is Hyperloop 2.0.0(!) ready for production apps?
I receive a similar error when creating a simple Swift class and including it in a Titanium/Appcelerator project with the appc.js file set up just how they show in the sparse documentation. The project will compile with no issues and run in the Xcode Simulator, as long as I don't reference the class from the Swift file. If I reference that class in the project, the error arises. Based on the compile log, Swift files do compile into the project, but the classes don't seem to properly link in the end for "require"-ing into the JavaScript code.
When I search online for solutions, this same issue shows up all over, and the Titanium/Appcelerator team never presents a solution. I've tried Titanium SDK 6.0.1, 6.1.2, and 6.2.0, with all the same results; I've tried adding Swift code to existing projects and to new ones; I've also uninstalled and reinstalled Titanium/Appcelerator. (I've even tried running their sample app with no luck!)
Hyperloop does work when I use native frameworks, like UIKit, but it doesn't seem ready for production apps for 3rd party frameworks and classes.
I hope my discoveries help you and others to debug projects, but, unfortunately, I don't think I will be able to offer much help outside that.

How to add Windows Phone project with resx localization to Xamarin PCL solution?

I have a working PCL project for iOS and I'm trying to create the Windows Phone 8.1 version.
I'm following this tutorial: https://developer.xamarin.com/guides/xamarin-forms/advanced/localization/
And checking the app at: https://github.com/xamarin/xamarin-forms-samples/tree/master/UsingResxLocalization
But it is too deprecated. Even the git project is different from the tutorial, and none of them works.
The ILocalize interface for Windows should look like:
[assembly: Dependency(typeof(UsingResxLocalization.WinPhone.Localize))]
namespace UsingResxLocalization.WinPhone
{
public class Localize : UsingResxLocalization.ILocalize
{
public System.Globalization.CultureInfo GetCurrentCultureInfo ()
{
return System.Threading.Thread.CurrentThread.CurrentUICulture;
}
}
}
But System.Threading.Thread.CurrentThread.CurrentUICulture simply doesn't exist. I found out that I can use Windows.System.UserProfile.GlobalizationPreferences.Languages[0].ToString() instead.
It works for the localized language resources but the default Resource is not working either for the default language "en" or any other non localized language like "ru". I get another error:
In the TranslateExtention class ProvideValue() method I get:
Key 'Start' was not found in resources 'AppNameSpace.AppResources' for
culture 'en'
Being "Start" the first key it tries to get from the resource. It happens for all the other keys on the project.
AppNameSpace.AppResources would be the right file, and "en" is the region I set, so it should work. But it's not.
I'm also getting the following warning when compiling:
The assembly "MyApp.dll"
does not have a NeutralResourcesLanguageAttribute on it. To be used in
an app package, portable libraries must define a
NeutralResourcesLanguageAttribute on their main assembly (ie, the one
containing code, not a satellite assembly). 4>C:\Program Files
(x86)\MSBuild\Microsoft\VisualStudio\v14.0\AppxPackage\Microsoft.AppXPackage.Targets(1216,5):
warning APPX2002: Cannot find the neutral resource language for the
resources for portable library 'MyApp'. Verify that
the portable library defines a NeutralResourcesLanguageAttribute. The
build is continuing assuming the project's default culture of 'en-US'
correctly identifies the portable library's neutral resources.
4>MakePRI : warning 0xdef00522: Resources found for language(s) 'de,
es, fr, pt' but no resources found for default language(s): 'en-US'.
Change the default language or qualify resources with the default
language. http://go.microsoft.com/fwlink/?LinkId=231899
But I have no idea how to fix it.
On the tutorial it also says:
Windows Phone projects must be properly configured for localized text
to be displayed. Supported languages must be selected in the Project
Options and the WMAppManifest.xml files. If these settings are not
updated the localized RESX resources will not be loaded.
Fine, but those options doesn't exist anymore. At least where they should be. I even found a Package.appxmanifest file in my project, but it doesn't have those regional options.
So, I need help with an updated way to do it.
Thanks
So I found out that when you add a Windows Phone project to a solution with no Windows Phone projects, it doesn't add everything it needs.
Also the tutorials don't show everything that is necessary (no big news there).
All my project was missing was [assembly: NeutralResourcesLanguage("en-US")] in my PCL AssemblyInfo.cs file.
The RESX tutorial also says that you should use:
if (Device.OS == TargetPlatform.iOS || Device.OS == TargetPlatform.Android)
{
ci = DependencyService.Get<ILocalize>().GetCurrentCultureInfo();
}
In the TranslateExtention.cs file because Windows Phones don't need it. Well, that's false. At least for the emulator to get the right language, it needs to use the DependencyService and get the CultureInfo this way:
System.Globalization.CultureInfo ci = null;
ci = new System.Globalization.CultureInfo(Windows.System.UserProfile.GlobalizationPreferences.Languages[0].ToString());
return ci;

Resources