Publishing a precompiled CocoaPods - xcode

We are currently building an SDK for a customer using CocoaPods.
The main problem we have is that our boss would like the SDK to be a black box. He wants us to precompile the code in order to protect our source.
Is there anything we can do within the Podspec in order to protect our code?

You can do exactly that by creating a Static Framework and including it in the spec.vendored_frameworks property on your podspec.
http://guides.cocoapods.org/syntax/podspec.html#vendored_frameworks
Follow the tutorial below for how to create your own static framework.
https://github.com/jverkoey/iOS-Framework#walkthrough
How to Create a Static Framework for iOS
There are a few constraints that we want to satisfy when building a .framework:
Fast iterative builds when developing the framework. We may have a simple application that has the
.framework as a dependency and we want to quickly iterate on development of the .framework.
Infrequent distribution builds of the .framework.
Resource distribution should be intuitive and not bloat the application.
Setup for third-party developers using the .framework should be easy.
I believe that the solution I will outline below satisfies each of these constraints. I will outline
how to build a .framework project from scratch so that you can apply these steps to an existing
project if you so desire. I will also include project templates for easily creating a
.framework.
Overview
View a sample project that shows the result of following these steps in the sample/Serenity
directory.
Within the project we are going to have three targets: a static library, a bundle, and an aggregate.
The static library target will build the source into a static library (.a) and specify which headers
will be "public", meaning they will be accessible from the .framework when we distribute it.
The bundle target will contain all of our resources and will be loadable from the framework.
The aggregate target will build the static library for i386/armv6/armv7/armv7s, generate the fat framework
binary, and also build the bundle. You will run this target when you plan to distribute the
.framework.
When you are working on the framework you will likely have an internal application that links to the
framework. This application will link to the static library target as you normally would and copy
the .bundle in the copy resources phase. This has the benefit of only building the framework code
for the platform you're actively working on, significantly improving your build times. We'll do a
little bit of work in the framework project to ensure that you can use your framework in your app
the same way a third party developer would (i.e. importing should work
as expected). Jump to the dependent project walkthrough.
Create the Static Library Target
Step 1: Create a New "Cocoa Touch Static Library" Project
The product name will be the name of your framework. For example, Serenity will generate
Serenity.framework once we've set up the project.
Step 2: Create the Primary Framework Header
Developers expect to be able to import your framework by importing the <Serenity/Serenity.h>
header. Ensure that your project has such a header (if you created a new static library then there
should already be a Serenity.h and Serenity.m file; you can delete the .m).
Within this header you are going to import all of the public headers for your framework. For
example, let's assume that we have some Widget with a .h and .m. Our Serenity.h file would look
like this:
#import <Foundation/Foundation.h>
#import <Serenity/Widget.h>
Once you've created your framework header file, you need to make it a "public" header. Public
headers are headers that will be copied to the .framework and can be imported by those using your
framework. This differs from "project" headers which will not be distributed with the framework.
This distinction is what allows you to have a concept of public and private APIs.
To change a file's [target membership visibility in XCode 4.4+]
(Can't change target membership visibility in Xcode 4.5),
you'll need to select the Static Library target you created (Serenity), open the Build Phases tab:
Xcode 4.X:
Click on Add Build Phase > Add Copy Headers.
Xcode 5:
Add Build Phases from the menu. Click on Editor > Add Build Phase -> Add Copy Headers Build Phase. Note: If the menu options are grayed out, you'll need to click on the whitespace below the Build Phases to regain focus and retry.
You'll see 3 sections for Public, Private, and Project headers. To modify the scope of any header, drag and drop the header files between the sections. Alternatively you can open the Project Navigator and select the header. Next expand the Utilities pane for the File Inspector.
(Cmd+Option+0).
Look at the "Target Membership" group and ensure that the checkbox next to the .h file is checked.
Change the scope of the header from "Project" to "Public". You might have to uncheck and check the box to get the dropdown list. This will ensure that the header gets
copied to the correct location in the copy headers phase.
Step 3: Update the Public Headers Location
By default the static library project will copy private and public headers to the same folder:
/usr/local/include. To avoid mistakenly copying private headers to our framework we want to ensure
that our public headers are copied to a separate directory, e.g. $(PROJECT_NAME)Headers. To change this setting,
select the project in the Project Navigator and then click the "Build Settings" tab. Search for "public
headers" and then set the "Public Headers Folder Path" to "$(PROJECT_NAME)Headers" for all configurations.
If you are working with multiple Frameworks make sure that this folder is unique.
Ongoing Step: Adding New Sources to the Framework
Whenever you add new source to the framework you must decide whether to expose the .h publicly or
not. To modify a header's scope you will follow the same process as Step 2. By default a header's
scope will be "Project", meaning it will not be copied to the framework's public headers.
Step 4: Disable Code Stripping
We do not want to strip any code from the library; we leave this up to the application that is
linking to the framework. To disable code stripping we must modify the following configuration
settings:
"Dead Code Stripping" => No (for all settings)
"Strip Debug Symbols During Copy" => No (for all settings)
"Strip Style" => Non-Global Symbols (for all settings)
Step 5: Prepare the Framework for use as a Dependent Target
In order to use the static library as though it were a framework we're going to generate the basic
skeleton of the framework in the static library target. To do this we'll include a simple post-build
script. Add a post-build script by selecting your project in the Project Navigator, selecting the target, and then the
"Build Phases" tab.
Xcode 4.X: Click Add Build Phase > Add Run Script
Xcode 5: Select Editor menu > Add Build Phase > Add Run Script Build Phase
Paste the following script in the source portion of the run script build phase. You can rename the phase by clicking
the title of the phase (I've named it "Prepare Framework", for example).
prepare_framework.sh
set -e
mkdir -p "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
# Link the "Current" version to "A"
/bin/ln -sfh A "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/Current"
/bin/ln -sfh Versions/Current/Headers "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Headers"
/bin/ln -sfh "Versions/Current/${PRODUCT_NAME}" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/${PRODUCT_NAME}"
# The -a ensures that the headers maintain the source modification date so that we don't constantly
# cause propagating rebuilds of files that import these headers.
/bin/cp -a "${TARGET_BUILD_DIR}/${PUBLIC_HEADERS_FOLDER_PATH}/" "${BUILT_PRODUCTS_DIR}/${PRODUCT_NAME}.framework/Versions/A/Headers"
This will generate the following folder structure:
-- Note: "->" denotes a symbolic link --
Serenity.framework/
Headers/ -> Versions/Current/Headers
Serenity -> Versions/Current/Serenity
Versions/
A/
Headers/
Serenity.h
Widget.h
Current -> A
Try building your project now and look at the build products directory (usually
~/Library/Developer/Xcode/DerivedData/<ProjectName>-<gibberish>/Build/Products/...). You should
see a libSerenity.a static library, a Headers folder, and a Serenity.framework folder that
contains the basic skeleton of your framework.
Create the Framework Distribution Target
When actively developing the framework we only care to build the platform that we're testing on. For
example, if we're testing on the iPhone simulator then we only need to build the i386 platform.
This changes when we want to distribute the framework to third party developers. The third-party
developers don't have the option of rebuilding the framework for each platform, so we must provide
what is called a "fat binary" version of the static library that is comprised of the possible
platforms. These platforms include: i386, armv6, armv7, and armv7s.
To generate this fat binary we're going to build the static library target for each platform.
Step 1: Create an Aggregate Target
Click File > New Target > iOS > Other and create a new Aggregate target. Title it something like "Framework".
Step 2: Add the Static Library as a Dependent Target
Add the static library target to the "Target Dependencies".
Step 3: Build the Other Platform
To build the other platform we're going to use a "Run Script" phase to execute some basic commands.
Add a new "Run Script" build phase to your aggregate target and paste the following code into it.
build_framework.sh
set -e
set +u
# Avoid recursively calling this script.
if [[ $SF_MASTER_SCRIPT_RUNNING ]]
then
exit 0
fi
set -u
export SF_MASTER_SCRIPT_RUNNING=1
SF_TARGET_NAME=${PROJECT_NAME}
SF_EXECUTABLE_PATH="lib${SF_TARGET_NAME}.a"
SF_WRAPPER_NAME="${SF_TARGET_NAME}.framework"
# The following conditionals come from
# https://github.com/kstenerud/iOS-Universal-Framework
if [[ "$SDK_NAME" =~ ([A-Za-z]+) ]]
then
SF_SDK_PLATFORM=${BASH_REMATCH[1]}
else
echo "Could not find platform name from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SDK_NAME" =~ ([0-9]+.*$) ]]
then
SF_SDK_VERSION=${BASH_REMATCH[1]}
else
echo "Could not find sdk version from SDK_NAME: $SDK_NAME"
exit 1
fi
if [[ "$SF_SDK_PLATFORM" = "iphoneos" ]]
then
SF_OTHER_PLATFORM=iphonesimulator
else
SF_OTHER_PLATFORM=iphoneos
fi
if [[ "$BUILT_PRODUCTS_DIR" =~ (.*)$SF_SDK_PLATFORM$ ]]
then
SF_OTHER_BUILT_PRODUCTS_DIR="${BASH_REMATCH[1]}${SF_OTHER_PLATFORM}"
else
echo "Could not find platform name from build products directory: $BUILT_PRODUCTS_DIR"
exit 1
fi
# Build the other platform.
xcrun xcodebuild -project "${PROJECT_FILE_PATH}" -target "${TARGET_NAME}" -configuration "${CONFIGURATION}" -sdk ${SF_OTHER_PLATFORM}${SF_SDK_VERSION} BUILD_DIR="${BUILD_DIR}" OBJROOT="${OBJROOT}" BUILD_ROOT="${BUILD_ROOT}" SYMROOT="${SYMROOT}" $ACTION
# Smash the two static libraries into one fat binary and store it in the .framework
xcrun lipo -create "${BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_EXECUTABLE_PATH}" -output "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
# Copy the binary to the other architecture folder to have a complete framework in both.
cp -a "${BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}" "${SF_OTHER_BUILT_PRODUCTS_DIR}/${SF_WRAPPER_NAME}/Versions/A/${SF_TARGET_NAME}"
Important Note
The above script assumes that your library name matches your project name in the following line:
SF_TARGET_NAME=${PROJECT_NAME}
If this is not the case (e.g. your xcode project is named SerenityFramework and the target name is
Serenity) then you need to explicitly set the target name on that line. For example:
SF_TARGET_NAME=Serenity
Step 4: Build and Verify
You now have everything set up to build a distributable .framework to third-party developers. Try
building the aggregate target. Once it's done, expand the Products folder in Xcode, right click the
static library and click "Show in Finder". If this doesn't open Finder to where the static library
exists then try opening
~/Library/Developer/Xcode/DerivedData/<project name>/Build/Products/Debug-iphonesimulator/.
Within this folder you will see your .framework folder.
You can now drag the .framework elsewhere, zip it up, upload it, and distribute it to your
third-party developers.

I succeeded using that Podspec as an example :
Pod::Spec.new do |s|
s.name = "EstimoteSDK"
s.version = "1.3.0"
s.summary = "iOS library for Estimote iBeacon devices"
s.homepage = "http://estimote.com"
s.author = { "Estimote, Inc" => "contact#estimote.com" }
s.platform = :ios
s.source = { :git => "https://github.com/Estimote/iOS-SDK.git", :tag => " {s.version}" }
s.source_files = 'EstimoteSDK/Headers/*.h'
s.preserve_paths = 'EstimoteSDK/libEstimoteSDK.a'
s.vendored_libraries = 'EstimoteSDK/libEstimoteSDK.a'
s.ios.deployment_target = '7.0'
s.frameworks = 'UIKit', 'Foundation', 'SystemConfiguration', 'MobileCoreServices', 'CoreLocation'
s.requires_arc = true
s.xcconfig = { 'LIBRARY_SEARCH_PATHS' => '"$(PODS_ROOT)/EstimoteSDK"',
'HEADER_SEARCH_PATHS' => '"${PODS_ROOT}/Headers/EstimoteSDK"' }
s.license = {
:type => 'Copyright',
:text => <<-LICENSE
Copyright 2013 Estimote, Inc. All rights reserved.
LICENSE
}
end

Static Libraries are not supported in Swift, so for anyone coming here looking for the solution for a Swift SDK, here is a nice article explaining how it should be done.
Update
Swift 4 now natively supports static swift libraries.
Side note:
For Anyone still interested in creating a swift dynamic library, this is still a nice, very helpful article article

Related

Getting 'no such module' error when importing a Swift Package Manager dependency

I'm running Xcode 11 Beta 4.
I'm using CocoaPods, and wanted to use one of my dependencies with Swift Package Manager as a static library instead of as a framework.
On a fresh project created with Xcode 11, the dependency can be imported successfully, but on my existing CocoaPods workspace, it does not.
I think it's likely related, but I'm also getting this link warning in Xcode:
directory not found for option '-L/Users/username/Library/Developer/Xcode/DerivedData/App-axanznliwntexmdfdskitsxlfypz/Build/Products/Release-iphoneos
I went to see if the directory exists after the warning is emitted, and it does.
I could not find any meaningful difference between the newly-created project and my old one, other than the existence of CocoaPods.
Would appreciate any pointers.
After adding a library (FASwiftUI in my case) through Swift Package Manager I had to add it to
Project Settings -> My Target ->
General -> Frameworks, Libraries, and Embedded Content
to be visible in the import statement.
I did not add any scripts for it to work.
Based on #AlexandreMorgado answer it seems like it is better to run this script in Build phases before Compile Sources. Then it works when archiving.
if [ -d "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" ] && [ "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" != "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/" ]
then
cp -f -R "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/"
fi
Solution
let package = Package(
name: "PackageName",
dependencies: [
// YOU MUST ADD THE DEPENDENCY BOTH HERE [1] AND BELOW [2]
.package(url: "https://github.com/mattmaddux/FASwiftUI", from: "1.0.4")
],
targets: [
.target(
name: "PackageName",
/*[2]*/ dependencies: ["FASwiftUI"], // [2] <<<--------- Added here as well
]
)
Explanation
I'm developing a Swift package that must provide FontAwesome Icons to whoever imports it.
I was getting "No such module 'FASwiftUI'" in my SwiftUI preview canvas.
I solved it by adding "FASwiftUI" to BOTH the dependencies array of the package AS WELL AS to the dependencies array in the target itself.
Full Package.swift File
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.
import PackageDescription
let package = Package(
name: "PackageName",
platforms: [
.macOS(.v11),
.iOS(.v14)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "PackageName",
targets: ["PackageName"])
],
dependencies: [
// Dependencies declare other packages that this package depends on.
.package(url: "https://github.com/nalexn/ViewInspector", from: "0.8.1"),
.package(url: "https://github.com/mattmaddux/FASwiftUI", from: "1.0.4")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "PackageName",
dependencies: ["FASwiftUI"], // <<<--------- Added this here
resources: [
.process("Assets")
]
),
.testTarget(
name: "PackageNameTests",
dependencies: ["PackageName", "ViewInspector"])
]
)
It turned out that Swift Package Manager implicitly depends on the project's Configuration names. I had them at live/qa instead of Release/Debug, and changing them back resolved the issue. Very odd, but I hope it saves you some trouble dear reader.
After a whole week fighting this issue, I developed a workaround using schemes and pre-actions.
I have a configuration called "Beta", so Xcode can't compile SPM dependencies. I realised Xcode compile SPM dependencies as Swift modules and add the files in Build/Products/Release-iphoneos folder in DeriverData.
So I created a scheme in Xcode and added this run script on build pre-actions:
cp -f -R "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/"
This script run before the build process, copying files and modules generated by Xcode on default Release-iphoneos folder to configuration folder, Beta-iphoneos, in my case.
After coping the content from Release-iphoneos to your $configuration$-iphoneos folder Xcode should correctly compile, build and run your project.
I just ran into a similar problem and discovered that my schemes referenced old configurations, configurations that no longer existed. Once I updated them to the correct configurations the build succeeded.
(I'm leaving this comment more than a year after the original post. It's possible that what I ran into is completely different from what was originally reported. Still, it took me quite a while to track the problem down, so I wanted to leave a note that might save others time.)
Clearing the derived data solved the issue in my case. I have Microsoft Azure Devops CI Pipeline, to clear the derived data I have to edit the Xcode build task and in the "Actions" field add this command: clean.
What worked for me: I removed my import WebMIDIKit line and added it again.
Based on #sliwinski.lukas's answer, in my case the ${CONFIGURATION} was outputting "Release", so it was just copying the Release folder itself which was no good. I simply hardcoded my configuration name, and flipped Release and MyConfiguration, and it worked. I put the following code right before "Compile Sources" in the "Build Phases" tab:
cp -f -R "${SYMROOT}/MyConfiguration${EFFECTIVE_PLATFORM_NAME}/" "${SYMROOT}/Release${EFFECTIVE_PLATFORM_NAME}/" || true
Also importantly, I had to add this in the project that used the SPM and not in the main app.
I just ran into a similar problem when running xcodebuild from the command line. I was passing CONFIGURATION_BUILD_DIR=build but found that it needs to be an absolute path: CONFIGURATION_BUILD_DIR=$(pwd)/build solved the problem.
Might I shed a bit more light on your plight...
I'm working on a fairly large iOS app (6680 files) whose result is composed of many frameworks and a mixed bag of podfiles, swift packages, legacy ObjC code (that still outnumbers newer Swift stuff).
Whenever we deal with swift packages, we need to wrap them in frameworks because it simplifies podfile & dependency resolutions when we have our remote (Jenkins) build system eat everything up to spew binaries for internal QA & ultimately, Enterprise & AppStore publishing.
Earlier today, I was dealing with one such swift package wrapped in a framework and all the issues listed above hit me square in the face.
After stashing, pushing usable code and then reapplying my stashed framework wrapper to the swift package, I used a different route than opening our project's workspace where a bunch of projects and targets are collected.
Opening the lone framework wrapper seems to have kicked XCode (13.3.1) into submission and at that point, the target settings "Frameworks, Libraries and Embeddable" section was actually able to display the swift package's "Foo" binary. Added it, and then everything was playing nice.
So, if you're still having problems, try simplifying the problem by opening smaller morsles if you can. Or start making these wrapper frameworks (if it's at all possible) so that you can actually manage smaller bites before integrating them on XC's platter.
For me, I go to Xcode -> File (The one on mac top bar) -> Packages -> Update to Latest Package Versions. This solved my problem.
In order to keep incremental builds working I had to specify the output files of "Fix SPM" build phase like so:

NDK Error "Unexpected native build target xyz. Valid targets are:"

I have an Android Studio project which depends on a native shared library. I have created a cmake file to compile the library and I have added a soft link to the shared library inside the android project (in src/main/jniLibs/armeabi). That way when the android project is built, the shared library is included in the package.
Here is the relevant part of build.gradle:
android {
...
externalNativeBuild {
cmake {
path "../cpp/CMakeLists.txt"
}
}
}
The problem is that gradle tries to open the shared library before invoking the instructions to build it.
Information:Gradle tasks [:app:assembleDebug]
Error:Could not list contents of 'app/src/main/jniLibs/armeabi/libfoo.so'. Couldn't follow symbolic link.
How can I invoke the cmake from inside the project and include the library in the project at the same time?
--
EDIT
In the cmake the shared library is built with ExternalProject_Add. Unfortunately gradle doesn't see that target, nor does it see imported shared libraries as targets. So this does not work:
add_library(libfoo SHARED IMPORTED GLOBAL)
add_dependencies(libfoo libactual)
I tried to invoke building the particular target with a gradle config:
defaultConfig {
...
externalNativeBuild {
cmake {
targets "libfoo"
}
}
}
But gradle still doesn't see it and fails with:
Unexpected native build target libfoo. Valid values are:
The valid values are basically an empty list.
Currently I work around this by creating a fictional executable depending on the library.
add_executable(libfoo a.c)
add_dependencies(libfoo libactual)
In my case, I added a new CMake target, but having none was cached somehow (by CMake or Gradle).
Simply close Android Studio, remove the entire build or .build directory, then open Android Studio and build again.
Note that sub-projects have their own separate build directory.
So, you may need to search for the word build, and after ensuring found result is not required, remove them too.
If still not fixed, remember that CMake has it's own separate cache files, which normallay are inside said directories unless you run CMake directly (outside of Android Studio).

ModuleMap: How can I set a relative path for the umbrella header?

I am working with the Swift Package Manager. I have a project which I can successfully build via "swift build". I have created an Xcode project via "swift package generate-xcodeproj". When I open the project in Xcode it builds successfully.
The Xcode project includes two modules A and B.
Module A has the following map:
module ModuleA {
umbrella header "/Users/Robert/Temp/MyProject/Sources/ModuleA/include/ModuleA.h"
link "ModuleA"
export *
}
Module B depends upon A and has the following import:
import ModuleA
So far so good; everything builds successfully. Now I want to change the module map so that it uses a relative path, such as:
module ModuleA {
umbrella header "ModuleA.h"
link "ModuleA"
export *
}
However, when I do that I cannot get Module B to build: Error - Umbrella header 'ModuleA.h' not found. I have tried everything that I can think of in Build Settings -> Search Paths -> Header Search Paths and User Header Search Paths. I've found similar issues online, here and elsewhere, and have tried what I read but so far no go.
This has reached the hair pulling stage. Any advice will be much appreciated!
My guess is you're modifying the generated modulemap. Create a modulemap file at /Users/Robert/Temp/MyProject/Sources/ModuleA/include/module.modulemap containing:
module ModuleA {
umbrella header "ModuleA.h"
link "ModuleA"
export *
}
Run $ swift package clean to remove the old generated modulemap in .build directory and $ swift build to confirm that the custom modulemap works.
Then delete the generated Xcode project and re-generate it.

Swift app: “Missing required module” when importing framework that imports static library

Here’s my setup:
Static library of Objective C code called Stat.
A Swift framework that uses code from Stat in its own classes (this framework is called Dyn). The static library and this framework are in the same Xcode project.
A Mac app / project that has the above project as a subproject and which links to Dyn.
In my app I have code like:
import Cocoa
import Dyn
...
SomeDynClass().doSomething()
However, when I try to compile I get an error when I import Dyn. The error is
error: missing required module ‘Stat'
It appears my app can find my framework just fine, but it somehow needs to find a module for my static library, too?
Stat has a module file that’s pretty basic:
module Stat {
header "Stat.h"
export *
}
I think I need to point my Mac app’s framework search paths at Stat but I don’t know why and I don’t know how. How do I solve this?
Select your Target, then go into Build Settings and set the Import Paths within the Swift Compiler - Search Paths section:
${SRCROOT}/Stat
Normally the module would be named the same as the library, however, I'm not sure how you've setup the directory with the module.map (it could be named Dyn perhaps, in which case the Import Path would reflect that name.
Build Settings > Swift Compiler > Search Paths:
${SRCROOT}/(directory with module.map) should resolve itself once
you press enter or tab..
I got the same error when in my unit tests project which involves SQLite3 package. After I add the package, unit tests always throw out error saying "missing required module SQLiteObjc"
I had it fixed by toggle "Force Package Info Generation" on and off in my Unit Tests Target's build setting

Building my own NativeScript app made of TypeScript and using external Telerik modules

I am building a simple NativeScript app, and am trying to do it using a TypeScript base code.
I am using Sublime Text 3 under OSX.
I realized by looking at the demo apps that the tns_modules matches the NativeScript repository so I added it to my app/ folder as a Git submodule, and then compiled it (npm i && grunt). Is that the wrong way to integrate these modules?
I then realized that I could not just run a tns emulate android of my app made of .ts files: I had to compile them too. So I set up a Grunt task to do so, but it was not easy to handle the dependencies. I ended up with this Gruntfile.coffee in app/:
module.exports = (grunt) ->
grunt.loadNpmTasks 'grunt-typescript'
grunt.config 'typescript',
build:
src: [
'**/*.ts'
'!*_modules/**'
]
options:
references: [
'tns_modules/bin/dist/definitions/**/*.d.ts'
]
target: 'es5'
sourceMap: false
declaration: false
module: 'commonjs'
noResolve: true
And it works with simple code, e.g. I'm able to extend a module like Observable by writing:
import observable = require("data/observable");
class Activities extends observable.Observable {
//...
}
I then compile with grunt (the .js files are created along with the .ts ones) and run with tns emulate android (with Genymotion emulator).
Is it the right architecture for my development? When I use Telerik Platform, the compilation process is hidden so I'm not sure I'm doing it right.
And now I'm trying to use Telerik's side-bar module directly in a page's XML file, the way they do it:
<Page xmlns="http://www.nativescript.org/tns.xsd" loaded="pageLoaded" xmlns:tsb="./tns_modules/bin/dist/apps/TelerikNEXT/TelerikUI/side-bar">
<tsb:SideBar title="MyApp">
...
But I get this error:
E/TNS.Native( 2456): TypeError: Cannot read property 'android' of undefined
E/TNS.Native( 2456): File: "/data/data/org.nativescript.scmobile/files/app/./tns_modules/bin/dist/apps/TelerikNEXT/TelerikUI/side-bar, line: 39, column: 39
Which corresponds to:
this._android = new com.telerik.android.primitives.widget.sidedrawer.RadSideDrawer(this._context);
Any idea how I should include these modules? Note that I'm new to mobile dev.
The sidebar, which they're using in the example, is a (payed) controller from Telerik.
As such, it needs to be downloaded and added with tns library add {ios|android} /path/to/the/sidebar.
This command will read project.properties file from the specified shared library folder and will add a reference to it in your project. If in turn the shared library has references to other projects then these projects will be included recursively. As a result, this will create a new folder lib which is sibling to already existing app and platforms.
http://docs.nativescript.org/runtimes/android/external-libs/resource-libs

Resources