0
votes

The problem is that sometimes (not always) my completion handler become nil.

I've already tried [weak self] but it doesn't work.

func setup() {
 _collectionView.refreshControl?.addTarget(self, action: #selector(refresh(sender:)), for: .valueChanged)
}
 @objc private func refresh(sender: UIRefreshControl) {
       print("request1")
       request(refresh: true, completion: { () -> Void in
           // When completion become nil, this is not called.
           (sender as UIRefreshControl).endRefreshing()
           print("completion1")
       })
   }


   fileprivate func request(refresh: Bool, completion: (() -> Void)? = nil) {

       guard !isFetchInProgress else {
           return
       }
       isFetchInProgress = true

       APIRequest.request(request: request).success { [weak self] result in
           Log.debugLog(“success!:%@“, result)
               if let _self = self {
                   if RequestResponseManager.sharedInstance.handleResponse(response: result) {
                        if let _data = result.data {
                         print(_data)
                   }
               }
           }.failure { error, _ in
               Log.debugLog(“%@“, error.debugDescription)
               completion?()
               isFetchInProgress = false
           }.then { _,_  in
               Log.debugLog(“then!“)    

               // SOMETIMES (not always) completion?() become nil
               completion?()
               isFetchInProgress =false
           }
   }
    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {

        var target: UICollectionReusableView? = nil
  if kind == UICollectionView.elementKindSectionFooter {
            target = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: footerId, for: indexPath)

            if isNeedRequest {
                print("request2")
                self.request(refresh: false)
            }
        }

        return target!
    }

1、When scroll down & up speed is normal, log shows the below and that's fine.

request1
completion1
request2

2、However when scroll down & up speed is faster, log shows the below and never called completion1 and stopped refreshControl.

request1
request2

I wonder how I can handle it properly? Thank you.

1
Are you sure the APIRequest always finishes? For example: When isFetchInProgress is true, the API request never even starts and the completion handler is never called. - Rengers
Looks like in this case completion is strongly referenced and may not in any case be set to nil, not even explicitly. The only way this is nil because it is already injected as nil. Ergo somewhere something is calling request(refresh: flag, completion: nil). - Matic Oblak
@wvteijlingen Thank you for your comment. debugLog is always showed either . failure or .then - Alea
@MaticOblak Thank you for your reply. Actually request(refresh: false) is called from other method when the collectionView is scrolled to the end. How can I avoid @objc private func refresh(sender: UIRefreshControl) completion is injected as nil? - Alea
@Alea could you please add that code as well? - Matic Oblak

1 Answers

2
votes

The completion handler does not "become" nil. It is nil by default if no completion handler is supplied at the time the method is called.

Your method is declared

fileprivate func request(refresh: Bool, completion: (() -> Void)? = nil)

This means that in effect there are two ways of calling this method. You can call it with a completion handler:

request(refresh: true) {
    // this is the completion handler
}

or

request(refresh: false) {
    // this is the completion handler
}

But you can also call it without a completion handler:

request(refresh: true)

or

request(refresh: false)

In that case, inside your request method, completion will be nil. So if you don't want completion to be nil, don't do that.