Advanced Concepts
Adding ObjectiveC/Swift Code to an application
You can add Objective-C/Swift source files to App_Resources/iOS/src. Objective-C files also need a module.modulemap file that lists their headers. To add a CocoaPod, edit App_Resources/iOS/Podfile:
App_Resources/
├─ iOS/
│ ├─ src/
│ │ ├─ HelloSwift.swift
│ │ ├─ Shimmer.h
│ │ ├─ Shimmer.m
│ │ └─ module.modulemap
│ └─ Podfile
└─ ... moreAdding Swift code
Define the Swift file in App_Resources/iOS/src.
// HelloSwift.swift
import UIKit
class HelloSwift: NSObject {
@objc public var stringToReturn: String = "Hello from Swift!"
@objc public func getString() -> String {
return stringToReturn;
}
}Given the example above, your JavaScript or TypeScript code can reference the Swift code by using the full class name:
const helloSwift = new HelloSwift()
helloSwift.stringToReturn = 'Custom hello from Swift!'
console.log(helloSwift.getString())
// prints: Custom hello from Swift!Using @objc
@objc allows exposing variables and functions to use them from JS/TS, note that in the example the variables and methods have this notation.
Using @objcMembers
A shortcut to the @objc notation is to use the @objcMembers notation at the class level to make the entire class accessible.
// HelloSwift.swift
import UIKit
@objcMembers
class HelloSwift: NSObject {
public var stringToReturn: String = "Hello from Swift!"
public func getString() -> String {
return stringToReturn;
}
}Adding Objective-C code
Objective-C code needs one more file than Swift: a module.modulemap. The example below adds a Shimmer class written in Objective-C to App_Resources/iOS/src and makes it callable from JavaScript/TypeScript.
1. Add the header and implementation files
// Shimmer.h
#import <UIKit/UIKit.h>
@interface Shimmer : NSObject
+ (void)startOnView:(UIView *)view;
+ (void)stopOnView:(UIView *)view;
@end// Shimmer.m
#import "Shimmer.h"
@implementation Shimmer
+ (void)startOnView:(UIView *)view {
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = view.bounds;
gradient.startPoint = CGPointMake(0, 0.5);
gradient.endPoint = CGPointMake(1, 0.5);
gradient.colors = @[
(id)[UIColor colorWithWhite:1 alpha:0.4].CGColor,
(id)[UIColor whiteColor].CGColor,
(id)[UIColor colorWithWhite:1 alpha:0.4].CGColor,
];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"locations"];
animation.fromValue = @[@-1.0, @-0.5, @0.0];
animation.toValue = @[@1.0, @1.5, @2.0];
animation.duration = 1.2;
animation.repeatCount = HUGE_VALF;
[gradient addAnimation:animation forKey:@"shimmer"];
view.layer.mask = gradient;
}
+ (void)stopOnView:(UIView *)view {
view.layer.mask = nil;
}
@end2. Add a module.modulemap
A module map is a small text file, used by the Clang compiler, that groups a set of Objective-C header files into a named module. NativeScript reads the module map when it builds your app to find out which Objective-C classes, methods and properties exist. It uses that information to generate the metadata that lets JavaScript call your code.
Without a module map, your Objective-C code is still compiled into the app, but JavaScript cannot see it. The class will be undefined at runtime, and the build log will contain this warning:
warning: Directory .../App_Resources/iOS/src with native iOS source code doesn't contain a modulemap file. Metadata for it will not be generated and it will not be accessible from JavaScript.Create a file named exactly module.modulemap at the root of App_Resources/iOS/src, next to your files:
App_Resources/
└─ iOS/
└─ src/
├─ Shimmer.h
├─ Shimmer.m
└─ module.modulemapFor the Shimmer example, it contains:
// module.modulemap
module Shimmer {
header "Shimmer.h"
export *
}Each line does the following:
module Shimmer { ... }: declares a module. The name can be anything that is unique in your app, and it does not need to match a class name. You never import this name in JavaScript, because the classes become globals (see step 3).header "Shimmer.h": a header to include in the module. The path is relative to themodule.modulemapfile. List only.hfiles, never.mfiles. Classes that are only declared in a.mfile are not exposed.export *: re-exports everything that the listed headers import (here,UIKit), so that types likeUIViewin your method signatures resolve correctly.
When you add more Objective-C classes, add one header line per header file to the same module. Headers in subfolders use a relative path:
module LocalModule {
header "Shimmer.h"
header "views/GradientView.h"
export *
}Note
Swift files do not need to be listed in a module map. Only Objective-C headers do.
The ns native add objective-c <ClassName> command creates the .h and .m files and adds the header to App_Resources/iOS/src/module.modulemap for you, using a module named LocalModule. See Adding custom native code.
3. Use it from JavaScript/TypeScript
After a rebuild, the class is available as a global. Objective-C selectors are mapped to JavaScript method names by dropping the colons, so startOnView: becomes startOnView:
import { EventData, View } from '@nativescript/core'
export function onTap(args: EventData) {
const view = args.object as View
Shimmer.startOnView(view.ios)
}To get type checking and autocomplete for Shimmer, generate TypeScript typings for it. Until you do, you can declare it yourself with declare const Shimmer: any.
If the class is still undefined after you add or change the module map, run ns clean and build again.
- Previous
- Adding Java/Kotlin Code



