Skip to content

Commit

Permalink
Merge pull request #33 from SaltechSystems/example-updates
Browse files Browse the repository at this point in the history
Example updates
  • Loading branch information
bawelter committed Apr 12, 2020
2 parents 42b92ac + 7a893a2 commit fb0c7ac
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 218 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Expand Up @@ -61,4 +61,8 @@

## 2.7.0+2

* Updated documentation
* Updated documentation

## 2.7.0+3

* Giving a simplified example and advanced example
278 changes: 85 additions & 193 deletions README.md
Expand Up @@ -31,17 +31,18 @@ For help getting started with Flutter, view the

### iOS

| Platform | Minimum OS version |
| -------- | ------------------ |
| iOS | 9.0 |
| Platform | Minimum OS version |
| -------- | ----------------------- |
| iOS | 10.0 (9.0 - DEPRECATED) |

### Android

| Platform | Runtime architectures | Minimum API Level |
| -------- | --------------------- | ----------------- |
| Android | armeabi-v7a | 19 |
| Android | arm64-v8a | 21 |
| Android | x86 | 19 |
| Platform | Runtime architectures | Minimum API Level |
| -------- | --------------------- | ------------------- |
| Android | armeabi-v7a | 22 (19 - DEPRECATED)|
| Android | arm64-v8a | 22 (21 - DEPRECATED)|
| Android | x86 | 22 (19 - DEPRECATED)|
| Android | x86_64 | 22 |

## API References

Expand Down Expand Up @@ -72,212 +73,103 @@ Start Sync Gateway:

## Usage example

*Note*: The protocol ws and not wss is used in the example. The easy way to implement this is to use this attribute to your AndroidManifest.xml where you allow all http for all requests:

```xml
<application android:usesCleartextTraffic="true">
</application>
```

Below is an example for the database using the BLoC pattern ( View <-> BLoC <-> Repository <-> Database )

```dart
class AppDatabase {
AppDatabase._internal();
static final AppDatabase instance = AppDatabase._internal();
String dbName = "myDatabase";
List<Future> pendingListeners = List();
ListenerToken _replicatorListenerToken;
Database database;
Replicator replicator;
Future<bool> login(String username, String password) async {
try {
database = await Database.initWithName(dbName);
// Note wss://10.0.2.2:4984/my-database is for the android simulator on your local machine's couchbase database
ReplicatorConfiguration config =
ReplicatorConfiguration(database, "ws://10.0.2.2:4984/beer-sample");
config.replicatorType = ReplicatorType.pushAndPull;
config.continuous = true;
// Using self signed certificate
//config.pinnedServerCertificate = "assets/cert-android.cer";
config.authenticator = BasicAuthenticator(username, password);
replicator = Replicator(config);
replicator.addChangeListener((ReplicatorChange event) {
if (event.status.error != null) {
print("Error: " + event.status.error);
}
print(event.status.activity.toString());
});
await replicator.start();
return true;
} on PlatformException {
return false;
}
}
Future<void> logout() async {
await Future.wait(pendingListeners);
await replicator.removeChangeListener(_replicatorListenerToken);
_replicatorListenerToken =
replicator.addChangeListener((ReplicatorChange event) async {
if (event.status.activity == ReplicatorActivityLevel.stopped) {
await database.close();
await replicator.removeChangeListener(_replicatorListenerToken);
await replicator.dispose();
_replicatorListenerToken = null;
}
});
await replicator.stop();
}
Future<Document> createDocumentIfNotExists(String id, Map<String, dynamic> map) async {
try {
var oldDoc = await database.document(id);
if (oldDoc != null) return oldDoc;
var newDoc = MutableDocument(id: id, data: map);
if (await database.saveDocument(newDoc)) {
return newDoc;
} else {
return null;
}
} on PlatformException {
return null;
}
}
ObservableResponse<ResultSet> getMyDocument(String documentId) {
final stream = BehaviorSubject<ResultSet>();
// Execute a query and then post results and all changes to the stream
final Query query = QueryBuilder.select([
SelectResult.expression(Meta.id.from("mydocs")).as("id"),
SelectResult.expression(Expression.property("foo").from("mydocs")),
SelectResult.expression(Expression.property("bar").from("mydocs")),
])
.from(dbName, as: "mydocs")
.where(Meta.id.from("mydocs").equalTo(Expression.string(documentId)));
final processResults = (ResultSet results) {
if (!stream.isClosed) {
stream.add(results);
}
};
return _buildObservableQueryResponse(stream, query, processResults);
}
ObservableResponse<T> _buildObservableQueryResponse<T>(
BehaviorSubject<T> subject,
Query query,
ResultSetCallback resultsCallback) {
final futureToken = query.addChangeListener((change) {
if (change.results != null) {
resultsCallback(change.results);
}
});
final removeListener = () {
final newFuture = futureToken.then((token) async {
if (token != null) {
await query.removeChangeListener(token);
}
});
pendingListeners.add(newFuture);
newFuture.whenComplete(() {
pendingListeners.remove(newFuture);
});
};
try {
query.execute().then(resultsCallback);
} on PlatformException {
removeListener();
rethrow;
}
return ObservableResponse<T>(subject, () {
removeListener();
subject.close();
});
}
// Initialize the database
try {
database = await Database.initWithName("gettingStarted");
} on PlatformException {
return "Error initializing database";
}
```
```dart
class ObservableResponse<T> implements StreamController<T> {
ObservableResponse(this._result, [this._onDispose]);
final Subject<T> _result;
final VoidCallback _onDispose;
// Create a new document (i.e. a record) in the database.
var mutableDoc = MutableDocument()
.setDouble("version", 2.0)
.setString("type", "SDK");
@override
void add(data) => _result?.add(data);
@override
ControllerCallback get onCancel => throw UnsupportedError('ObservableResponses do not support cancel callbacks');
// Save it to the database.
try{
await database.saveDocument(mutableDoc);
} on PlatformException {
return "Error saving document";
}
@override
ControllerCallback get onListen => throw UnsupportedError('ObservableResponses do not support listen callbacks');
// Update a document.
mutableDoc = (await database.document(mutableDoc.id))?.toMutable()?.setString("language", "Dart");
@override
ControllerCallback get onPause => throw UnsupportedError('ObservableResponses do not support pause callbacks');
if (mutableDoc != null) {
// Save it to the database.
try {
await database.saveDocument(mutableDoc);
@override
ControllerCallback get onResume => throw UnsupportedError('ObservableResponses do not support resume callbacks');
var document = await database.document(mutableDoc.id);
@override
void addError(Object error, [StackTrace stackTrace]) => throw UnsupportedError('ObservableResponses do not support adding errors');
// Log the document ID (generated by the database)
// and properties
print("Document ID :: ${document.id}");
print("Learning ${document.getString("language")}");
} on PlatformException {
return "Error saving document";
}
}
@override
Future addStream(Stream<T> source, {bool cancelOnError}) => throw UnsupportedError('ObservableResponses do not support adding streams');
// Create a query to fetch documents of type SDK.
var query = QueryBuilder
.select([SelectResult.all().from("mydocs")])
.from("gettingStarted", as: "mydocs")
.where(Expression.property("type").from("mydocs").equalTo(Expression.string("SDK")));
// Run the query.
try {
var result = await query.execute();
print("Number of rows :: ${result.allResults().length}");
} on PlatformException {
return "Error running the query";
}
@override
Future get done => _result?.done ?? true;
// Note wss://10.0.2.2:4984/my-database is for the android simulator on your local machine's couchbase database
// Create replicators to push and pull changes to and from the cloud.
ReplicatorConfiguration config =
ReplicatorConfiguration(database, "ws://10.0.2.2:4984/beer-sample");
config.replicatorType = ReplicatorType.pushAndPull;
config.continuous = true;
@override
bool get hasListener => _result?.hasListener ?? false;
// Add authentication.
config.authenticator = BasicAuthenticator("foo", "bar");
@override
bool get isClosed => _result?.isClosed ?? true;
// Create replicator (make sure to add an instance or static variable named replicator)
var replicator = Replicator(config);
@override
bool get isPaused => _result?.isPaused ?? false;
// Listen to replicator change events.
_listenerToken = replicator.addChangeListener((ReplicatorChange event) {
if (event.status.error != null) {
print("Error: " + event.status.error);
}
@override
StreamSink<T> get sink => _result?.sink;
print(event.status.activity.toString());
});
@override
Stream<T> get stream => _result?.stream;
// Start replication.
await replicator.start();
```

@override
Future<dynamic> close() {
if (_onDispose != null) {
// Do operations here like closing streams and removing listeners
_onDispose();
}
For this getting started guide, you will need to allow using ws protocol.

return _result?.close();
}
As of Android Pie, version 9, API 28, cleartext support is disabled, by default. Although wss: protocol URLs are not affected, in order to use the ws: protocol, applications must target API 27 or lower, or must configure application network security as described [here](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted).

@override
set onCancel(Function() onCancelHandler) => throw UnsupportedError('ObservableResponses do not support cancel callbacks');
```xml
<application android:usesCleartextTraffic="true">
</application>
```

@override
set onListen(void Function() onListenHandler) => throw UnsupportedError('ObservableResponses do not support listen callbacks');
App Transport Security is enabled by default since iOS 9 and enforces applications to use HTTPS exclusively. For this getting started guide, you will disable it but we recommend to enable it in production (and [enable HTTPS on Sync Gateway](https://docs.couchbase.com/sync-gateway/2.7/security.html#connection-to-sync-gateway)). In the Xcode navigator, right-click on Info.plist and open it as a source file.

@override
set onPause(void Function() onPauseHandler) => throw UnsupportedError('ObservableResponses do not support pause callbacks');
Append the following inside of the <dict> XML tags to disable ATS.

@override
set onResume(void Function() onResumeHandler) => throw UnsupportedError('ObservableResponses do not support resume callbacks');
}
```xml
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key><true/>
</dict>
```
9 changes: 8 additions & 1 deletion example/README.md
Expand Up @@ -31,4 +31,11 @@ Start Sync Gateway:

~/Downloads/couchbase-sync-gateway/bin/sync_gateway ~/path/to/sync-gateway-config.json

*Note*: Included in this example is sync-gateway-config.json (Login => u: foo, p: bar)
*Note*: Included in this example is sync-gateway-config.json (Login credentials u: foo / p: bar)

As of Android Pie, version 9, API 28, cleartext support is disabled, by default. Although wss: protocol URLs are not affected, in order to use the ws: protocol, applications must target API 27 or lower, or must configure application network security as described [here](https://developer.android.com/training/articles/security-config#CleartextTrafficPermitted).

```xml
<application android:usesCleartextTraffic="true">
</application>
```
8 changes: 4 additions & 4 deletions example/lib/app.dart → example/lib/beer_sample_app.dart
Expand Up @@ -22,16 +22,16 @@ import 'package:couchbase_lite_example/pages/login.dart';

enum AppMode { production, development }

class MyApp extends StatefulWidget {
MyApp(this.mode);
class BeerSampleApp extends StatefulWidget {
BeerSampleApp(this.mode);

final AppMode mode;

@override
_MyAppState createState() => _MyAppState();
_BeerSampleAppState createState() => _BeerSampleAppState();
}

class _MyAppState extends State<MyApp> {
class _BeerSampleAppState extends State<BeerSampleApp> {
@override
void initState() {
super.initState();
Expand Down

0 comments on commit fb0c7ac

Please sign in to comment.