Updated May 15, 2023
Introduction to React Native Swift
The following article provides an outline for React Native Swift. As we all know, how the number of iOS users is increasing worldwide, and the need for interactive applications is at an all-time high; the React Native developers community is very much excited to build iOS applications with awesome user Interfaces and flexibility, but with grand ambitions come some difficulties. Whenever we are developing an application using the native modules and it accessing the iOS native libraries, it becomes a hectic task. It can be done quite easily using Objective C and Swift.
Here we will go through a project that works as a property finder and helps find houses for rent or buying.
React Native Swift-Based Property Finder
Given below is the react native swift-based property finder:
Components inside src folder:
- .babelrc
- .buckconfig
- .flowconfig
- .gitattributes
- .gitignore
- .watchmanconfig
- App.js
- SearchPage.js
- SearchResults.js
- app.json
- index.js
- ios folder
- tests folder
Components inside iosfolder:
- PropertyFinderfolder
- PropertyFinder-tvOSfolder
- PropertyFinder-tvOSTestsfolder
- xcodeprojfolder
- PropertyFinderTests folder
Components inside PropertyFinderfolder:
- Base.lproj folder
- Images.xcassetsfolder
- AppDelegate.h
- AppDelegate.m
- Info.plist
- main.m
Components inside Base.lproj folder:
- LaunchScreen.xib
Components inside Images.xcassets folder:
- Contents.json
- AppIconset folder
Components inside AppIcon.appiconsetfolder:
- Contents.json
Components inside PropertyFinder-tvOS folder:
- Info.plist
Components inside PropertyFinder.xcodeproj folder:
- xcshareddata folder
- project.pbxproj
Components inside xcshareddata folder:
- xcschemes folder
Components inside xcschemesfolder:
- PropertyFinder-tvOS.xcscheme
- PropertyFinder.xcscheme
Components inside PropertyFinderTests folder:
- Info.plist
- PropertyFinder.m
1. .babelrc
{
"presets": ["react-native"]
}
2. .buckconfig
{}
3. .flowconfig
{}
4. .gitattributes
{}
5. .gitignore
{}
6. .watchmanconfig
{}
7. App.js
'use strict';
import React, { Component } from 'react'; import {
StyleSheet, NavigatorIOS,
} from 'react-native';
import SearchPage from './SearchPage';
export default class App extends Component<{}>{ render(){
return(
<NavigatorIOSstyle={styles.container} initialRoute={{
title: 'Property Finder', component: SearchPage,
}}/>
);
}
}
const styles = StyleSheet.create({ container: {
flex: 1,
},
});
8. SearchPage.js
'use strict';
import React, { Component } from 'react'; import {
StyleSheet, Text, TextInput, View, Button,
ActivityIndicator, Image,
} from 'react-native';
import SearchResults from './SearchResults';
function urlForQueryAndPage(key, value, pageNumber) { const data = {
country: 'uk',
pretty: '1', encoding: 'json', listing_type: 'buy',
action: 'search_listings', page: pageNumber,
};
data[key] = value;
constquerystring = Object.keys(data)
.map(key => key + '=' + encodeURIComponent(data[key]))
.join('&');
return 'https://api.nestoria.co.uk/api?' + querystring;
}
export default class SearchPage extends Component<{}> { constructor(props) {
super(props); this.state ={
searchString: 'london', isLoading: false, message:'',
};
}
_onSearchTextChanged = (event) => {
this.setState({ searchString: event.nativeEvent.text });
};
_executeQuery = (query) =>{ console.log(query); this.setState({ isLoading: true }); fetch(query)
.then(response =>response.json())
.then(json => this._handleResponse(json.response))
.catch(error =>
this.setState({
isLoading: false,
message: 'It is bad,please check ' + error
}));
};
_handleResponse = (response) => {
this.setState({ isLoading: false , message: '' });
if (response.application_response_code.substr(0, 1) === '1') { this.props.navigator.push({
title: 'Results', component: SearchResults,
passProps: {listings: response.listings}
});
} else {
this.setState({ message: 'Location unable to find; kindly try again.'});
}
};
_onSearchPressed = () => {
const query = urlForQueryAndPage('place_name', this.state.searchString,
1);
this._executeQuery(query);
};
render() {
const spinner = this.state.isLoading ?
<ActivityIndicator size='large'/> : null; return (
<View style={styles.container}>
<Text style={styles.description}>
Search for houses to buy!
</Text>
<Text style={styles.description}> Search by place-name or postcode.
</Text>
<View style={styles.flowRight}>
<TextInput style={styles.searchInput} value={this.state.searchString}
onChange={this._onSearchTextChanged} placeholder='Search via name or postcode'/>
<Button onPress={this._onSearchPressed} color='#48BBEC'
title='Go'
/>
</View>
<Image source={require('./Resources/house.png')} style={styles.image}/>
{spinner}
<Text style={styles.description}>{this.state.message}</Text>
</View>
);
}
}
const styles = StyleSheet.create({ description: {
marginBottom: 20,
fontSize: 18,
textAlign: 'center',
color: '#656565'
},
container: { padding: 30,
marginTop: 65, alignItems: 'center'
},
flowRight: { flexDirection: 'row', alignItems: 'center', alignSelf: 'stretch',
},
searchInput: { height:36,
padding:4,
marginRight: 5,
flexGrow: 1,
fontSize: 18,
borderWidth: 1, borderColor: '#48BBEC', borderRadius: 8,
color: '#48BBEC',
},
image: { width: 217,
height: 138,
},
});
9. SearchResults.js
'use strict';
import React, { Component } from 'react' import {
StyleSheet, Image, View,
TouchableHighlight, FlatList,
Text,
} from 'react-native';
class ListItem extends React.PureComponent {
_onPress = () =>{ this.props.onPressItem(this.props.index);
}
render() {
const item = this.props.item;
const price = item.price_formatted.split(' ')[0]; return (
<TouchableHighlightonPress={this._onPress} underlayColor='#dddddd'>
<View>
<View style={styles.rowContainer}>
<Image style={styles.thumb} source={{ uri: item.img_url }} />
<View style={styles.textContainer}>
<Text style={styles.price}>{price}</Text>
<Text style={styles.title}
numberOfLines={1}>{item.title}</Text>
</View>
</View>
<View style={styles.separator}/>
</View>
</TouchableHighlight>
);
}
}
export default class SearchResults extends Component<{}> {
_keyExtractor = (item, index) => index;
_renderItem = ({item, index}) => (
<ListItem item={item} index={index}
onPressItem={this._onPressItem}
/>
);
_onPressItem = (index) =>{ console.log("Pressed row: "+index);
};
render() { return (
<FlatList data={this.props.listings} keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
const styles = StyleSheet.create({ thumb: {
width: 80,
height: 80,
marginRight: 10
},
textContainer: { flex: 1
},
separator: { height: 1,
backgroundColor: '#dddddd'
},
price: { fontSize: 25,
fontWeight: 'bold', color: '#48BBEC'
},
title: { fontSize: 20,
color: '#656565'
},
rowContainer: { flexDirection: 'row',
padding: 10
},
});
10. app.json
{
"name": "PropertyFinder", "displayName": "PropertyFinder"
}
11. index.js
import { AppRegistry } from 'react-native';
import App from './App';
AppRegistry.registerComponent('PropertyFinder', () => App);
12. App.js ( tests)
import 'react-native'; import React from 'react'; import App from '../App';
import renderer from 'react-test-renderer';
it('renders correctly', () => { const tree = renderer.create(
<App />
);
});
13. AppDelegate.h(PropertyFinder)
@property (nonatomic, strong) UIWindow *window;
@end
14. AppDelegate.m(PropertyFinder)
#import "AppDelegate.h"
#import <React/RCTBundleURLProvider.h> #import <React/RCTRootView.h>
@implementation AppDelegate
- (BOOL)application:(UIApplication*)applicationdidFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSURL *jsCodeLocation;
jsCodeLocation = [[RCTBundleURLProvidersharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil];
RCTRootView *rootView = [[RCTRootViewalloc] initWithBundleURL:jsCodeLocation
moduleName:@"PropertyFinder"
initialProperties:nil
launchOptions:launchOptions];
self.window = [[UIWindowalloc] initWithFrame:[UIScreenmainScreen].bounds];
UIViewController *rootViewController = [UIViewController new];
self.window.rootViewController = rootViewController; [self.windowmakeKeyAndVisible];
return YES;
}
@end
15. Info.plist(PropertyFinder)
{}
16. main.m(PropertyFinder)
#import<UIKit/UIKit.h>
#import"AppDelegate.h"
intmain(intargc, char * argv[]) { @autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
17. Contents. json(Images.xcassets)
{
"info" : {
"version" : 1,
"author" : "xcode"
}
}
18. json(AppIcon.appiconset)
"idiom" : "iphone",
"size" : "29x29",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "29x29",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "40x40",
"scale" : "3x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "2x"
},
{
"idiom" : "iphone",
"size" : "60x60",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
19. Info.plist(PropertyFinder-tvOS)
{}
20. Info.plist(PropertyFinder-tvOSTests)
{}
21. project.pbxproj(PropertyFinder.xcodeproj)
{}
22. PropertyFinder-tvOS.xcscheme (xcschemes)
{}
23. PropertyFinder.xcscheme (xcschemes)
{}
24 Info.plist (PropertyFinderTests)
{}
25. PropertyFinderTests.m(PropertyFinderTests)
#import <UIKit/UIKit.h> #import <XCTest/XCTest.h>
#import <React/RCTLog.h> #import <React/RCTRootView.h>
#define TIMEOUT_SECONDS 600
#define TEXT_TO_LOOK_FOR @"Welcome to React Native!"
@interface PropertyFinderTests :XCTestCase
@end
@implementation PropertyFinderTests
- (BOOL)findSubviewInView:(UIView*)view matching:(BOOL(^)(UIView
*view))test
{
if (test(view)) { return YES;
}
for (UIView
*
subview in [view subviews]) {
if ([self findSubviewInView:subviewmatching:test]) { return YES;
}
}
return NO;
}
- (void)testRendersWelcomeScreen
{
NSDate *date = [NSDatedateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; BOOL foundElement = NO;
block NSString *redboxError = nil;
RCTSetLogFunction(^(NSString *message, NSNumber *lineNumber, NSString
*fileName, RCTLogSource source, RCTLogLevel level) { if (level >= RCTLogLevelError) {
redboxError = message;
}
});
while ([date timeIntervalSinceNow] > 0 && !foundElement&& !redboxError)
{
[[NSRunLoopmainRunLoop] runMode:NSDefaultRunLoopModebeforeDate:[NSDatedateWithTimeIntervalSinceNow:0.1]];
[[NSRunLoopmainRunLoop]runMode:NSRunLoopCommonModesbeforeDate:[NSDatedateWithTimeIntervalSinceNow:0.1]];
{
if ([view.accessibilityLabelisEqualToString:TEXT_TO_LOOK_FOR]) { return YES;
}
return NO;
}];
}
RCTSetLogFunction(RCTDefaultLogFunction);
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); XCTAssertTrue(foundElement, @"Sorry! Unable to find such element'%@' in
%d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
}
Output:
Conclusion
In this article, we did a project on a Property Finder, which finds houses for rent or buying. This project can be a perfect example for beginners looking to connect native modules to Swift. We can explore even more with Swift and React Native. By connecting the native modules with Swift, we can easily make more interactive, flexible, and advanced apps for iOS. The apps built with this can enhance the user experience and help users buy iOS phones.
Recommended Articles
We hope that this EDUCBA information on “React Native Swift” was beneficial to you. You can view EDUCBA’s recommended articles for more information.