Hi everyone, hope you can take a moment to read the issue I describe below and help me fix it. Sorry for my bad English.
I am developing an application that allows users to enter surveys. Application can work when offline/online.
Here is the concept:
1. In case of online: When user enter survey and click on submit button, the app will call api (use httpClient) to send survey info to server.
2. In case of offline: When user enter survey and click on submit button, the app will save survey info in local. After the mobile device connected network, it will call api to send survey info from local to server.
Workflow:
For android, it works for these cases:
When online:
1. Create survey, survey will be sent to server.
When offline:
1. Create survey => survey is saved at local.
1.1. Keep app is still opening and then connect network => survey is sent to server.
1.2. Touch home button so app run background => Connect network => survey is sent to server.
1.3. Users force-quit app => Connect network (in this case survey wont be sent to server) => Open app (survey will be synced to server)
For iOS, it works for these cases:
When online:
1. Create survey, survey will be sync to server (need to wait about 5 seconds before closed app. Because if closed immediately, it will error when calling api).
When offline:
1. Create survey=> survey is saved at local.
1.1. Keep app is still opening and then connect network => survey is sent to server.
1.2. Touch home button so app run background => Connect network (in this case survey wont be sent to server) => Open app (survey will be sent to server)
1.3. Users force-quit app => Connect network (in this case survey wont be sent to server) => Open app (survey will be sent to server)
So I see that everything works fine on Andoird.
However, there was a problem if the app run the background on iOS. Survey is only sent to the server if users open the app.
For iOS, I'm using Silent Notification. Will push Notification to the app every 5 minutes to trigger calling API (send survey to server) when the app is running background. For now, the app can call API when app receive notification but there is an error occurred while calling api.
- I have used my iPhone and tested on Facebook Messenger, in case I send a message while offline and then touch the Home button to run background (not force-quit app). After network was connected, the message will be sent without having to open the app.
Here is the error:
NSLocalizedDescription=The network connection was lost., NSErrorFailingURLStringKey=[URL], NSErrorFailingURLKey=[URL], _kCFStreamErrorDomainKey=4}
--- End of inner exception stack trace ---
at System.Net.Http.NSUrlSessionHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x104e298d0 + 0x0091f> in <e7de71939f4649d9bdfb2e2411afa951#ca5a74421adead0e302c060b720be5bb>:0
at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) <0x104de7dd0 + 0x00423> in <51ea6b8d5ff1420888885e4cb6c720e9#ca5a74421adead0e302c060b720be5bb>:0
at SurveyApp.Service.RestClient.PostAsync[T,TP] (System.String url, TP t, System.Nullable1[T] token) <0x105c15460 + 0x00984> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0 at SurveyApp.Service.SyncService.Push (System.Collections.Generic.IList
1[T] synchronizations) <0x105c1d7a0 + 0x001e7> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0
at SurveyApp.Service.SyncService.PushSurveys () <0x105c1d1b0 + 0x0028b> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0 ","System.Net.Http.HttpRequestException The network connection was lost. at System.Net.Http.NSUrlSessionHandler.SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken) <0x104e298d0 + 0x0091f> in <e7de71939f4649d9bdfb2e2411afa951#ca5a74421adead0e302c060b720be5bb>:0
at System.Net.Http.HttpClient.SendAsyncWorker (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) <0x104de7dd0 + 0x00423> in <51ea6b8d5ff1420888885e4cb6c720e9#ca5a74421adead0e302c060b720be5bb>:0
at SurveyApp.Service.RestClient.PostAsync[T,TP] (System.String url, TP t, System.Nullable1[T] token) <0x105c15460 + 0x00984> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0 at SurveyApp.Service.SyncService.Push (System.Collections.Generic.IList
1[T] synchronizations) <0x105c1d7a0 + 0x001e7> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0
at SurveyApp.Service.SyncService.PushSurveys () <0x105c1d1b0 + 0x0028b> in <b617b7a5ed5c409182b773f98768e9a9#ca5a74421adead0e302c060b720be5bb>:0
Foundation.NSErrorException Error Domain=NSURLErrorDomain Code=-1005 ""The network connection was lost."" Use
Here is code that I'm handling for calling API (use httpClient):
public async Task PostAsync<T, TP>(string url, TP t, CancellationToken? token = null)
{
try
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
var content = JsonConvert.SerializeObject(t);
HttpContent httpContent = new StringContent(content);
httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json");
using (var httpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(5), DefaultRequestHeaders = { ConnectionClose = true } })
{
HttpResponseMessage responseMessage;
if (token != null)
{
responseMessage = await httpClient.PostAsync(url, httpContent, token.Value);
}
else
{
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
responseMessage = await httpClient.PostAsync(url, httpContent, cancellationTokenSource.Token);
}
if (responseMessage.IsSuccessStatusCode == false)
{
throw new Exception("Request has problem");
}
var responseContent = await responseMessage.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent, DefaultSerializerSettings);
}
}
catch (TaskCanceledException)
{
await Task.Delay(5000);
return await PostAsync<T, TP>(url, t, token);
}
catch (Exception e)
{
_logger.Error(e);
throw;
}
}
public async Task<T> PostSurveyPhotoWithDataAsync<T>(string url, string file, SurveyPhoto surveyPhoto)
{
try
{
ServicePointManager.ServerCertificateValidationCallback += (sender, cert, chain, sslPolicyErrors) => true;
ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
using (var formData = new MultipartFormDataContent())
{
using (var httpClient = new HttpClient { Timeout = TimeSpan.FromMinutes(10), DefaultRequestHeaders = { ConnectionClose = true } })
{
using (var streamReader = new StreamReader(file))
{
formData.Add(new StreamContent(streamReader.BaseStream), "File", file);
formData.Add(new StringContent(surveyPhoto.KeyId), "KeyId");
formData.Add(new StringContent(surveyPhoto.FileName), "FileName");
formData.Add(new StringContent(surveyPhoto.SurveySyncId), "SurveyId");
var cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMinutes(5));
var responseMessage = await httpClient.PostAsync(url, formData, cancellationTokenSource.Token);
streamReader.Close();
if (responseMessage.IsSuccessStatusCode == false)
{
throw new Exception("Request has problem");
}
var responseContent = await responseMessage.Content.ReadAsStringAsync();
return JsonConvert.DeserializeObject<T>(responseContent, DefaultSerializerSettings);
}
}
}
}
catch (TaskCanceledException)
{
await Task.Delay(5000);
return await PostSurveyPhotoWithDataAsync<T>(url, file, surveyPhoto);
}
catch (Exception e)
{
_logger.Error(e);
throw;
}
}
Therefore, Are there any solution, document or sample code help me solved this issue?
Thanks for take a look at this question. Hope everyone can help me.