Trouble returning an array of user-defined objects out of an escaping closure

I’m learning how to use and manipulate table views with downloaded JSON and am experiencing an issue with a closure. Here is a link to the code on pastebin if anyone wants to see it more nicely formatted than on this sub. pastebin link : https://pastebin.com/axdvumV7

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var objArr : [jsonObject] = []

internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return 5
}

internal func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
    cell.textLabel?.text = "Row Number: \(indexPath.row + 1)"
    return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "toSecondFromFirst", sender: self)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
   
}

override func viewDidLoad() {
    super.viewDidLoad()
   
    // Do any additional setup after loading the view, typically from a nib.
    var tempArr : [jsonObject] = []
    var exampleObject = downloadJson(min: 2140, max: 2145) { (jsonDataObject, error) -> [jsonObject] in
        tempArr.append(jsonDataObject!)
        print(tempArr)
        return tempArr
    }
    print(exampleObject)
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func downloadJson(min: Int, max: Int,completionHandler completion : @escaping (jsonObject?, Error?) ->   [jsonObject]){
    var tempObject = jsonObject()
    var achievID : Int = min
    for _ in min...max {
        achievID += 1
       
        let url = URL(string: "https://us.api.battle.net/wow/achievement/\(achievID)?  locale=en_US&apikey=wh7sqpen5a5gps5sp9mjvp7s9tnrtbsf")
        let urlRequest = URLRequest(url: url!)
        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if error != nil {
                print("error")
            } else {
                guard let httpResponse = response as? HTTPURLResponse else {
                    print("Error converting response as http url response or response is nil")
                    return
                }
                if httpResponse.statusCode == 200 {
                    guard let dataUnwrapped = data else {
                        print("data is nil ")
                        return
                    }
                    do {
                        tempObject = try JSONDecoder().decode(jsonObject.self, from: dataUnwrapped)
                        completion(tempObject,nil)
                    } catch {
                        print("Error decoding json")
                    }
                }
            }
            }.resume()
    }
}
}

I can’t seem to return the array of objects into the variable and don’t understand why. Looking for some pointers and help here. What I want to do is return an array of the jsonObjects so I can determine the amount of rows to draw etc. the whole basis of the listview application kind of relies on getting an array back of usable objects so I populate the list cells. Thanks in advance! Also, jsonObject is defined outside of the code that I linked.

The HTTP requests you are doing run asynchronously, so directly after calling downloadJson, there is no result that could be returned. Your completion handler is also running multiple times, once for each successful request you send, so there wouldn’t even be one single value to return.

From what I see in your code sample, you probably should change your completion handler to take a [jsonObject] as parameter (or [JsonObject], please don’t start type names with a lowercase letter :sweat_smile:) and return Void. In your downloadJson implementation, you can then collect the responses into an array and call the completion handler once, when all the requests are done.

how would I implement what you’re suggesting to do?

How do I run multiple HTTP requests inside of the downloadJson function so I can append it to an array and then return it so I can use that array outside of the scope of the closure?

Change downloadJson to have this signature:

func downloadJson(min: Int, max: Int, completionHandler completion: @escaping ([JsonObject]?, Error?) -> Void)

Then move the whole tempArr stuff into downloadJson. You are already running multiple requests in there. You’ll have to do a little book-keeping to keep track of the requests to know when all of them have finished, a Set<Int> of ids you’re still waiting for an answer to should do fine.

So the high-level summary of downloadJson would be:

  1. Create a temporary array to hold the results and a Set to keep track of which ids are still waiting for the result
  2. For each id (you can use for achievID in min...max btw):
    1. Add the id to the set
    2. Start the request
    3. In the request completion handler: add decoded result to temp array, remove id from Set, if Set is now empty (=> all requests have finished), call your completion handler with the temp array as parameter

Oh and at some point you’ll have to think about how you want to handle errors (does one error mean the result is failed, or do you ignore objects with errors?) and adapt your code accordingly.

is this how you’re suggesting I edit my function? I don’t really understand how I would implement the set ID thing you’re talking about I’ve never really used Sets. Also, how am I going to return an array from the downloadJson function if it returns void?

func downloadJson(min: Int, max: Int, completionHandler completion: @escaping ([JsonObject]?, Error?) -> Void){
    var achievID : Int = min

    var tempArray : [JsonObject] = []
    for achievID in min...max {
        let url = URL(string: "https://us.api.battle.net/wow/achievement/\(achievID)?locale=en_US&apikey=wh7sqpen5a5gps5sp9mjvp7s9tnrtbsf")
        let urlRequest = URLRequest(url: url!)
        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if error != nil {
                print("error")
            } else {
                guard let httpResponse = response as? HTTPURLResponse else {
                    print("Error converting response as http url response or response is nil")
                    return
                }
                if httpResponse.statusCode == 200 {
                    guard let dataUnwrapped = data else {
                        print("data is nil ")
                        return
                    }
                    do {
                        let tempObject = try JSONDecoder().decode(JsonObject.self, from: dataUnwrapped)
                        tempArray.append(tempObject)
                        completion(tempArray, nil)
                    } catch {
                        print("Error decoding json")
                    }
                }
            }
        }.resume()
    }
}

or do I call the completion handler over here

                        do {
                        let tempObject = try JSONDecoder().decode(JsonObject.self, from: dataUnwrapped)
                        tempArray.append(tempObject)
                    } catch {
                        print("Error decoding json")
                    }
                }
            }
        }.resume()
    }
    completion(tempArray,nil)
}

calling the completion handler outside of the .resume() scope with this code

        downloadJson(min: 2140, max: 2142) { (jsonobject, error) in
        print(jsonobject)
    }

returns an optional empty arrayt which I’m sure is just the empty array I instantiated at the beginning of the function definition.

Except for the Set, yes. Right now, the completion handler (the one that is passed to downloadJson that is, not the one passed to dataTask(...)) would still be called once for each request where you only want it called for the last one that finishes. Look at the docs for Set, it's pretty easy.

You're not. Remember that asynchronous completion handlers are not called before the method you pass the completion handler to returns, but at some later point (when the HTTP request finishes in this case). Because of that, you can not return the resulting value directly. Instead, you pass the resulting value into the completion handler which can then use the value to do whatever.

So is there a way I can update the amount of rows in the tableview and the information inside of it after the the asynchronous completion handler finishes downloading the data and puts it into an array? I want to be able to download the data into an array and then use the array with all the json objects in other parts of my code. What other instruments are available for me to be able to do that?

I’m looking to do something like this
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {

internal func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    let dataArray = downloadJson(min: 2140, max: 2143) { (jsonDataArray, error) in
        return jsonDataArray?
    }
    return dataArray.count
}

The simplest way would be something like this (assuming this is called somewhere from the table view controller, viewDidLoad, viewDidAppear, something like that):

downloadJson(min: 2140, max: 2145) { (jsonObjects, error) in
    self.objArray = jsonObjects ?? []
    self.tableView.reloadData()
}

Use objArray as basis for all your table view methods, so when the view controller comes on screen, it will first display no cells, since nothing is loaded yet and self.objArray is empty. Then the completion handler gets called, updates self.objArray with the received objects, and reloads the table view to display the new data.

Got an error,
UITableView.reloadData() must be used from main thread only
=================================================================
Main Thread Checker: UI API called on a background thread: -[UITableView reloadData]
PID: 34948, TID: 692298, Thread name: (none), Queue name: NSOperationQueue 0x60800003b940 (QOS: UNSPECIFIED), QoS: 0
Backtrace:
4 Tableviews 0x00000001018aa11f T010Tableviews14ViewControllerC05tableB0SiSo07UITableB0C_Si21numberOfRowsInSectiontFySayAA10JsonObjectCGSg_s5Error_pSgtcfU + 239
5 Tableviews 0x00000001018aa1a1 _T010Tableviews14ViewControllerC05tableB0SiSo07UITableB0C_Si21numberOfRowsInSectiontFySayAA10JsonObjectCGSg_s5Error_pSgtcfU_TA + 17
6 Tableviews 0x00000001018ab793 T010Tableviews14ViewControllerC12downloadJsonySi3min_Si3maxySayAA0E6ObjectCGSg_s5Error_pSgtc17completionHandlertFy10Foundation4DataVSg_So11URLResponseCSgALtcfU + 1379
7 Tableviews 0x00000001018abbf5 _T010Tableviews14ViewControllerC12downloadJsonySi3min_Si3maxySayAA0E6ObjectCGSg_s5Error_pSgtc17completionHandlertFy10Foundation4DataVSg_So11URLResponseCSgALtcfU_TA + 37
8 Tableviews 0x00000001018abd0e _T010Foundation4DataVSgSo11URLResponseCSgs5Error_pSgIegxxx_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 270
9 CFNetwork 0x00000001062d2c46 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
10 CFNetwork 0x00000001062d2497 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 147
11 Foundation 0x0000000101bd9363 NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 7
12 Foundation 0x0000000101bd91ca -[NSBlockOperation main] + 68
13 Foundation 0x0000000101bd76b2 -[__NSOperationInternal _start:] + 766
14 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
15 libdispatch.dylib 0x0000000106fc3e14 _dispatch_block_invoke_direct + 592
16 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
17 libdispatch.dylib 0x0000000106fc3e14 _dispatch_block_invoke_direct + 592
18 libdispatch.dylib 0x0000000106fc3ba4 dispatch_block_perform + 109
19 Foundation 0x0000000101bd375b __NSOQSchedule_f + 337
20 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
21 libdispatch.dylib 0x0000000106fc4b35 _dispatch_continuation_pop + 967
22 libdispatch.dylib 0x0000000106fc2fb0 _dispatch_async_redirect_invoke + 780
23 libdispatch.dylib 0x0000000106fca3c8 _dispatch_root_queue_drain + 664
24 libdispatch.dylib 0x0000000106fca0d2 _dispatch_worker_thread3 + 132
25 libsystem_pthread.dylib 0x00000001074e9169 _pthread_wqthread + 1387
26 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
2018-05-03 20:23:55.662557-0500 Tableviews[34948:692298] [reports] Main Thread Checker: UI API called on a background thread: -[UITableView reloadData]
PID: 34948, TID: 692298, Thread name: (none), Queue name: NSOperationQueue 0x60800003b940 (QOS: UNSPECIFIED), QoS: 0
Backtrace:
4 Tableviews 0x00000001018aa11f T010Tableviews14ViewControllerC05tableB0SiSo07UITableB0C_Si21numberOfRowsInSectiontFySayAA10JsonObjectCGSg_s5Error_pSgtcfU + 239
5 Tableviews 0x00000001018aa1a1 _T010Tableviews14ViewControllerC05tableB0SiSo07UITableB0C_Si21numberOfRowsInSectiontFySayAA10JsonObjectCGSg_s5Error_pSgtcfU_TA + 17
6 Tableviews 0x00000001018ab793 T010Tableviews14ViewControllerC12downloadJsonySi3min_Si3maxySayAA0E6ObjectCGSg_s5Error_pSgtc17completionHandlertFy10Foundation4DataVSg_So11URLResponseCSgALtcfU + 1379
7 Tableviews 0x00000001018abbf5 _T010Tableviews14ViewControllerC12downloadJsonySi3min_Si3maxySayAA0E6ObjectCGSg_s5Error_pSgtc17completionHandlertFy10Foundation4DataVSg_So11URLResponseCSgALtcfU_TA + 37
8 Tableviews 0x00000001018abd0e _T010Foundation4DataVSgSo11URLResponseCSgs5Error_pSgIegxxx_So6NSDataCSgAGSo7NSErrorCSgIeyByyy_TR + 270
9 CFNetwork 0x00000001062d2c46 __75-[__NSURLSessionLocal taskForClass:request:uploadFile:bodyData:completion:]_block_invoke + 19
10 CFNetwork 0x00000001062d2497 __49-[__NSCFLocalSessionTask _task_onqueue_didFinish]_block_invoke + 147
11 Foundation 0x0000000101bd9363 NSBLOCKOPERATION_IS_CALLING_OUT_TO_A_BLOCK + 7
12 Foundation 0x0000000101bd91ca -[NSBlockOperation main] + 68
13 Foundation 0x0000000101bd76b2 -[__NSOperationInternal _start:] + 766
14 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
15 libdispatch.dylib 0x0000000106fc3e14 _dispatch_block_invoke_direct + 592
16 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
17 libdispatch.dylib 0x0000000106fc3e14 _dispatch_block_invoke_direct + 592
18 libdispatch.dylib 0x0000000106fc3ba4 dispatch_block_perform + 109
19 Foundation 0x0000000101bd375b __NSOQSchedule_f + 337
20 libdispatch.dylib 0x0000000106fbe848 _dispatch_client_callout + 8
21 libdispatch.dylib 0x0000000106fc4b35 _dispatch_continuation_pop + 967
22 libdispatch.dylib 0x0000000106fc2fb0 _dispatch_async_redirect_invoke + 780
23 libdispatch.dylib 0x0000000106fca3c8 _dispatch_root_queue_drain + 664
24 libdispatch.dylib 0x0000000106fca0d2 _dispatch_worker_thread3 + 132
25 libsystem_pthread.dylib 0x00000001074e9169 _pthread_wqthread + 1387
26 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
2018-05-03 20:24:07.481637-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
3 UIKit 0x000000010323ca2b -[UIView(Internal) _addSubview:positioned:relativeTo:] + 1940
4 UIKit 0x000000010329af68 -[UITableView _addSubview:positioned:relativeTo:] + 124
5 UIKit 0x000000010326ef62 -[UIScrollView(UIScrollViewInternal) _addContentSubview:atBack:] + 564
6 UIKit 0x000000010329acc1 -[UITableView _addContentSubview:atBack:] + 257
7 UIKit 0x00000001032befed __53-[UITableView _configureCellForDisplay:forIndexPath:]_block_invoke + 2655
8 UIKit 0x0000000103234bca +[UIView(Animation) performWithoutAnimation:] + 90
9 UIKit 0x00000001032be4aa -[UITableView _configureCellForDisplay:forIndexPath:] + 240
10 UIKit 0x00000001032d165a -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 930
11 UIKit 0x00000001032d1b44 -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 74
12 UIKit 0x0000000103298f0a -[UITableView _updateVisibleCellsNow:isRecursive:] + 3168
13 UIKit 0x00000001032b9840 -[UITableView layoutSubviews] + 176
14 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
15 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
16 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
17 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
18 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
19 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
20 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
21 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
22 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
23 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.484022-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
3 UIKit 0x0000000103229e9e __UIViewWasRemovedFromSuperview + 169
4 UIKit 0x0000000103229990 -[UIView(Hierarchy) removeFromSuperview] + 479
5 UIKit 0x00000001035aafc1 -[UITableViewCell removeFromSuperview] + 210
6 UIKit 0x00000001032d168a -[UITableView _createPreparedCellForGlobalRow:withIndexPath:willDisplay:] + 978
7 UIKit 0x00000001032d1b44 -[UITableView _createPreparedCellForGlobalRow:willDisplay:] + 74
8 UIKit 0x0000000103298f0a -[UITableView _updateVisibleCellsNow:isRecursive:] + 3168
9 UIKit 0x00000001032b9840 -[UITableView layoutSubviews] + 176
10 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
11 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
12 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
13 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
14 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
15 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
16 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
17 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
18 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
19 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.485758-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
3 UIKit 0x0000000103229e9e __UIViewWasRemovedFromSuperview + 169
4 UIKit 0x0000000103229990 -[UIView(Hierarchy) removeFromSuperview] + 479
5 UIKit 0x000000010329ab89 -[UITableView _adjustExtraSeparators] + 1937
6 UIKit 0x00000001032b9861 -[UITableView layoutSubviews] + 209
7 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
8 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
9 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
10 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
11 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
12 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
13 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
14 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
15 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
16 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.487171-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x0000000103ce2cd6 -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 218
3 UIKit 0x000000010322c0ed __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 112
4 Foundation 0x0000000101bf15a7 -[NSISEngine withBehaviors:performModifications:] + 131
5 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
6 UIKit 0x0000000103229e9e __UIViewWasRemovedFromSuperview + 169
7 UIKit 0x0000000103229990 -[UIView(Hierarchy) removeFromSuperview] + 479
8 UIKit 0x000000010329ab89 -[UITableView _adjustExtraSeparators] + 1937
9 UIKit 0x00000001032b9861 -[UITableView layoutSubviews] + 209
10 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
11 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
12 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
13 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
14 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
15 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
16 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
17 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
18 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
19 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.488835-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
3 UIKit 0x0000000103229e9e __UIViewWasRemovedFromSuperview + 169
4 UIKit 0x0000000103229990 -[UIView(Hierarchy) removeFromSuperview] + 479
5 UIKit 0x000000010329ab89 -[UITableView _adjustExtraSeparators] + 1937
6 UIKit 0x00000001032b9861 -[UITableView layoutSubviews] + 209
7 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
8 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
9 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
10 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
11 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
12 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
13 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
14 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
15 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
16 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.490148-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101bf1540 -[NSISEngine withBehaviors:performModifications:] + 28
2 UIKit 0x0000000103ce2cd6 -[UIView(AdditionalLayoutSupport) _switchToLayoutEngine:] + 218
3 UIKit 0x000000010322c0ed __45-[UIView(Hierarchy) _postMovedFromSuperview:]_block_invoke + 112
4 Foundation 0x0000000101bf15a7 -[NSISEngine withBehaviors:performModifications:] + 131
5 UIKit 0x000000010322c005 -[UIView(Hierarchy) _postMovedFromSuperview:] + 835
6 UIKit 0x0000000103229e9e __UIViewWasRemovedFromSuperview + 169
7 UIKit 0x0000000103229990 -[UIView(Hierarchy) removeFromSuperview] + 479
8 UIKit 0x000000010329ab89 -[UITableView _adjustExtraSeparators] + 1937
9 UIKit 0x00000001032b9861 -[UITableView layoutSubviews] + 209
10 UIKit 0x0000000103243808 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1515
11 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
12 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
13 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
14 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
15 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
16 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
17 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
18 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
19 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.491406-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101de5aba -[NSISEngine _optimizeWithoutRebuilding] + 61
2 Foundation 0x0000000101bf6639 -[NSISEngine optimize] + 108
3 Foundation 0x0000000101de301d -[NSISEngine performPendingChangeNotifications] + 84
4 UIKit 0x000000010324309e -[UIView(CALayerDelegate) _wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:] + 167
5 UIKit 0x0000000103243848 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1579
6 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
7 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
8 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
9 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
10 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
11 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
12 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
13 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
14 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.492092-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101de5aba -[NSISEngine _optimizeWithoutRebuilding] + 61
2 Foundation 0x0000000101bf6639 -[NSISEngine optimize] + 108
3 Foundation 0x0000000101de301d -[NSISEngine performPendingChangeNotifications] + 84
4 UIKit 0x0000000103243b0f -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2290
5 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
6 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
7 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
8 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
9 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
10 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
11 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
12 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
13 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.492787-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101de5aba -[NSISEngine _optimizeWithoutRebuilding] + 61
2 Foundation 0x0000000101bf6639 -[NSISEngine optimize] + 108
3 Foundation 0x0000000101de301d -[NSISEngine performPendingChangeNotifications] + 84
4 UIKit 0x000000010324309e -[UIView(CALayerDelegate) _wantsReapplicationOfAutoLayoutWithLayoutDirtyOnEntry:] + 167
5 UIKit 0x0000000103243848 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 1579
6 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
7 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
8 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
9 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
10 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
11 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
12 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
13 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
14 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)
2018-05-03 20:24:07.493506-0500 Tableviews[34948:692299] This application is modifying the autolayout engine from a background thread after the engine was accessed from the main thread. This can lead to engine corruption and weird crashes.
Stack:(
0 Foundation 0x0000000101de5df7 _AssertAutolayoutOnAllowedThreadsOnly + 77
1 Foundation 0x0000000101de5aba -[NSISEngine _optimizeWithoutRebuilding] + 61
2 Foundation 0x0000000101bf6639 -[NSISEngine optimize] + 108
3 Foundation 0x0000000101de301d -[NSISEngine performPendingChangeNotifications] + 84
4 UIKit 0x0000000103243b0f -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2290
5 QuartzCore 0x00000001095c061a -[CALayer layoutSublayers] + 177
6 QuartzCore 0x00000001095c482b _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 395
7 QuartzCore 0x000000010954b29f _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 343
8 QuartzCore 0x0000000109578940 _ZN2CA11Transaction6commitEv + 568
9 QuartzCore 0x0000000109579005 _ZN2CA11Transaction14release_threadEPv + 213
10 libsystem_pthread.dylib 0x00000001074eb1aa _pthread_tsd_cleanup + 534
11 libsystem_pthread.dylib 0x00000001074eaee9 _pthread_exit + 79
12 libsystem_pthread.dylib 0x00000001074e924b _pthread_wqthread + 1613
13 libsystem_pthread.dylib 0x00000001074e8be9 start_wqthread + 13
)

Oh, right, wrap the code inside the completion handler in DispatchQueue.main.async { ... } (import Dispatch if needed), to get back from the url session delegate queue to the main thread.

Which completion handler?

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var objArray : [JsonObject] = []

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    downloadJson(min: 2140, max: 2145) { (jsonObjects, error) in
        DispatchQueue.main.async {
            
        self.objArray = jsonObjects ?? []
        tableView.reloadData()
        }
    }
    return objArray.count
}

is this what you’re looking to do?

Almost. You only want the download to happen once, when the view controller appears, not every time the table view queries the count, which can happen multiple times.

So the code is somewhat functioning at this point but with some weird and definitely unusable functionality. the table is constantly changing title values and is spamming itself. It’s seems like there is an endless update loop

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from	 a nib.
    downloadJson(min: 2140, max: 2145) { (jsonObjects, error) in
        DispatchQueue.main.async {
            self.objArray = jsonObjects ?? []
            self.tableView.reloadData()
        }
    }
}

This code throws an error because of an “Ambiguous reference to member ‘tableView(_:numberOfRowsInSection:)’”

I think I figured it out… I didnt’ have an IBOutlet for my tableView, which is why I couldn’t reference the reloadData() and I didn’t realize that is what you were talking about. This is what the final code looks like… Thanks a lot for the help and let me kn ow if there is anything else that you would add. I appreciate your patience on getting me through all of this…

//
//  ViewController.swift
//  Tableviews

import UIKit

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var objArray : [JsonObject] = []
@IBOutlet weak var tableViewDataOutlet: UITableView!

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    
    return objArray.count
}

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = UITableViewCell(style: .default, reuseIdentifier: "Cell")
    cell.textLabel?.text = "\(objArray[indexPath.row].title)"
    return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    performSegue(withIdentifier: "toSecondFromFirst", sender: self)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
}

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from	 a nib.
    downloadJson(min: 2140, max: 2145) { (jsonObjects, error) in
        DispatchQueue.main.async {
            self.objArray = jsonObjects ?? []
            self.tableViewDataOutlet.reloadData()
        }
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}

func downloadJson(min: Int, max: Int, completionHandler completion: @escaping ([JsonObject]?, Error?) -> Void){
    var achievID : Int = min
    var tempArray : [JsonObject] = []
    for achievID in min...max {
        let url = URL(string: "https://us.api.battle.net/wow/achievement/\(achievID)?locale=en_US&apikey=wh7sqpen5a5gps5sp9mjvp7s9tnrtbsf")
        let urlRequest = URLRequest(url: url!)
        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if error != nil {
                print("error")
            } else {
                guard let httpResponse = response as? HTTPURLResponse else {
                    print("Error converting response as http url response or response is nil")
                    return
                }
                if httpResponse.statusCode == 200 {
                    guard let dataUnwrapped = data else {
                        print("data is nil ")
                        return
                    }
                    do {
                        let tempObject = try JSONDecoder().decode(JsonObject.self, from: dataUnwrapped)
                        tempArray.append(tempObject)
                        completion(tempArray,nil)
                    } catch {
                        print("Error decoding json")
                    }
                }
            }
            }.resume()
    }
    
}

}

1 Like