Skip to content

Instantly share code, notes, and snippets.

@samerce
Last active November 14, 2016 12:32
Show Gist options
  • Save samerce/0f5072a078894b159559399547553a4c to your computer and use it in GitHub Desktop.
Save samerce/0f5072a078894b159559399547553a4c to your computer and use it in GitHub Desktop.
An implementation for React Native's Settings component.
/*
@providesModule RNSettings
@flow
*/
import {
DeviceEventEmitter,
NativeModules,
} from 'react-native';
const {RNSettings} = NativeModules;
let subscriptions: {[id: string]: {keys: Array<string>, callback: ?Function}} = {};
let nextId = 0; // monotonically increasing id
const Settings = {
_settings: RNSettings && RNSettings.settings,
get(key: string): mixed {
return this._settings[key];
},
set(settings: Object) {
this._settings = Object.assign(this._settings, settings);
RNSettings.setValues(this._settings);
},
watchKeys(keys: string | Array<string>, callback: Function): number {
if (typeof keys === 'string') {
keys = [keys];
}
const watchId = nextId++;
subscriptions[watchId] = {keys, callback};
return watchId;
},
clearWatch(watchId: number) {
if (subscriptions[watchId]) {
delete subscriptions[watchId];
}
},
_sendObservations(body: Object) {
Object.keys(body).forEach((key) => {
const newValue = body[key];
const didChange = this._settings[key] !== newValue;
this._settings[key] = newValue;
if (didChange) {
Object.keys(subscriptions).forEach((subId) => {
const sub = subscriptions[subId];
if (sub.keys.includes(key) && sub.callback) {
sub.callback();
}
});
}
});
},
};
DeviceEventEmitter.addListener(
'settingsUpdated',
Settings._sendObservations.bind(Settings)
);
export default Settings;
package com.iodine.start;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.ReadableMapKeySetIterator;
import com.facebook.react.bridge.ReadableType;
import com.facebook.react.modules.core.DeviceEventManagerModule.RCTDeviceEventEmitter;
import java.util.Map;
import java.util.HashMap;
import java.util.Iterator;
import org.json.JSONArray;
import org.json.JSONObject;
import org.json.JSONException;
import android.content.SharedPreferences;
public class RNSettingsModule extends ReactContextBaseJavaModule implements SharedPreferences.OnSharedPreferenceChangeListener {
private SharedPreferences _settings;
public RNSettingsModule(ReactApplicationContext reactContext) {
super(reactContext);
this._settings = reactContext.getSharedPreferences(YOUR_PREFS_KEY, Context.MODE_PRIVATE);
this._settings.registerOnSharedPreferenceChangeListener(this);
}
protected void finalize() {
this._settings.unregisterOnSharedPreferenceChangeListener(this);
}
@Override
public String getName() {
return "RNSettings";
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("settings", this._settingsToMap());
return constants;
}
@ReactMethod
public void setValues(ReadableMap values) throws JSONException {
SharedPreferences.Editor editor = this._settings.edit();
ReadableMapKeySetIterator iterator = values.keySetIterator();
while (iterator.hasNextKey()) {
String key = iterator.nextKey();
ReadableType type = values.getType(key);
switch (type) {
case Null:
editor.putString(key, null);
break;
case Boolean:
editor.putBoolean(key, values.getBoolean(key));
break;
case Number:
// Since SharedPreferences does not support storing doubles,
// we store the raw long bits to preserve precision.
double number = values.getDouble(key);
editor.putLong(key, Double.doubleToRawLongBits(number));
break;
case String:
editor.putString(key, values.getString(key));
break;
case Map:
editor.putString(key, MapUtil.toJSONObject(values.getMap(key)).toString());
break;
case Array:
editor.putString(key, ArrayUtil.toJSONArray(values.getArray(key)).toString());
break;
}
editor.commit();
}
}
private Map<String, Object> _settingsToMap() {
Iterator iterator = this._settings.getAll().entrySet().iterator();
Map<String, Object> map = new HashMap<>();
while (iterator.hasNext()) {
Map.Entry pair = (Map.Entry)iterator.next();
String key = (String) pair.getKey();
Object value = pair.getValue();
try {
if (value instanceof String) {
if (((String) value).charAt(0) == '[') {
// Try parsing string as JSONArray
Object[] array = ArrayUtil.toArray(new JSONArray((String) value));
map.put(key, array);
} else {
// Try parsing string as JSON
Map<String, Object> object = MapUtil.toMap(new JSONObject((String) value));
map.put(key, object);
}
} else if (value instanceof Long) {
// Parse raw long bits as double
double number = Double.longBitsToDouble((long) value);
map.put(key, number);
} else {
// For all other data types
map.put(key, value);
}
} catch(JSONException e) {
// If string can't be parsed as JSON, we just treat it as a normal string
map.put(key, value);
}
iterator.remove();
}
return map;
}
public void onSharedPreferenceChanged(SharedPreferences sharedPrefs, String key) {
ReactApplicationContext reactContext = this.getReactApplicationContext();
reactContext.getJSModule(RCTDeviceEventEmitter.class)
.emit("settingsUpdated", MapUtil.toWritableMap(this._settingsToMap()));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment