poniedziałek, 11 stycznia 2016

iOS & React Native

Nie znam React, to chyba React Native nie dla mnie…  Tak myślałem jakiś czas temu, ale dziś zapoznałem się w pewnej mierze z tym frameworkiem i muszę przyznać, że może się podobać. Upraszcza w znaczny sposób pewne standardowe rzeczy.  W dodatku zachęciło mnie to do samego Reacta, który - jeśli odpowiada w pewnym stopniu Native Reactowi - nie powinien wydawać się taki zły, a może być naprawdę wygodny i przyjemny. Jedynie samodzielne opakowywanie natywnego API w Objective-C nie wydaje się już do końca takie wygodne (brakuje mi czegoś na kształt automatycznej projekcji języka jak w WinRT).

 

No HTML, 100% native

debug w Chrome developer tools

stylowanie w składni bardzo podobnej do CSS

layout za pomocą FlexBox

narzędzia: Xcode, Homebrew

brew install node

brew install –HEAD watchman

brew install flow

npm install –g react-native-cli

react-native init GithubBrowser

otwieramy wygenerowany projekt Xcode

index.ios.js

‘use strict’;

var React = require(‘react-native’);

var {

AppRegistry,

StyleSheet,

Text,

View,

}    = React

var GithubBrowser = React.createClass({

render: function() {

       var msg = ‘abc’;

       return (

            <View style={styles.container}>

                    <Text style={styles.welcome}>

                              {msg}

                     </Text>

             </View>

      );

   }

});

var styles = StyleSheet.create({

        container:  {

             flex: 1,

             …

        },

        welcome: {

             fontSize: 20,

             textAlign:  ‘center’,

             margin:  10,

        },

       …

});

AppRegistry.registerComponent(‘GithubBrowser’, () => GithubBrowser);   

github Samples

npm –i

plik komponentu Login: 

var Text = React.Text;

… <Text style={{color: ‘white’}}>

module.exports =  Login;

inny view:

<Login>

 

<Image … source={require(‘image!Octocat’)}    //obrazki w Images.xcassets w Xcode

“styl”:  width, height - tylko liczby (nie piksele)

<TextInput … />

przycisk:

<TouchableHighlight … >

<Text …>

        Log in

</Text>

</TouchableHighlight>

 

ES6

class Login extends Component {

      constructor(props) {

             super(props);

             this.state = {

                     showProgress: false

             }

      }                

render() {

        return …

}

}

<TextInput  onChangeText={(text) =>this.setState({username: text)}

<TouchableHighlight onPress={this.onLoginPressed.bind(this)}

onLoginPressed() {

}

Progress

<ActivityIndicatorIOS  animating={this.state.showProgress}  size=”large” />

 

HTTP

fetch(‘http://…’)

.then((response)=> {

return response.json();

})

.then((results) => {

});

 

Autentykacja

var buffer = require(‘buffer’);

var b = new buffer.Buffer(‘hello’);

//b.toString(‘base64’);

 

---

var buffer = require(‘buffer’);

class AuthService {

login(creds, cb) {

     …

}

}

module.exports = new AuthService();

---

var authService = require(‘./AuthService’);

authService.login({

username: this.state.username,

password: this.state.password

}, (results)=> {

this.setState(Object.assign({

       showProgress: false

}, results));

});

 

przejście do innego widoku:

render: 

if(this.state.isLoggedIn)

return …

else

return …

//początkowy stan komponentu/widoku

getInitialState: function() {

return {

       isLoggedIn: false

};

}

 

dane:

AsyncStorage.setItem()

AsyncStorage.multiSet()

 

var AsyncStorage = require(‘react-native’).AsyncStorage;

AsyncStorage.multiSet([

[‘auth’, encodedAuth],

[‘user’, JSON.stringify(results]]

], …)

odczyt

npm install lodash –save

var _ = require(‘loadash’);

getAuthInfo(cb) {

AsyncStorage.multiGet([authKey, userKey], (err, val) => {

       if (err) {

                return cb(err);

       }

       if(!val) {

                return cb();

       }

       var zippedObj = _.zipObject(val);

       if (!zippedObj[authKey]) { return cb(); }

      var authInfo = {

              header: {

                     Authorization: ‘Basic ‘ + zippedObj[authKey]

              },

             user:  JSON.parse(zippedObj[userKey])

      }

      return cb(null, authInfo);

});

}

componentDidMount:  function() {

AuthService.getAuthInfo((err, authInfo)=> {

this.setState({

         …

         isLoggedIn: authInfo != null

})

})

},

 

Zakładki:

this.state = {

selectedTab: ‘feed’

}

<TabBarIOS  …>

<TabBarIOS.Item

          title=”Feed”

          selected={this.state.selectedTab == ‘feed’}

          icon={require(‘image!inbox’)}

          onPress={()=>this.setState({selectedTab: ‘feed’})}

     >

       <Text …>Tab 1</Text>

</TabBarIOS.Item>

</TabBarIOS>

 

listview

constructor:

var ds = new ListView.DataSource({

rowHasChanged:  (r1, r2) => r1 != r2

});

this.state = {

dataSource: ds.cloneWithRows([‘A’, ‘B’, ‘C’])

};

renderRow(rowData) {

return <Text …>

                   {rowData}

              </Text>

}

<ListView

dataSource={this.state.dataSource}

renderRow={this.renderRow.bind(this)} />

fetch(url, {

headers:  authInfo.header

})

.then((response) => response.json())

.then((responseData) => {

var feedItems = responseData.filter((ev) => ev.type == ‘PushEvent’);

this.setState({

        dataSource:   this.state.dataSource.cloneWithRows(feedItems)

});

 

<Image source={{uri:  rowData.actor.avatar_url}}

 

Navigator

<TouchableHighlight

      onPress={()=> this.pressRow(rowData)}

underlayColor=’#ddd’

>   //cały szablon itemu

</TouchableHighlight>

<TabBarIOS.Item  …>

<NavigatorIOS  …

          initialRoute={{

                 component:  Feed,

                  title:  ‘Feed’

           }}

pressRow(rowData) {

this.props.navigator.push({

        title:  ‘Push Event’,

        component:  PushPayload,

        passProps:  {

              pushEvent:  rowData

        }

});

}

 

<ListView

contentInset={{ top: –50 }}

 

Wywoływanie kodu Objective-C:

Encoding.h:

#include “RTCBridgeModule.h”

@interface Encoding: NSObject <RCTBridgeModule>

@end

Encoding.m:

#include “Encoding.h”

#include “RCTRootView.h”

      @implementation Encoding

RCT_EXPORT_MODULE();

RCT_EXPORT_METHOD(base64Encode: (NSString*)str  callback: (RCTResponseSenderBlock)callback)

{

       NSData *nsdata = [str dataUsingEncoding:NSUTF8StringEncoding];

       NSString *base64Encoded = [nsdata base64EncodedStringWithOptions: 0];

       callback(@[base64Encoded]);

}

@end

JS:

var encoding = require(‘NativeModules’).Encoding;

encoding.base64Encode(authStr, (encodedAuth) => { … });

Brak komentarzy: