Introduction to React Native Swift
The following article provides an outline for React Native Swift. As we all know how the iOS users are increasing worldwide and the need for interactive applications are at all time high and the React Native developers community are very much excited to build iOS applications with awesome user Interface and flexibility, but with great ambitions comes some difficulties. Whenever we are developing an application using the native modules and along with 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 which works as a property finder and it helps in finding houses for rent or for buying.
React Native Swift Based Property Finderf
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 made a project on a Property Finder which finds houses for rent or for buying. This project can be a very good example for the beginners who are looking to connect the 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 will also help the users to buy iOS phones.
Recommended Articles
This is a guide to React Native Swift. Here we discuss the introduction to react native swift with property finder respectively. You may also have a look at the following articles to learn more –