POST request doesn't work on iOS devices in IONIC app - ios

I tried to make post-request to my server from iOS device (iOS emulator inside Xcode). I'm using ionic 5 and VueJS and #capacitor-community/http.
So my code looks like:
methods: {
async submitForm(){
const form_data = this.$store.getters.my_data;
const options = {
url: 'https://my_domain.com/api/page.php',
headers: {"Content-Type": "application/json"},
data: form_data,
};
await Http.post(options)
.then(res => {
if (res.status === 200) {
console.log(res.data)
}
})
.catch((err) => {
console.log(err)
});
}
I have no submit buttons and I'm not using prevent in the method
When I run my project in the web to testing - I have no problems. GET and POST requests works fine (when testing as web-app I have to remove headers: {"Content-Type": "application/json"} for prevent CORS problems).
So, next I run:
ionic build
npx cap copy
Then I try android emulator (inside android-studio). And my app also works great (all types of requests). Builded android app works perfect on real devices too.
But when it comes to iOS... GET request works fine, as expected. But when I press button which initialize POST request - nothing happens! No errors... No alerts... No logs... Just nothing. My backend doesn't get any request. I have no idea what to do.
There are lines in my Info.plist:
<key>WKAppBoundDomains</key>
<array>
<string>www.my_domain.com</string>
<string>https://my_domain.com</string>
</array>
TransportSecurity flag set as TRUE:
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
I tried use native fetch, but the result is same - nothing happens.
So I think it is kind of problems with iOS, but I haven't enough knowledges to understand what the problem is.

So, a few days I tried to realize what's wrong with my code.
Finally I found some logs which says:
Unhandled Promise Rejection: DataCloneError: The object can not be cloned.
It means that something wrong with my data I trying to send.
And the reason is:
const form_data = this.$store.getters.my_data;
This line returns not a json object but some Proxy object. The behavior is pretty similar to json: you can use form_data.name or any other values, but swift for some reasons can't use this object.
So, we need just to create new json object for using in post body:
async submitForm(){
const form_data = this.$store.getters.my_data;
const body_data = {};
for (const [key, val] of Object.entries(form_data)) {
body_data[key] = val;
}
const options = {
url: 'https://my_domain.com/api/page.php',
headers: {"Content-Type": "application/json"},
data: body_data,
};
await Http.post(options)
.then(res => {
if (res.status === 200) {
console.log(res.data)
}
})
.catch((err) => {
console.log(err)
});
}
And this code works fine
P.S. be sure that all you values in body_data are standard JS types

I had the same Issue:
Worked in Android and Browser but not on IOS.
VueJS Composition API Uses Some Poxy on Reactive Variables.
Convert it into a JSON Object first - Then it works for me.
const bodyJson = JSON.stringify(bodyParameters);
const options = {
url: `https://my_domain.com/api/page.php`,
headers: { "Content-Type": "application/json" },
data: bodyJson,
};

Related

Ionic 5 - API request working on browser, not on emulated IOS

I have this Ionic 5/Capacitor app, which I'm making an API call to a local server from, that server running on docker at localhost:3000. When I test from the browser, the request is made fine. From Postman it requests fine, too. In my XCode logs the emulator, I see this
[error] - ERROR {"headers":{"normalizedNames":{},"lazyUpdate":null,"headers":{}},"status":0,"statusText":"Unknown Error","url":"http://localhost:3000/pins","ok":false,"name":"HttpErrorResponse","message":"Http failure response for http://localhost:3000/pins: 0 Unknown Error","error":{"isTrusted":true}}
The really interesting part, is that I'm running Fiddler to monitor the request as it's made. Fiddler gets a 200 as well, I can even see the response data. So, Fiddler sees the proper network call, but then my Ionic app gets that error. That makes me feel like it's an Ionic/Emulator/IOS problem, but I don't have enough familiarity with Ionic to know right off the bat what it is.
Here's the code responsible for making the request:
ngOnInit() {
const request = this.http.get('http://localhost:3000/pins');
this.refresh$.subscribe(
(lastPos: { latitude?: any; longitude?: number }) => {
request.subscribe(data => {
if (data) {
this.addMarkersToMap(data, lastPos);
}
});
}
);
}
And the HTTPClient imported in the constructor is from Angular:
import { HttpClient } from '#angular/common/http';
I ended up having to use this package, doing a check on if I'm on mobile or not.
https://ionicframework.com/docs/native/http/
Try with this :
const request = this.http.get('http://localhost:3000/pins', { observe: 'response', withCredentials: true });
Solution 2 : capacitor.config.json
"server": {
"hostname": "localhost", (maybe try precising the port number too)
}
Solution 3 : On your Express server (from https://ionicframework.com/docs/troubleshooting/cors)
const express = require('express');
const cors = require('cors');
const app = express();
const allowedOrigins = [
'capacitor://localhost',
'ionic://localhost',
'http://localhost',
'http://localhost:8080',
'http://localhost:8100'
];
// Reflect the origin if it's in the allowed list or not defined (cURL, Postman, etc.)
const corsOptions = {
origin: (origin, callback) => {
if (allowedOrigins.includes(origin) || !origin) {
callback(null, true);
} else {
callback(new Error('Origin not allowed by CORS'));
}
}
}
// Enable preflight requests for all routes
app.options('*', cors(corsOptions));
app.get('/', cors(corsOptions), (req, res, next) => {
res.json({ message: 'This route is CORS-enabled for an allowed origin.' });
})
app.listen(3000, () => {
console.log('CORS-enabled web server listening on port 3000');
});

Fetching data on IOS react redux

I'm building my first application using react and redux, and for some reason, the structure of the data received on a Async action is behaving different on the IOS (safari).
This action makes the fetch (cross-fetch) request:
export function fetchTransactions() {
return (dispatch) => {
dispatch(requestTransactions());
return fetch('/getTransactions', {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
Cache: 'no-cache',
},
credentials: 'same-origin',
})
.then(
response => response.json(),
error => console.log('An error occurred.', error),
)
.then(json => dispatch(receiveTransactions(json.dados)));
};
}
And the receive action is:
export const RECEIVE_TRANSACTIONS = 'RECEIVE_TRANSACTIONS';
export function receiveTransactions(data) {
return {
type: RECEIVE_TRANSACTIONS,
data,
};
}
Reducer
case 'REQUEST_TRANSACTIONS':
return Object.assign({}, {...state}, {isFetching: true});
case 'RECEIVE_TRANSACTIONS':
return Object.assign({}, {...state}, {data: action.data}, {isFetching: false});
Now I'll show you the results on Firefox running on linux:
It works as expected, the transaction data is present on the array called data.
Here is a print screen from a Safari emulator:
As you can see, the are duplicate fields and other fields. This behavior is causing the application to not work correctly. Any ideas? Thank you very much and sorry about the English thing.
#edit-------------------------------------------
Running the application on other emulator, results in a good state but the data isn't displayed on the table as you can see bellow:
As you can see, there are 100 data objects in the array, and nothing on the table, this is not happening on firefox or chrome.

Fetch not working on react native when run on Expo from iphone

So it's actually been on and off the past few days, the fetch function for POST method (for login purposes) on my app would suddenly stop working when I'm not even making any changes to the code.
I spent hrs tracking down the bug the first time this happened but realized that the code is still running fine on Android simulator that I run from Android studio.
This problem only happens on my iphone device when I run the code via expo.
is there any sort of convention on how to tackle this issue?
I've been googling for a few days now but no one forum actually agreed on a solution, some works but says it's unstable and some just doesn't work.
I'm pretty new to expo, react native, and everything in general, so please enlighten me using "english" lol
EDIT: since I was asked about the code, here it is, I hope it helps:
export function login(user, callback){
var endpoint = "oauth/token";
const { username, password } = user;
//from https://scotch.io/tutorials/how-to-encode-and-decode-strings-with-base64-in-javascript
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9+/=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}};
let header = {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": ("Basic " + Base64.encode(username+":"+password))
};
let userData = {
"grant_type": "password",
"username": username,
"password": password
};
return fetchAPI(endpoint,'POST', header, userData)
.then((tokenJson) => {
alert("\nAccess Token:\n\n" + tokenJson.access_token + "");
callback(true, {exists : true, token : tokenJson.access_token}, null);
})
.catch((error) => {
alert(error);
callback(false, null, error);
});
}
And here's the fetchAPI function (a generic fetch function) called from that function:
export function fetchAPI(endpoint, method, header, data) {
let url = 'http://10.64.2.149:8081/' + endpoint;
let options = {
method: method,
headers: header,
body: stringify(data) //from qs-stringify package
};
return fetch(url, options)
.then(response => {
return response.json()
.then((json) => { ...//not relevant from this point on
The fetch 'POST' seems to always get stuck at response.json(), it just never resolves until it returns "network request failed". But as I said only happens sometimes when I run it on iphone via expo. It is fine on Android sim.
Thanks! :)

Ionic Angular. $http and geolocation.getCurrentPosition do not appear to work in iOS simulator

I'm developing a simple iOS app using Ionic.
I have 2 bits of functionality that do not appear to work in the iOS simulator that are working fine when I run the app in chrome.
Firstly, I am using the $http service in a factory to pull in some data:
var harbours = function () {
var deferred = $q.defer();
$http({
method: 'GET',
url: '../data/harbourData.json'
}).success(function (data, status, headers, config) {
deferred.resolve(data);
}).error(function(data, status, headers, config) {
deferred.reject(status);
});
return deferred.promise;
}
.....
return {
all: harbours
....
}
and then set it in my scope in the controller with:
var harboursPromise = Harbours.all();
harboursPromise.then(function(response){
console.log('response from controller', response);
$scope.harbours = response.harbours;
});
This does not appear to execute as I do not get the data in my template, whereas I do when running the app in chrome.
Secondly, geolocation. This piece of code is working fine in the browser but not when I build and run the app in the simulator:
$scope.position = 'Trying to find your location...'
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(function(position){
$scope.$apply(function(){
$scope.position = position.coords.latitude+", "+position.coords.longitude;
})
})
} else {
$scope.position = "Sorry, we can't get your location";
}
In the running app, it appears to have access to navigator.geolocation as $scope.position does not get changed to "Sorry, we can't get your location". I'm sure there is something fundamentally wrong that I am / am not doing. Can anyone point me in the right direction, please?
Thank you.

ionic framework bad request when calling web service over 3G

I'm developing iOS app using ionic framework and I have one problem when I try to call web service by using 3G network.
here is my service in UserService:
function getUserStat(user_id){
var request = $http({ method: "get",
url: "http://www.example.com/user.php",
params: {
action: "stat",
user_id:user_id
},
data: {
}
});
return(request.then(handleSuccess, handleError));
}
function handleError( response ) {
// The API response from the server should be returned in a
// nomralized format. However, if the request was not handled by the
// server (or what not handles properly - ex. server error), then we
// may have to normalize it on our end, as best we can.
if (!angular.isObject( response.data ) || !response.data.message) {
return( $q.reject("An unknown error occurred.") );
}
// Otherwise, use expected error message.
return( $q.reject( response.data.message ) );
}
// I transform the successful response, unwrapping the application data
// from the API response payload.
function handleSuccess( response ) {
return( response.data );
}
the getUserStat() function will return json back.
here is my controller
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
});
in my control I just show the json.
I build this code to my iPhone and test it over WIFI network, everything work fine. If i update the serverside, UserService.getUserStat in controller will show update. but the problem is when I test it on 3G network, iPhone always show the old json returned from the server (even I change server side data).
any idea to solve this problem?
Thank you
I had a similar problem when I tried to upload a camera photo to my data server.when i tested the app on my local WIFI it worked perfectly but when I tested it outside i noticed it fails to upload the file. eventualy the problem was that since the internet outside is much slower the app moved to another view without finish the upload action.
so for example if your controller looks something like this:
.controller('Ctrl1', function(webService, $scope, $state) {
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
});
$state.go('app.posts');
});
it should be like this:
.controller('Ctrl1', function(webService, $scope, $state) {
UserService.getUserStat($scope.user_id).then(function(data){
alert("Result: " + JSON.stringify(data));
})
.finally(function() {
$state.go('app.posts');
});
});

Resources