iOS Ionic 2 app unable to read from SqlStorage - ios

We're building an Ionic 2 (currently beta 11).
We're using built-in SqlStorage database. From Android our app is able to read and write date just fine, but with iOS we can only write the data. When we attempt to read the data we get the number of rows returned but none of the actual data.
getQueueItems(): Promise<any> {
return new Promise<any>((resolve, reject) => {
this.sql.query('SELECT * FROM queue').then(
(res) => {
console.log(res, 'result');
// resolve(sqlResult.res.rows);
}
);
}).catch(() => {
});
}
The resultset looks like this:
{
"tx": {
"db": {
"openargs": {
"name":"__ionicstorage",
"location":2,
"createFromLocation":0,
"backupFlag":2,
"existingDatabase":false,
"dblocation":"nosync"
},
"dbname":"__ionicstorage"
},
"txlock":true,
"readOnly":false,
"executes":[],
"finalized":true
},
"res": {
"rows": {
"length":3
},
"rowsAffected":0
}
}
Does anyone know how we can read from SqlStorage so that iOS gets the data?

After a lot of searching and reading tons of forum posts we finally found the answer. I'm posting here for future searchers, hopefully it will help you.
The trick is that in order for the query results to be usable by all platforms you have to iterate through the result set yourself adding the appropriate objects as you go.
this.sql.query('SELECT * FROM queue').then(
(sqlResult) => {
let queueItems = [];
for(let i = 0; i < sqlResult.res.rows.length; i++){
queueItems.push(sqlResult.res.rows.item(i));
}
resolve(queueItems);
}
);

Related

.Net Maui app completion handlers and iOS HealthKit not working with HttpClient

This works perfectly in Android.
public async Task<double> UploadData()
{
double steps = 0.0;
await _healthData.GetSteps((totalSteps) =>
{
SentrySdk.CaptureMessage("totalSteps = " + totalSteps);
MainThread.BeginInvokeOnMainThread(() =>
{
steps = totalSteps;
//Task.Delay(1000);
});
});
SentrySdk.CaptureMessage("UploadData steps = " + steps);
var fitness = new Fitness();
fitness.Steps = steps;
await _restService.SaveItemAsync(fitness, true);
return steps;
}
In iOS, totalSteps is correct, but steps is still 0 when fitness.Steps = steps runs. Bottom line, I can't get the totalSteps value from inside the _healthData.GetSteps((totalSteps) operation. The Android Google Fit and iOS HealthKit API calls run with completion handlers.
At this stage, I'm just trying to figure out how to upload data (steps, calories, active minutes, distance) to my server.
Does anyone know how to make this work? I can display all the data (steps, calories, active minutes, distance) in a View using an ObservableCollection.
I got this to work by embedding the calls like so.
_healthData.GetHealthPermissionAsync((result) =>
{
if (result)
{
_healthData.FetchSteps((totalSteps) =>
{
_healthData.FetchMetersWalked((metersWalked) =>
{
_healthData.FetchActiveMinutes((activeMinutes) =>
{
_healthData.FetchActiveEnergyBurned((caloriesBurned) =>
{
var fitness = new Fitness();
fitness.Steps = totalSteps;
fitness.Calories = caloriesBurned;
fitness.Distance = metersWalked;
fitness.Minutes = activeMinutes;
_restService.SaveItemAsync(fitness, true);
});
});
});
});
}
});

How to pass different test data to a test in Playwright test runner?

How to pass different test data to a test in Playwright test runner?
Like sending the test data from a csv file?
You could use a csv-parser like this one and loop over the data to create new tests automatically. If you don't need to create tests automatically, you can also use it directly inside your test to get the externally data loaded. It might also make sense to wait / subscribe to this issue, which adds test.each support to Playwright test.
function PreformMultipleTestsWithData(data) {
for(let i=0; i<data.length; i++) {
console.log(`xyz${data[i]}`);
test(`Testing data ${data[i]}`, async({page}, testInfo) => {
expect(data[i]).toBe(2);
})
}
}
PreformMultipleTestsWithData([1,2,3]);
Putting async issues aside, this works the UI and test results correctly reflect that 3 tests were preformed.
Update:
According to https://github.com/microsoft/playwright/issues/9916
We cannot put the async issue aside.
In other words the following will not work
function async PreformMultipleTestsWithData() {
let data = await getData();
console.log("Data is present:", JSON.stringify(data))
for(let i=0; i<data.length; i++) {
console.log(`xyz${data[i]}`);
test(`Testing data ${data[i]}`, async({page}, testInfo) => {
expect(data[i]).toBe(2);
})
}
}
PreformMultipleTestsWithData();
but this will work:
function async PreformMultipleTestsWithData() {
let data = getData();
console.log("Data is present:", JSON.stringify(data))
for(let i=0; i<data.length; i++) {
console.log(`xyz${data[i]}`);
test(`Testing data ${data[i]}`, async({page}, testInfo) => {
expect(data[i]).toBe(2);
})
}
}
PreformMultipleTestsWithData();
You can loop through some JSON and parameterise your tests
JSON
const testData = [{
name:"test1",
url:"https://www.youtube.com"
},{
name:"test2",
url:"https://www.google.com"
},
{
name:"test3",
url:"https://www.bing.com"
}
]
Loop through the JSON
testData.forEach(data =>{
test(`MyTest ${data.name}`, async ({ page}) => {
await page.goto(data.url);
//Test assertion here
});
})

Printing using ngx-extended-pdf-viewer on iOS and Mobile Safari or Chrome

I have an Angular 7 app that is using ngx-extended-pdf-viewer to render a PDF that I get as a byte array from the web.api. I have no issues rendering the PDF or even printing it from any desktop application. ngx-extended-pdf-viewer as a print button built right in. However, when trying to print from Safari on an iPhone (iOS 12) it only prints a blank page with the url at the bottom. The actual PDF does not print. With Chrome on iOS it doesn't do anything that I can see. I am pretty new to Angular and actually to mobile web development, so perhaps lack of knowledge is getting me. The PDF viewer is in a mat-tab, so not sure if maybe that is causing some issues??
I have tried other packages, but they all seem to be based on the same pdf.js code from Mozilla. Also this is the only one I've found so far that has a print. I was thinking about perhaps trying pdf.js outside of an npm package, but so far have not found solid directions on getting this to work in Angular. I'm sure it will, but all directions I have found seem to omit details. Such as, put this code in your app. They just fail to say where in the app.
From the web.api:
[HttpPost("GetPdfBytes/{PdfId}")]
public ActionResult<byte[]> GetPdfBytesId([FromBody]int id)
{
string exactPath = string.Empty;
if (id == 1)
{
exactPath = Path.GetFullPath("pdf-test.pdf");
}
else if (id == 2)
{
exactPath = Path.GetFullPath("DPP.pdf");
}
else if (id == 3)
{
exactPath = Path.GetFullPath("Request.pdf");
}
else
{
exactPath = Path.GetFullPath("Emergency Issue.pdf");
}
byte[] bytes = System.IO.File.ReadAllBytes(exactPath);
return Ok(bytes);
}
The HTML:
<mat-tab label="PDF">
<ng-template matTabContent>
<ngx-extended-pdf-viewer *ngIf="visible[2]" id="pdf3" [src]="pdfSrc3" useBrowserLocale="true" delayFirstView="1000" showSidebarButton="false"
showOpenFileButton="false" >
</ngx-extended-pdf-viewer>
</ng-template>
</mat-tab>
TypeScript:
getPDFBytesId(id: string) {
this.getPDFFromServicePdfBytesId(Number(id)).subscribe(
(data: any) => {
this.pdfSrc3 = this.convertDataURIToBinary(data);
},
error => {
console.log(error);
}
);
}
// hits the web.api
getPDFFromServicePdfBytesId(id: number): Observable<any> {
const body = id;
return this.http.post<any>('http://localhost:5000/api/values/GetPdfBytes/' + id, body);
}
// converts what we got back to a Uint8Array which is used by the viewer
convertDataURIToBinary(dataURI: string) {
const raw = window.atob(dataURI);
const rawLength = raw.length;
const array = new Uint8Array(new ArrayBuffer(rawLength));
for (let i = 0; i < rawLength; i++) {
array[i] = raw.charCodeAt(i);
}
return array;
}

SELECT statement no longer working after installing cordova-plugin-ionic-webview

I was trying to fix some performance issues in my ionic hybrid app when using AWS cognito which requires installing cordova-plugin-ionic-webview. However, after installing this plugin, my SELECT statement is no longer working - it is now returning no records found. Here is the statement:
dbAccess.SelectGoodsReceiptDetail = function SelectGoodsReceiptDetail(goodsreceipt) {
var resultData = {};
// Select Multiple Items
return $q(function(resolve, reject) {db.executeSql("SELECT * FROM goodsreceiptdetailview WHERE goodsReceiptKey LIKE ?", [ goodsreceipt.header.goodsReceiptKey] , function(rs) {
resultData.data = [{}];
if (rs.rows.length > 0) {
if (rs.rows.item) {
for (i=0;i<rs.rows.length; i++) {
resultData.data[i] = rs.rows.item(i);
}
resultData.exist = true;
}
} else {
// no item found
resultData.exist = false;
}
resolve(resultData);
}, function(error) {
resultData.data = [{}];
resultData.exist = false;
resultData.failed = true;
resolve(resultData);
})
});
}
The variable goodsreceipt.header.goodsReceiptKey in an integer. I have read in the release notes for the cordova sqlite plugin that whole numbers are treated as REAL values when using WKWebView while it is being treated as INT on UIWebView here. Could this be causing the problem? How can I fix this with WKWebView?
I was able to fix this by converting the INT to string.

IOS 10.3.1 cordova based App crashes when trying to write to IndexedDB

I'm developing a cordova-based multi-platform web-app using sapui5 framework v1.44 and indexedDB for storing data.The app was working fine untill last ios update, 10.3.1, now it crashes when trying to write to indexedDB. I'm using put method for updating data and i did a clean install of the app. The code frame where i try to write to indexedDB is this:
writeToIDB: function (objStoreName, result, success, error) {
//Asynchronous function
var defer = Q.defer();
var res = [];
if (!!result && Array.isArray(result)) {
res = result;
} else if (!!result && result.hasOwnProperty("results") && Array.isArray(result.results)) {
res = result.results;
} else if (!!result && typeof result === 'object') {
res.push(result);
}
if (res.length >= 0) {
if (window.myDB) {
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
} else {
this.createIDB().then(
function (resCreate) {
console.log("DB Created successfully");
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
}.bind(this),
function (err) {
console.log("DB Creation failed");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}.bind(this)
);
}
} else {
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}
if (typeof success === 'undefined' && typeof error === 'undefined') {
return defer.promise;
}
},
P.S.I have omitted parts of the code.
This was working fine with the previous version of ios, i think i had installed the 10.2.1, now it simply crashes after calling the put method. I tried upgrading now ios to the beta of 10.3.2 but the result is the same. Anyone else noticed this or have any idea of how to resolve this problem?
Thanks
K
UPDATE
I've found the issue: the complex dataTypes. Since IndexedDB supports saving and retrieving complex dataTypes, i had some properties which were arrays or objects that i used to save in some of my ObjectStores. This is definitely a big problem for me because the only workaround i can think for this is to stingify the complex fields but since i work with a lot of data this would create a big performance issue. I hope the ios developer team will find a solution for this soon enough
Are you sure every key in the res[] array is a valid key? There is a closed bug here:
https://bugs.webkit.org/show_bug.cgi?id=170000
It looks if you pass in an invalid key it will cause webkit to crash.
This fix for this will likely be contained in the next public release of iOS.
To determine what a valid key is see this section of the W3.org spec:
3.1.3 Keys
In order to efficiently retrieve records stored in an indexed database, each record is organized according to its key. A value is said to be a valid key if it is one of the following ECMAScript [ECMA-262] types: Number primitive value, String primitive value, Date object, or Array object. An Array is only a valid key if every item in the array is defined and is a valid key (i.e. sparse arrays can not be valid keys) and if the Array doesn't directly or indirectly contain itself. Any non-numeric properties on an Array are ignored, and thus do not affect whether the Array is a valid key. If the value is of type Number, it is only a valid key if it is not NaN. If the value is of type Date it is only a valid key if its [[PrimitiveValue]] internal property, as defined by [ECMA-262], is not NaN. Conforming user agents must support all valid keys as keys.
This was taken from here:
https://www.w3.org/TR/IndexedDB/#key-construct
Not sure if it's the same issue, but I had a crash on iOS 10.3 that I didnt get in any other browser. Using Dexie wrapper for indexedDB, I did a get all records from table search:
db.table.toArray(function (results) {
// process...
})
and got flames from Xcode to what looked like a threading issue in WebKit so I just added setTimeout( ... ,1) and that hacked around the problem for me.

Resources