1

The following Objective-C Code processes the NSURLSession code (via viewDidLoad()) before the UICollectionView's delegate (which is what I want):

--- Session ---
--- Session ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---
--- {CellForItemAtIndexPath} ---

Note: the above debug msgs were made within their respective break properties.

The abridged Objective-C code:

@interface MainViewController () <UICollectionViewDataSource, UICollectionViewDelegate>
@property (weak, nonatomic) IBOutlet UICollectionView *collectionView;
@property (nonatomic, strong) NSMutableArray *downloaders;
@property (nonatomic, strong) NSArray *photoInfos;
@end

@implementation MainViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self fetchFlickrPhotoWithSearchString:@"Ric"];
}

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    if (_currentImageDownloader) {
        [_downloaders replaceObjectAtIndex:_selectedItemIndex withObject:_currentImageDownloader];
    }
}

// -----------------------------------------------------------------------------------------------------------------------
#pragma mark -

- (void)fetchFlickrPhotoWithSearchString:(NSString *)searchString {

    SimpleFlickrAPI *flickr = [SimpleFlickrAPI new];


    [[[NSURLSession sharedSession] dataTaskWithURL:[flickr getURLForString:@"Ric"]
                                 completionHandler:^(NSData *data,
                                                     NSURLResponse *response,
                                                     NSError *error) {
                                     if (!error) {
                                         NSString *string = [flickr stringByRemovingFlickrJavaScript:data];
                                         NSData *jsonData = [string dataUsingEncoding:NSUTF8StringEncoding];
                                         NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData:jsonData
                                                                                                  options:NSJSONReadingAllowFragments
                                                                                                    error:&error];

                                         self.photoInfos = [[jsonDict objectForKey:@"photos"] objectForKey:@"photo"];

                                         NSMutableArray *downloaders = [[NSMutableArray alloc] initWithCapacity:[_photoInfos count]];
                                         for (NSInteger index = 0; index < [_photoInfos count]; index++) {
                                             ImageDownloader *downloader = [[ImageDownloader alloc] initWithDict:_photoInfos[index]];
                                             [downloaders addObject:downloader];
                                         }
                                         self.downloaders = downloaders; // ...link local array with instance array 'downloaders' (Note: same object!).

                                         dispatch_async(dispatch_get_main_queue(), ^{
                                             [self.collectionView reloadData];
                                         });
                                     } else {
                                         NSLog(@"*** {SessionDataError}: %@",error);
                                         // ...Handle Error.
                                     }
                                 }] resume];

}

// -----------------------------------------------------------------------------------------------------------------------
#pragma mark - UICollectionViewDelegate methods
// ...to be implemented later.

// -----------------------------------------------------------------------------------------------------------------------
#pragma mark - UICollectionViewDataSource methods

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return [self.photoInfos count];
}

// -----------------------------------------------------------------------------------------------------------------------

- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"PhotoCell" forIndexPath:indexPath];
    ...
    ...
    return cell;
}    

...
@end


However... Swift's processing pattern is different. The UIControllerView's data-source delegate methods are process PRIOR to the NSURLSession:
--- {UICollectionView numberOfItems...} ---
--- {cellForItemAtIndexPath} ---
--- {cellForItemAtIndexPath} ---
--- {cellForItemAtIndexPath} ---
--- {cellForItemAtIndexPath} ---
--- {cellForItemAtIndexPath} ---
--- {session} ---
--- {session} ---

Here's the Swift 'equivalent' code:

import UIKit

var gPhotoData:Array<Dictionary<String,AnyObject>>?
var gDownloaders:NSMutableArray = NSMutableArray()
var currentImageDownloader:ImageDownloader?
var gSelectedItemIndex:Int?


class ViewController: UIViewController {

    @IBOutlet weak var collectionView: UICollectionView!

    override func viewDidLoad() {
        super.viewDidLoad()
        fetchFlickrPhotoWithSearchString("Ric");
    }

    // -----------------------------------------------------------------------------------------------------
    // MARK: -


    func fetchFlickrPhotoWithSearchString(searchString:String) {

        let url = getURLForString("Ric")

        let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) in
            if let httpRes = response as? NSHTTPURLResponse {
                if httpRes.statusCode == 200 {
                    let string = stringByRemovingFlickrJavaScriptFromData(data)
                    let data = string.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)
                    let JSON: AnyObject? = NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments, error: nil)

                    let rawDataDict = JSON as Dictionary<String,AnyObject>!
                    let photos: AnyObject? = rawDataDict["photos"]
                    gPhotoData = (photos!["photo"] as Array<Dictionary<String,AnyObject>>)

                    let myCount = (gPhotoData!.count - 1)

                    for index in 0...myCount {
                        let smirf = gPhotoData![index]
                        let downloader:ImageDownloader = ImageDownloader(dict: smirf)
                        gDownloaders.addObject(downloader)
                    }

                    dispatch_async(dispatch_get_main_queue(), {
                        // ...do something.
                    })

                }
            }
        }

        task.resume()

    } // ...end class ViewController().

    // =======================================================================================================================
    // MARK: - Action Methods

    @IBAction func exitAction(sender: AnyObject) {
        exit(0)
    }

}

// =======================================================================================================================

extension ViewController: UICollectionViewDataSource {

    // -----------------------------------------------------------------------------------------------------

    func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        if let anyData = gPhotoData {
            return gPhotoData!.count
        }
        return 5  //...arbitrary number to avoid crashing.
    }

    // -----------------------------------------------------------------------------------------------------

    func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
        let cell: AnyObject = collectionView.dequeueReusableCellWithReuseIdentifier("photoCell", forIndexPath:indexPath)
        let photoImageView = cell.viewWithTag!(1) as UIImageView

...
...

        return cell as UICollectionViewCell


    }

}

Note: the above debug msgs were made within their respective break properties.

Question: How do I fire-off the NSURLSession/Swift, with completion BEFORE having the UIViewController's delegate methods fire (as is done in the Objective-C version)?

Frederick C. Lee
  • 9,019
  • 17
  • 64
  • 105

1 Answers1

0

If your target is to show data in the collectionView after it's been retrieved through the session why not just calling collectionView.reloadData() in you dispatch_main_queue?

To avoid the crash in the numberOfRows don't force the un-wrap of the array, but act on the optional: return anyData.count. It will call this code only if anyData is not nil and a return 0 should be ok after the optional un-wrapping.

Danny S
  • 1,291
  • 8
  • 12