NSUrlSession updating UI Control only the first time
I've a ViewController from where I'm downloading a pdf document.
While downloading, I'm showing a UIAlertController which contains a UIProgressView which I'm updating with the download progress. It all works fine the first time.
Now after downloading I press the back button in the navigation bar to go to previous ViewController. Then when I go forward again to the download controller and try to download again, the progress is not updating and also the UIAlertController is not dismissing.
Problem is only when I go back to the previous controller. If I stay in the same controller and try the download again, it works.
I've been stuck with this for a while now. Any help is appreciated.
public partial class WAReportController : UITableViewController
{
const string Identifier = "com.gch.DownloadDocument.BackgroundSession";
public NSUrlSessionDownloadTask downloadTask;
public NSUrlSession session;
public void DownloadReport()
{
if (session == null)
session = InitBackgroundSession();
using (var url = NSUrl.FromString(RestApiPaths.REPORT_DOWNLOAD_PATH))
using (var request = new NSMutableUrlRequest(url)) {
request.Headers = CommonUtils.GetHeaders();
downloadTask = session.CreateDownloadTask(request);
downloadTask.Resume();
ShowAlert();
}
}
public NSUrlSession InitBackgroundSession()
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier)) {
return NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)new ReportDownloadDelegate(this), new NSOperationQueue());
}
}
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WAReportController _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
_vc.DismissDownloadProgressAlert();
}
}
UIAlertController downloadProgressAlert;
UIProgressView downloadProgress;
void ShowAlert()
{
downloadProgressAlert = UIAlertController.Create("Downloading", "nn", UIAlertControllerStyle.Alert);
downloadProgressAlert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, (action) => {
downloadTask.Cancel();
}));
PresentViewController(downloadProgressAlert, true, () => {
nfloat margin = 8.0f;
var rect = new CGRect(margin, 72.0f, downloadProgressAlert.View.Frame.Width - margin * 2.0f, 2.0f);
downloadProgress = new UIProgressView(rect) {
Progress = 0.0f,
TintColor = UIColor.Blue
};
downloadProgressAlert.View.AddSubview(downloadProgress);
});
}
public void UpdateDownloadProgress(float progress)
{
if (downloadProgress != null) {
downloadProgress.Progress = 50;
}
}
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
}
}
}
ios xamarin.ios nsurlsession
add a comment |
I've a ViewController from where I'm downloading a pdf document.
While downloading, I'm showing a UIAlertController which contains a UIProgressView which I'm updating with the download progress. It all works fine the first time.
Now after downloading I press the back button in the navigation bar to go to previous ViewController. Then when I go forward again to the download controller and try to download again, the progress is not updating and also the UIAlertController is not dismissing.
Problem is only when I go back to the previous controller. If I stay in the same controller and try the download again, it works.
I've been stuck with this for a while now. Any help is appreciated.
public partial class WAReportController : UITableViewController
{
const string Identifier = "com.gch.DownloadDocument.BackgroundSession";
public NSUrlSessionDownloadTask downloadTask;
public NSUrlSession session;
public void DownloadReport()
{
if (session == null)
session = InitBackgroundSession();
using (var url = NSUrl.FromString(RestApiPaths.REPORT_DOWNLOAD_PATH))
using (var request = new NSMutableUrlRequest(url)) {
request.Headers = CommonUtils.GetHeaders();
downloadTask = session.CreateDownloadTask(request);
downloadTask.Resume();
ShowAlert();
}
}
public NSUrlSession InitBackgroundSession()
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier)) {
return NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)new ReportDownloadDelegate(this), new NSOperationQueue());
}
}
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WAReportController _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
_vc.DismissDownloadProgressAlert();
}
}
UIAlertController downloadProgressAlert;
UIProgressView downloadProgress;
void ShowAlert()
{
downloadProgressAlert = UIAlertController.Create("Downloading", "nn", UIAlertControllerStyle.Alert);
downloadProgressAlert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, (action) => {
downloadTask.Cancel();
}));
PresentViewController(downloadProgressAlert, true, () => {
nfloat margin = 8.0f;
var rect = new CGRect(margin, 72.0f, downloadProgressAlert.View.Frame.Width - margin * 2.0f, 2.0f);
downloadProgress = new UIProgressView(rect) {
Progress = 0.0f,
TintColor = UIColor.Blue
};
downloadProgressAlert.View.AddSubview(downloadProgress);
});
}
public void UpdateDownloadProgress(float progress)
{
if (downloadProgress != null) {
downloadProgress.Progress = 50;
}
}
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
}
}
}
ios xamarin.ios nsurlsession
add a comment |
I've a ViewController from where I'm downloading a pdf document.
While downloading, I'm showing a UIAlertController which contains a UIProgressView which I'm updating with the download progress. It all works fine the first time.
Now after downloading I press the back button in the navigation bar to go to previous ViewController. Then when I go forward again to the download controller and try to download again, the progress is not updating and also the UIAlertController is not dismissing.
Problem is only when I go back to the previous controller. If I stay in the same controller and try the download again, it works.
I've been stuck with this for a while now. Any help is appreciated.
public partial class WAReportController : UITableViewController
{
const string Identifier = "com.gch.DownloadDocument.BackgroundSession";
public NSUrlSessionDownloadTask downloadTask;
public NSUrlSession session;
public void DownloadReport()
{
if (session == null)
session = InitBackgroundSession();
using (var url = NSUrl.FromString(RestApiPaths.REPORT_DOWNLOAD_PATH))
using (var request = new NSMutableUrlRequest(url)) {
request.Headers = CommonUtils.GetHeaders();
downloadTask = session.CreateDownloadTask(request);
downloadTask.Resume();
ShowAlert();
}
}
public NSUrlSession InitBackgroundSession()
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier)) {
return NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)new ReportDownloadDelegate(this), new NSOperationQueue());
}
}
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WAReportController _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
_vc.DismissDownloadProgressAlert();
}
}
UIAlertController downloadProgressAlert;
UIProgressView downloadProgress;
void ShowAlert()
{
downloadProgressAlert = UIAlertController.Create("Downloading", "nn", UIAlertControllerStyle.Alert);
downloadProgressAlert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, (action) => {
downloadTask.Cancel();
}));
PresentViewController(downloadProgressAlert, true, () => {
nfloat margin = 8.0f;
var rect = new CGRect(margin, 72.0f, downloadProgressAlert.View.Frame.Width - margin * 2.0f, 2.0f);
downloadProgress = new UIProgressView(rect) {
Progress = 0.0f,
TintColor = UIColor.Blue
};
downloadProgressAlert.View.AddSubview(downloadProgress);
});
}
public void UpdateDownloadProgress(float progress)
{
if (downloadProgress != null) {
downloadProgress.Progress = 50;
}
}
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
}
}
}
ios xamarin.ios nsurlsession
I've a ViewController from where I'm downloading a pdf document.
While downloading, I'm showing a UIAlertController which contains a UIProgressView which I'm updating with the download progress. It all works fine the first time.
Now after downloading I press the back button in the navigation bar to go to previous ViewController. Then when I go forward again to the download controller and try to download again, the progress is not updating and also the UIAlertController is not dismissing.
Problem is only when I go back to the previous controller. If I stay in the same controller and try the download again, it works.
I've been stuck with this for a while now. Any help is appreciated.
public partial class WAReportController : UITableViewController
{
const string Identifier = "com.gch.DownloadDocument.BackgroundSession";
public NSUrlSessionDownloadTask downloadTask;
public NSUrlSession session;
public void DownloadReport()
{
if (session == null)
session = InitBackgroundSession();
using (var url = NSUrl.FromString(RestApiPaths.REPORT_DOWNLOAD_PATH))
using (var request = new NSMutableUrlRequest(url)) {
request.Headers = CommonUtils.GetHeaders();
downloadTask = session.CreateDownloadTask(request);
downloadTask.Resume();
ShowAlert();
}
}
public NSUrlSession InitBackgroundSession()
{
Console.WriteLine("InitBackgroundSession");
using (var configuration = NSUrlSessionConfiguration.CreateBackgroundSessionConfiguration(Identifier)) {
return NSUrlSession.FromConfiguration(configuration, (INSUrlSessionDelegate)new ReportDownloadDelegate(this), new NSOperationQueue());
}
}
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WAReportController _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
_vc.DismissDownloadProgressAlert();
}
}
UIAlertController downloadProgressAlert;
UIProgressView downloadProgress;
void ShowAlert()
{
downloadProgressAlert = UIAlertController.Create("Downloading", "nn", UIAlertControllerStyle.Alert);
downloadProgressAlert.AddAction(UIAlertAction.Create("Cancel", UIAlertActionStyle.Cancel, (action) => {
downloadTask.Cancel();
}));
PresentViewController(downloadProgressAlert, true, () => {
nfloat margin = 8.0f;
var rect = new CGRect(margin, 72.0f, downloadProgressAlert.View.Frame.Width - margin * 2.0f, 2.0f);
downloadProgress = new UIProgressView(rect) {
Progress = 0.0f,
TintColor = UIColor.Blue
};
downloadProgressAlert.View.AddSubview(downloadProgress);
});
}
public void UpdateDownloadProgress(float progress)
{
if (downloadProgress != null) {
downloadProgress.Progress = 50;
}
}
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
}
}
}
ios xamarin.ios nsurlsession
ios xamarin.ios nsurlsession
edited Nov 18 '18 at 6:59
HeisenBerg
asked Nov 15 '18 at 17:41
HeisenBergHeisenBerg
2,41843562
2,41843562
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
The problem is you are holding a strong reference to the NSURLSession instance in your NSURLSessionDelegate object and never invalidating the session. According to Apple's Documentation
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
You are supposed to invalidate the session at some point. Also it looks like you are creating a strong reference cycle here:
WAReportController has a strong reference to NSUrlSession; NSURLSession has a strong reference to ReportDownloadDelegate; and ReportDownloadDelegate has a strong reference to WAReportController;. You see the problem here?
You should use weak references wherever possible. Try something like this:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
and in your view controller class:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
This way, once the download task is finished and you are dismissing the progress alert and invalidating the session. NSURLSession will, in turn, release WAReportController instance. Now, you shouldn't necessarily invalidate the session in the DismissDownloadProgressAlert() method. But you should invalidate the session when you're done using the session. The could be when you are dismissing your view controller or whenever you see fit. I just put it here as an example.
Now all this based on Apple's documentation and my knowledge of memory management on iOS. I've never worked on Xamarin and and may be wrong about how weak references are used when compared to native iOS development. Hope this helps!
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53325130%2fnsurlsession-updating-ui-control-only-the-first-time%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
The problem is you are holding a strong reference to the NSURLSession instance in your NSURLSessionDelegate object and never invalidating the session. According to Apple's Documentation
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
You are supposed to invalidate the session at some point. Also it looks like you are creating a strong reference cycle here:
WAReportController has a strong reference to NSUrlSession; NSURLSession has a strong reference to ReportDownloadDelegate; and ReportDownloadDelegate has a strong reference to WAReportController;. You see the problem here?
You should use weak references wherever possible. Try something like this:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
and in your view controller class:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
This way, once the download task is finished and you are dismissing the progress alert and invalidating the session. NSURLSession will, in turn, release WAReportController instance. Now, you shouldn't necessarily invalidate the session in the DismissDownloadProgressAlert() method. But you should invalidate the session when you're done using the session. The could be when you are dismissing your view controller or whenever you see fit. I just put it here as an example.
Now all this based on Apple's documentation and my knowledge of memory management on iOS. I've never worked on Xamarin and and may be wrong about how weak references are used when compared to native iOS development. Hope this helps!
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
add a comment |
The problem is you are holding a strong reference to the NSURLSession instance in your NSURLSessionDelegate object and never invalidating the session. According to Apple's Documentation
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
You are supposed to invalidate the session at some point. Also it looks like you are creating a strong reference cycle here:
WAReportController has a strong reference to NSUrlSession; NSURLSession has a strong reference to ReportDownloadDelegate; and ReportDownloadDelegate has a strong reference to WAReportController;. You see the problem here?
You should use weak references wherever possible. Try something like this:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
and in your view controller class:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
This way, once the download task is finished and you are dismissing the progress alert and invalidating the session. NSURLSession will, in turn, release WAReportController instance. Now, you shouldn't necessarily invalidate the session in the DismissDownloadProgressAlert() method. But you should invalidate the session when you're done using the session. The could be when you are dismissing your view controller or whenever you see fit. I just put it here as an example.
Now all this based on Apple's documentation and my knowledge of memory management on iOS. I've never worked on Xamarin and and may be wrong about how weak references are used when compared to native iOS development. Hope this helps!
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
add a comment |
The problem is you are holding a strong reference to the NSURLSession instance in your NSURLSessionDelegate object and never invalidating the session. According to Apple's Documentation
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
You are supposed to invalidate the session at some point. Also it looks like you are creating a strong reference cycle here:
WAReportController has a strong reference to NSUrlSession; NSURLSession has a strong reference to ReportDownloadDelegate; and ReportDownloadDelegate has a strong reference to WAReportController;. You see the problem here?
You should use weak references wherever possible. Try something like this:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
and in your view controller class:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
This way, once the download task is finished and you are dismissing the progress alert and invalidating the session. NSURLSession will, in turn, release WAReportController instance. Now, you shouldn't necessarily invalidate the session in the DismissDownloadProgressAlert() method. But you should invalidate the session when you're done using the session. The could be when you are dismissing your view controller or whenever you see fit. I just put it here as an example.
Now all this based on Apple's documentation and my knowledge of memory management on iOS. I've never worked on Xamarin and and may be wrong about how weak references are used when compared to native iOS development. Hope this helps!
The problem is you are holding a strong reference to the NSURLSession instance in your NSURLSessionDelegate object and never invalidating the session. According to Apple's Documentation
The session object keeps a strong reference to this delegate until your app exits or explicitly invalidates the session. If you do not invalidate the session, your app leaks memory until it exits.
You are supposed to invalidate the session at some point. Also it looks like you are creating a strong reference cycle here:
WAReportController has a strong reference to NSUrlSession; NSURLSession has a strong reference to ReportDownloadDelegate; and ReportDownloadDelegate has a strong reference to WAReportController;. You see the problem here?
You should use weak references wherever possible. Try something like this:
public class ReportDownloadDelegate : NSUrlSessionDownloadDelegate
{
private WeakReference<WAReportController> _vc;
public ReportDownloadDelegate(WAReportController vc)
{
_vc = vc;
}
public override void DidWriteData(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, long bytesWritten, long totalBytesWritten, long totalBytesExpectedToWrite)
{
if (_vc != null) {
float progress = totalBytesWritten / (float)totalBytesExpectedToWrite;
Console.WriteLine(string.Format("progress: {0}", progress));
DispatchQueue.MainQueue.DispatchAsync(() => {
_vc.UpdateDownloadProgress(progress); // updates successfully only the first time
});
}
}
public override void DidFinishDownloading(NSUrlSession session, NSUrlSessionDownloadTask downloadTask, NSUrl location)
{
if (_vc != null) {
_vc.DismissDownloadProgressAlert();
}
}
}
and in your view controller class:
public void DismissDownloadProgressAlert()
{
if (downloadProgressAlert != null) {
InvokeOnMainThread(() => {
downloadProgressAlert.DismissViewController(false, null);
});
}
session.InvalidateAndCancel() // I didn't type in an IDE so not sure if this is the exact method signature.
session = null;
}
This way, once the download task is finished and you are dismissing the progress alert and invalidating the session. NSURLSession will, in turn, release WAReportController instance. Now, you shouldn't necessarily invalidate the session in the DismissDownloadProgressAlert() method. But you should invalidate the session when you're done using the session. The could be when you are dismissing your view controller or whenever you see fit. I just put it here as an example.
Now all this based on Apple's documentation and my knowledge of memory management on iOS. I've never worked on Xamarin and and may be wrong about how weak references are used when compared to native iOS development. Hope this helps!
answered Nov 15 '18 at 18:46
PranayPranay
52616
52616
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
add a comment |
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
I did some research and everything you said applies to xamarin too. docs.microsoft.com/en-us/xamarin/ios/deploy-test/performance
– HeisenBerg
Nov 20 '18 at 6:03
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
@HeisenBerg I’m glad it helped
– Pranay
Nov 20 '18 at 6:25
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53325130%2fnsurlsession-updating-ui-control-only-the-first-time%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown