NSUrlSession updating UI Control only the first time












0















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);
});
}
}

}


}










share|improve this question





























    0















    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);
    });
    }
    }

    }


    }










    share|improve this question



























      0












      0








      0








      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);
      });
      }
      }

      }


      }










      share|improve this question
















      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






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 18 '18 at 6:59







      HeisenBerg

















      asked Nov 15 '18 at 17:41









      HeisenBergHeisenBerg

      2,41843562




      2,41843562
























          1 Answer
          1






          active

          oldest

          votes


















          1














          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!






          share|improve this answer
























          • 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











          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
          });


          }
          });














          draft saved

          draft discarded


















          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









          1














          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!






          share|improve this answer
























          • 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
















          1














          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!






          share|improve this answer
























          • 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














          1












          1








          1







          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!






          share|improve this answer













          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!







          share|improve this answer












          share|improve this answer



          share|improve this answer










          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



















          • 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




















          draft saved

          draft discarded




















































          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.




          draft saved


          draft discarded














          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





















































          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







          Popular posts from this blog

          List item for chat from Array inside array React Native

          Thiostrepton

          Caerphilly