Skip to content

Commit d87ae36

Browse files
committed
feat: ios native code mention about modulemap files
1 parent 6348ab7 commit d87ae36

1 file changed

Lines changed: 106 additions & 32 deletions

File tree

  • content/guide/native-code

‎content/guide/native-code/ios.md‎

Lines changed: 106 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ contributors:
77
- vallemar
88
---
99

10-
You can add Objective-C/Swift source files to `App_Resources/iOS/src`. For Objective-C files, create a `.modulemap` file. To add a [CocoaPod](https://guides.cocoapods.org/using/getting-started.html), edit `App_Resources/iOS/Podfile`:
10+
You can add Objective-C/Swift source files to `App_Resources/iOS/src`. Objective-C files also need a [`module.modulemap`](#_2-add-a-module-modulemap) file that lists their headers. To add a [CocoaPod](https://guides.cocoapods.org/using/getting-started.html), edit `App_Resources/iOS/Podfile`:
1111

1212
```bash
1313
App_Resources/
1414
├─ iOS/
1515
│ ├─ src/
16-
│ │ ├─ Shimmer.swift
16+
│ │ ├─ HelloSwift.swift
1717
│ │ ├─ Shimmer.h
1818
│ │ ├─ Shimmer.m
1919
│ │ └─ module.modulemap
@@ -23,7 +23,7 @@ App_Resources/
2323

2424
### Adding Swift code
2525

26-
Define the swfit file in `App_Resources/iOS/src`.
26+
Define the Swift file in `App_Resources/iOS/src`.
2727

2828
```swift
2929
// HelloSwift.swift
@@ -69,51 +69,125 @@ class HelloSwift: NSObject {
6969
}
7070
```
7171

72-
<!---
73-
TODO: add Objective-C code
7472
### Adding Objective-C code
7573

76-
Define the Objective-C code within the path `App_Resources/iOS/src`.
74+
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.
7775

78-
### Create extension
79-
```swift
80-
// UIViewExtension.swift
81-
import UIKit
82-
83-
extension UIView {
84-
@objc func removeAllSubViews() {
85-
for view in self.subviews {
86-
view.removeFromSuperview()
87-
}
88-
}
89-
}
90-
```
76+
#### 1. Add the header and implementation files
9177

9278
```objc
93-
// UIView+Extension.h
79+
// Shimmer.h
9480
#import <UIKit/UIKit.h>
9581

96-
@interface UIView (SDExtension)
82+
@interface Shimmer : NSObject
9783

98-
- (void) removeAllSubViews;
84+
+ (void)startOnView:(UIView *)view;
85+
+ (void)stopOnView:(UIView *)view;
9986

10087
@end
10188
```
10289
10390
```objc
104-
// UIView+Extension.m
105-
#import "UIView+SDExtension.h"
106-
107-
@implementation UIView (SDExtension)
91+
// Shimmer.m
92+
#import "Shimmer.h"
93+
94+
@implementation Shimmer
95+
96+
+ (void)startOnView:(UIView *)view {
97+
CAGradientLayer *gradient = [CAGradientLayer layer];
98+
gradient.frame = view.bounds;
99+
gradient.startPoint = CGPointMake(0, 0.5);
100+
gradient.endPoint = CGPointMake(1, 0.5);
101+
gradient.colors = @[
102+
(id)[UIColor colorWithWhite:1 alpha:0.4].CGColor,
103+
(id)[UIColor whiteColor].CGColor,
104+
(id)[UIColor colorWithWhite:1 alpha:0.4].CGColor,
105+
];
106+
107+
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"locations"];
108+
animation.fromValue = @[@-1.0, @-0.5, @0.0];
109+
animation.toValue = @[@1.0, @1.5, @2.0];
110+
animation.duration = 1.2;
111+
animation.repeatCount = HUGE_VALF;
112+
[gradient addAnimation:animation forKey:@"shimmer"];
113+
114+
view.layer.mask = gradient;
115+
}
108116
109-
- (void) removeAllSubViews
110-
{
111-
for (int i = 0; i < [[self subviews] count]; i++ ) {
112-
[[[self subviews] objectAtIndex:i] removeFromSuperview];
113-
}
117+
+ (void)stopOnView:(UIView *)view {
118+
view.layer.mask = nil;
114119
}
115120
116121
@end
117122
```
118123

119-
-->
124+
#### 2. Add a `module.modulemap`
125+
126+
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.
127+
128+
**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:
129+
130+
```
131+
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.
132+
```
133+
134+
Create a file named exactly `module.modulemap` at the root of `App_Resources/iOS/src`, next to your files:
135+
136+
```bash
137+
App_Resources/
138+
└─ iOS/
139+
└─ src/
140+
├─ Shimmer.h
141+
├─ Shimmer.m
142+
└─ module.modulemap
143+
```
144+
145+
For the `Shimmer` example, it contains:
146+
147+
```
148+
// module.modulemap
149+
module Shimmer {
150+
header "Shimmer.h"
151+
export *
152+
}
153+
```
154+
155+
Each line does the following:
156+
157+
- `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).
158+
- `header "Shimmer.h"`: a header to include in the module. The path is relative to the `module.modulemap` file. List **only `.h` files**, never `.m` files. Classes that are only declared in a `.m` file are not exposed.
159+
- `export *`: re-exports everything that the listed headers import (here, `UIKit`), so that types like `UIView` in your method signatures resolve correctly.
160+
161+
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:
162+
163+
```
164+
module LocalModule {
165+
header "Shimmer.h"
166+
header "views/GradientView.h"
167+
export *
168+
}
169+
```
170+
171+
::: tip Note
172+
Swift files do **not** need to be listed in a module map. Only Objective-C headers do.
173+
174+
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](/guide/adding-native-code).
175+
:::
176+
177+
#### 3. Use it from JavaScript/TypeScript
178+
179+
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`:
180+
181+
```ts
182+
import { EventData, View } from '@nativescript/core'
183+
184+
export function onTap(args: EventData) {
185+
const view = args.object as View
186+
Shimmer.startOnView(view.ios)
187+
}
188+
```
189+
190+
To get type checking and autocomplete for `Shimmer`, [generate TypeScript typings](/guide/native-code/generate-typings) for it. Until you do, you can declare it yourself with `declare const Shimmer: any`.
191+
192+
If the class is still `undefined` after you add or change the module map, run `ns clean` and build again.
193+

0 commit comments

Comments
 (0)