mechanical_sparrow_in_fligh

Swift Views in iOS Native Components, Part 1: UIKit Views

It’s fairly easy to get started using iOS Native components in React Native. At the time of writing, this is the example given in the React Native docs.

#import <MapKit/MapKit.h>
#import <React/RCTViewManager.h>
@interface RNTMapManager : RCTViewManager
@end
@implementation RNTMapManager
RCT_EXPORT_MODULE(RNTMap)
- (UIView *)view
{
return [[MKMapView alloc] init];
}
@end

It’s straightforward – you subclass RCTViewManager and implement a view method that returns a UIView. Obviously you’ll soon want to start creating more custom views. Like this UITextView, for example:

//
// TextViewManager.m
// SwiftViewSchool
//
// Created by Tanner West on 8/16/23.
//
#import <UIKit/UIKit.h>
#import <React/RCTViewManager.h>
@interface RNTTextViewManager : RCTViewManager
@end
@implementation RNTTextViewManager
RCT_EXPORT_MODULE(RNTTextView)
- (UIView *)view
{
UITextView *textView = [[UITextView alloc] initWithFrame:CGRectMake(0, 0, 500, 250)];
textView.text = @"Hello, iOS!";
textView.font = [UIFont systemFontOfSize:48];
textView.textColor = [UIColor purpleColor];
return [textView init];
}
@end

…then we can import our native view in React Native…

import React from 'react';
import {SafeAreaView, Text} from 'react-native';
import {requireNativeComponent} from 'react-native';
const TextView = requireNativeComponent('RNTTextView');
function App(): JSX.Element {
return (
<SafeAreaView style={{flex: 1}}>
<Text>Hello, React Native</Text>
<TextView style={{flex: 1}} />
</SafeAreaView>
);
}
export default App;

And our result will look something like this:

Nice! This is a good start towards learning to build custom components, but if you’re like me, you like to avoid Objective-C as much as possible, and would prefer to use Swift when you write iOS native code. Here’s how we’d do that.

First, let’s create a swift file. I’ll call it TextViewProvider. When you create this file in Xcode, you should be asked whether you’d like to create a bridging header (if your project doesn’t already have one). Answer yes to this; the bridging header is what will make your Swift code available to your Objective-C code.

Our new file will look this this, which creates the same type of UITextView as our original code, but in Swift.

//
// TextViewProvider.swift
// SwiftViewSchool
//
// Created by Tanner West on 8/16/23.
//
import Foundation
import UIKit
@objc class TextViewProvider: NSObject {
@objc func createTextView() -> UITextView {
let textView = UITextView(frame: CGRect(x: 0, y: 0, width: 200, height: 100))
textView.text = "Hello, from Swift World!"
textView.font = UIFont.systemFont(ofSize: 48)
textView.textColor = UIColor.blue
return textView
}
}

…then, in our main RCTViewManager class, we can make a couple of tweaks to get our view from the new swift class:

//
// TextViewManager.m
// SwiftViewSchool
//
// Created by Tanner West on 8/16/23.
//
#import <UIKit/UIKit.h>
#import <React/RCTViewManager.h>
#import "SwiftViewSchool-Swift.h"
@interface RNTTextViewManager : RCTViewManager
@end
@implementation RNTTextViewManager
RCT_EXPORT_MODULE(RNTTextView)
- (UIView *)view
{
TextViewProvider *provider = [[TextViewProvider alloc] init];
UITextView *textView = [provider createTextView];
return textView;
}
@end

Notice how we’ve imported SwiftViewSchool-Swift.h in our file. This is a reference our bridging header, and it’s how our Objective-C code is able to access our Swift class. The naming convention for the import is {YourProjectName}-Swift.h. You can see the header name under Build Settings > Swift Compiler – General.

Now, when we rebuild our app, we should the text view we created in Swift.

Another approach would be to subclass UITextView with our Swift class:

//
// TextViewProvider.swift
// SwiftViewSchool
//
// Created by Tanner West on 8/16/23.
//
import Foundation
import UIKit
@objc class TextViewProvider: UITextView {
override init(frame: CGRect, textContainer: NSTextContainer?) {
super.init(frame: frame, textContainer: textContainer)
common()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
common()
}
private func common() {
text = "Hello, from Swift World!"
font = .systemFont(ofSize: 48)
textColor = .blue
}
}

Posted

in

by