EXC_BAD_ACCESS error when inserting NSManagedObject - ios

I get an EXC_BAD_ACCESS error when inserting an NSManagedObject at the superclass init call. The error doesn't always occur, so my guess is that it has something to do with threading, however I'm very new to iOS development so I might be completely wrong.
In the code below I mark the line where the error occurs with a comment.
import Foundation
import CoreData
#objc(Measurement)
public class Measurement: NSManagedObject {
convenience init(sensorId: Int32, fromDatetime: Int64, pm10: Float, pm25: Float, airQualityIndex: Float32, pollutionLevel: Int16,
latitude: Double, longitude: Double, source: String, windDeg: Float32, windSpeed: Float32,
context: NSManagedObjectContext) {
if let ent = NSEntityDescription.entity(forEntityName: "Measurement", in: context) {
self.init(entity: ent, insertInto: context) // Thread 10: EXC_BAD_ACCESS (code=1, address=0xfffffffc)
self.fromDatetime = fromDatetime
self.pm10 = pm10
// omitted value asignments
} else {
fatalError("Unable to find entity name Measurement")
}
}
}
The inserting happens after a call to an api inside a for loop.
let task = session.dataTask(with: request) { (data, response, error) in
// if an error occurs, print it and re-enable the UI
func displayError(_ error: String) {
print(error)
}
/* GUARD: Was there an error? */
guard (error == nil) else {
displayError("There was an error with your request: \(String(describing: error))")
return
}
/* GUARD: Did we get a successful 2XX response? */
guard let statusCode = (response as? HTTPURLResponse)?.statusCode, statusCode >= 200 && statusCode <= 299 else {
displayError("Your request returned a status code other than 2xx!")
return
}
/* GUARD: Was there any data returned? */
guard let data = data else {
displayError("No data was returned by the request!")
return
}
// parse the data
let parsedResult: [[String:AnyObject]]!
do {
parsedResult = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as! [[String:AnyObject]]
} catch {
displayError("Could not parse the data as JSON: '\(data)'")
return
}
let delegate = UIApplication.shared.delegate as! AppDelegate
let stack = delegate.stack
var counter = 0
for measurement in parsedResult {
if counter == 0 {
print(measurement)
}
guard let sensorId = measurement[Constants.MeasurementModelKeys.sensorId] as? Int32 else {
print("Cannot find key 'sensor_id' in \(measurement)")
continue
}
// other guarded assignments ...
_ = Measurement(sensorId: sensorId, fromDatetime: fromDatetime, pm10: pm10, pm25: pm25,
airQualityIndex: airQualityIndex, pollutionLevel: pollutionLevel, latitude: latitude, longitude: longitude,
source: source, windDeg: windDeg, windSpeed: windSpeed,
context: stack.context) // << this is where the insertion happens
counter += 1
}
print("counter counted: \(counter) elements")
}
// start the task!
task.resume()
}
Any help is much appreciated (including tips on how to track down the exact cause of the error).

Core Data is indeed very touchy about threading. Inserting new objects in a context should be performed on that context queue.
Try this :
stack.context.perform {
_ = Measurement(sensorId: sensorId, fromDatetime: fromDatetime, pm10: pm10, pm25: pm25,
airQualityIndex: airQualityIndex, pollutionLevel: pollutionLevel, latitude: latitude, longitude: longitude,
source: source, windDeg: windDeg, windSpeed: windSpeed,
context: stack.context)
}
Note that this is now an asynchronous call. Make sure this doesn't break your code somewhere else.

Related

Closure returning data before async work is done

UPDATED WITH PROPOSED SOLUTION AND ADDITIONAL QUESTION
I'm officially stuck and also in callback hell. I have a call to Firebase retrieving all articles in the FireStore. Inside each article object is a an Image filename that translates into a storage reference location that needs to be passed to a function to get the absolute URL back. I'd store the URL in the data, but it could change. The problem is the ArticleListener function is prematurely returning the closure (returnArray) without all the data and I can't figure out what I'm missing. This was working fine before I added the self.getURL code, but now it's returning the array back empty and then doing all the work.
If anyone has some bonus tips here on chaining the methods together without resorting to PromiseKit or GCD that would be great, but open to all suggestions to get this to work as is
and/or refactoring for more efficiency / readability!
Proposed Solution with GCD and updated example
This is calling the Author init after the Article is being created. I am trying to transform the dataDict dictionary so it get's used during the Author init for key ["author"]. I think I'm close, but not 100% sure if my GCD enter/leave calls are happening in the right order
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
print("Error in setting up snapshot listener - \(error)")
} else {
let fireStoreDispatchGrp = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
//NEW EXAMPLE WITH ADDITIONAL TASK HERE
if let author = $0.data()["author"] as? DocumentReference {
author.getDocument() {(authorSnapshot, error) in
fireStoreDispatchGrp.enter() //1
if let error = error {
print("Error getting Author from snapshot inside Article getDocumentFunction - leaving dispatch group and returning early")
fireStoreDispatchGrp.leave()
return
}
if let newAuthor = authorSnapshot.flatMap(Author.init) {
print("Able to build new author \(newAuthor)")
dataDict["author"] = newAuthor
dataDict["authorId"] = authorSnapshot?.documentID
print("Data Dict successfully mutated \(dataDict)")
}
fireStoreDispatchGrp.leave() //2
}
}
///END OF NEW EXAMPLE
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
fireStoreDispatchGrp.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
dataDict["image"] = url.absoluteString
case .failure(let error):
print("Error getting URL for author: \n Error: \(error) \n forReference: \(reference) \n forArticleID: \(id)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
returnArray.append(newArticle)
}
fireStoreDispatchGrp.leave() ///3
}
}
}
//Completion block
print("Exiting dispatchGroup all data should be setup correctly")
fireStoreDispatchGrp.notify(queue: .main) { ///4
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
Original Code
Calling Setup Code
self.manager.SetupArticleListener() { [weak self] articles in
print("🌈🌈🌈🌈🌈🌈🌈In closure function to update articles🌈🌈🌈🌈🌈🌈🌈")
self?.articles = articles
}
Article Listener
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
}
}
}
print("🌈🌈🌈🌈🌈🌈🌈 sending back completion array \(returnArray)🌈🌈🌈🌈🌈🌈🌈")
completion(returnArray)
}
}
updateListeners(for: listener)
}
GetURL
private func getURL(reference: StorageReference, _ result: #escaping (Result<URL, Error>) -> Void) {
reference.downloadURL() { (url, error) in
if let url = url {
result(.success(url))
} else {
if let error = error {
print("error")
result(.failure(error))
}
}
}
}
You need dispatch group as the for loop contains multiple asynchronous calls
public func SetupArticleListener(completion: #escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
let g = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = $0.data() //mutable copy of the dictionary data
let id = $0.documentID
if let imageURL = $0.data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
g.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
g.leave() /// 3
}
}
}
g.notify(queue:.main) { /// 4
print("🌈🌈🌈🌈🌈🌈🌈 sending back completion array \(returnArray)🌈🌈🌈🌈🌈🌈🌈")
completion(returnArray)
}
}
}
updateListeners(for: listener)
}

Nested DispatchGroup

I try use nested DispatchGroup, there is my code :
class TranslateService {
private let myGroup = DispatchGroup()
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for index in 0...text.count - 1 {
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(text[index])&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += "،" + flattArr[0]
}
}
}
}
self.myGroup.leave()
}
}catch {
responseError = error
self.myGroup.leave()
}
}
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
}
}
}
}
}
class AddressService {
private let translateService: TranslateService = TranslateService()
private let myGroup = DispatchGroup()
func fetchAddressFromGeographicalLocation(latitude: Double, longitude: Double,closure:#escaping ((_ success:String,_ name:String,_ error:Error?) -> Void)) {
var address: String = ""
let name: String = ""
var responseError: Error?
if let url = URL(string:"https://maps.googleapis.com/maps/api/geocode/json?latlng=\(latitude),\(longitude)&key=AIzaSyAdEzHZfZWyjLMuuW92w5fkR86S3-opIF0&language=fa&region=IR&locale=fa"){
self.myGroup.enter()
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
self.myGroup.leave()
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let addressDic = json as? [String:Any] {
if let result = addressDic["results"] as? [AnyObject] {
if result.count > 0 {
let flattRes = Utilities.shareInstance.flatMapArray(result)
let item = flattRes[0]
address = item["formatted_address"] as? String ?? ""
var res = address
if res.isContainEnglishCharachter {
self.myGroup.enter()
let resArr = res.components(separatedBy: ",")
var all : [String] = []
for item in resArr {
if item != " " {
all.append(item)
}}
self.translateService.translateText(text: all, closure: {stringAdd,error in
self.myGroup.enter()
if error != nil {
self.myGroup.leave()
}else {
address = stringAdd ?? ""
self.myGroup.leave()
} })
}else {
self.myGroup.leave()
}
}
}
}
}catch {
responseError = error
self.myGroup.leave()
}
self.myGroup.notify(queue: .main) {
// print("Finished all requests.")
closure(address, name, responseError)
}
}
}
}
}
All I want is that the myGroup I put in the class AddressService waiting for the myGroup that I put in the class TranslateService.
but now self.myGroup.notify not call in the AddressService class, So closure not work.
How can solve this problem, Thank you for all the answers.
I think you are over complicating it.
If I understood a bit, what you want to do is the following:
Get an address from the Address service.
Translate some words of that address, one by one, using the translation service.
When using the Address service there is only one call being done, so there is no need to use Dispatch Groups at this point. Dispatch Groups are used to synchronize more than one async call.
For your Translation service you can make good use of the Dispatch groups, since you are doing calls to the service inside a for loop. The problem here is, that the implementation is slightly wrong. You are setting the notification block inside the for loop, and it should be outside, so that it gets only triggered once, when all the calls inside the loop are done.
So move this block outside the for loop in the Translation service:
self.myGroup.notify(queue: .main) {
print("Finished all requests.")
print(translateString)
closure(translateString, responseError)
}
Now "Finished all requests." will only be printed once, when all requests are done.
In the address service you do not need dispatch groups at all. Just wait until the completion block is called.
self.translateService.translateText(text: all, closure: {stringAdd,error in
Everything is done here already.
}

How to return a value within an if let statement in Swift 4? [duplicate]

This question already has an answer here:
Return a string from a web scraping function in swift
(1 answer)
Closed 4 years ago.
How can I return a value within an if let statement to be further returned within a function? Here is the code:
func loadUrl(url:String) -> String {
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]] {
print("Test from if let 1")
if let first = results[0] as? [String:Any] {
print("Test from if let 2")
var cityStateLocation = first["formatted_address"]!
return cityStateLocation
//What needs to be returned
}
}
DispatchQueue.main.async {
print("No Error")
}
} catch {
DispatchQueue.main.async {
print("Cannot connect to the server.")
}
}
}
}
What I would like to be able to do is take cityStateLocation and return it in the func, but because it is a part of an if let statement within an .async method I don't know how to do that. Could someone please explain?
EDIT: I need the return value of cityStateLocation to equal a variable in a separate function. Here is the separate function:
#IBAction func continueButton(_ sender: Any) {
var cityState:String
if locationSwitch.isOn == true {
print(location.latitude)
print(location.longitude)
let url = "https://maps.googleapis.com/maps/api/geocode/json?latlng=\(location.latitude),\(location.longitude)&result_type=locality&key=AIzaSyDI-ZacHyPbLchRhkoaUTDokwj--z_a_jk"
loadUrl(url: url)
cityState = loadUrl(url: url)
} else {
cityState = ""
}
CoreDataHandler.saveObject(locationLocality: cityState)
}
Edit 2: The main reason why the "duplicate answer" is not a duplicate is that my code needs to call the return of this function within a separate function then save it to Core Data. Also, my code is not using an array.
You could modify your function to include a closure. For instance:
func loadUrl(url: String, completionHandler: #escaping (_ location: String?) -> (Void)) {
And then, where you want to return it, you'd pass it in as such.
completionHandler(cityStateLocation)
I made it an optional so that, in your fail paths, you could return nil.
Then, where you call the function would change. Using trailing closure syntax, it could look like this:
loadUrl(url: "someurl.com/filepath.txt") { optionalLocation in
guard let nonOptionalLocation = optionalLocation else {
// Location was nil; Handle error case here
return
}
// Do something with your location here, like setting UI or something
}
This is a fairly common pattern when dealing with asynchronous activity, such as working with network calls.
The simplest (perhaps no the prettiest), way of doing this would simply be to declare and instantiate a variable above the dispatch queue. Then you can set the variable equal to whatever you want, within the dispatch queue, and return it afterwards. You can change the type of ret, so that it suits your needs more directly.
func loadUrl(url:String) -> String {
var ret = NSObject()
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]] {
print("Test from if let 1")
if let first = results[0] as? [String:Any] {
print("Test from if let 2")
var cityStateLocation = first["formatted_address"]!
ret = cityStateLocation
//What needs to be returned
}
}
DispatchQueue.main.async {
print("No Error")
}
} catch {
DispatchQueue.main.async {
print("Cannot connect to the server.")
}
}
}
return ret
}
DispatchQueue.global().async will cause the coded included in the closure to be executed at some point the future, meaning you loadUrl function will return (almost) immediately.
What you need is some kind of callback which can be called when you have a result (AKA closure)
This is just another way to approach the problem, the difference between this and Josh's example is simply, I provide an additional closure to handle the errors
func loadUrl(url:String, complition: #escaping (String?) -> Void, fail: #escaping (Error) -> Void) {
DispatchQueue.global().async {
do {
let appUrl = URL(string:url)!
let data = try Data(contentsOf:appUrl)
let json = try JSONSerialization.jsonObject(with: data) as! [String:Any]
print("Test from do")
if let results = json["results"] as? [[String:Any]], !results.isEmpty {
print("Test from if let 1")
let first = results[0]
print("Test from if let 2")
if let cityStateLocation = first["formatted_address"] as? String {
complition(cityStateLocation)
} else {
complition(nil)
}
} else {
complition(nil)
}
} catch let error {
fail(error)
}
}
}
Which you might call using something like...
loadUrl(url: "your awesome url", complition: { (value) in
guard let value = value else {
// No value
return
}
// process value
}) { (error) in
// Handle error
}

Add try catch and convert a function to a generic class swift

In one of my view controllers I have a function to establish a tcp connection , send and receive messages which is as follows.
func tcpConnection(host: NSString){
var client = TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
self.recivedMsgFromServer = recievedMsg
log?.debug("Recieved msg\(recievedMsg)")
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
}
I am using this piece of code twice in the same view controller. So I want to have a generic class for this function . Also I have many guards which I want to replace with a single try catch block.
Also after receiving a message I am doing different things in both the tcp connection functions.
Following is what I have tried until now.
class TcpService{
var jsonString : String = ""
func tcpConnection(host: NSString){
do {
var client = try TCPClient(addr: String(host) as String, port: Config.tcpPort)
let (success, err) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(success)")
guard success else { log?.error("Cannot Connect \(err)"); return}
let (successMsg, failmsg) = client.send(str: self.jsonString)
let data = client.read(Config.expectedByteLength)
guard let d = data else { return }
guard let recievedMsg = String(bytes: d, encoding: NSUTF8StringEncoding) else { return }
log?.debug("Recieved msg\(recievedMsg)")
/*Do Something different in every viewController
//For Example
self.Info = Mapper<Info>().map(recievedMsg)
log?.debug("Status\(self.Info?.Status)")
*/
let (clientClosed, errormsg) = client.close()
guard clientClosed else { return }
} catch {
let fetchError = error as NSError
print(fetchError)
// Handle Error
}
}
}
When I try to call this function from the new class in my view controller it does not behave in the same way the function in the view controller did.
Is there anything I should change in the way I have created the class?
Any help will be appreciated as I am very new to swift. Thank you
This is a way your problem could be solved. Skip the custom error throwing part if you don't actually need it. The way to refactor it is using callback function, just send your function into this one, i.e.:
TCPService.connect("some_host", message: "your_message") { response in
Mapper<Info>().map(response)
}
Code:
import Foundation
extension NSError {
static func error(with localized: String) -> NSError {
return NSError(domain: "error", code: 2, userInfo: [NSLocalizedDescriptionKey: localized])
}
}
class TCPService {
static func connect(host: String, andSend message: String, onComplete: String -> ()) {
do {
let client = try TCPClient(addr: host, port: Config.tcpPort)
let (successfulConnection, error) = client.connect(timeout: Config.tcpConnectionTimeout)
log?.debug("Connected \(successfulConnection)")
guard successfulConnection else {
throw NSError.error(with: error)
log?.error("Cannot Connect \(error)")
return
}
let (successfullySent, failureMessage) = client.send(str: message)
let data = client.read(Config.expectedByteLength)
guard successfullySent,
let d = data,
receivedMessage = String(bytes: d, encoding: NSUTF8StringEncoding) else {
throw NSError.error(with: failureMessage)
return
}
log?.debug("Received msg\(receivedMessage)")
onComplete(receivedMessage)
let (clientClosed, errorMessage) = client.close()
guard clientClosed else {
throw NSError.error(with: errorMessage)
return
}
} catch let error as NSError {
let fetchError = error as NSError
print(fetchError)
// Handle errors
}
}
}

Alamofire request gives memory warning

I am using a Master Detail Application. Master Screen is a Dashboard and on selecting an item, moves to the detailed screen where I trigger an Alamofire request in the backend
Below is the snippet
class APIManager: NSObject {
class var sharedManager: APIManager {
return _sharedManager
}
private var requests = [Request]()
// Cancel any ongoing download
func cancelRequests() {
if requests.count > 0 {
for request in requests {
request.cancel()
}
}
}
func getData(completion: (dataSet: [Data]?, error: NSError?) -> Void) {
let request = Alamofire.request(.GET, "http://request")
.response { (request, response, data, error) in
dispatch_async(dispatch_get_main_queue(), {
if(error == nil) {
if let response = data, data = (try? NSJSONSerialization.JSONObjectWithData(response, options: [])) as? [NSDictionary] {
var dataSet = [Data]()
for (_, dictionary) in data.enumerate() {
let lat = dictionary["Latitude"]
let lng = dictionary["Longitude"]
let id = dictionary["ID"] as! Int
let data = Data(lat: lat!, long: lng!, id: shuttleID)
dataSet.append(data)
}
completion(dataSet: dataSet, error: nil)
}
} else { completion(dataSet: nil, error: error) }
})
}
requests.append(request)
}
}
I have a singleton API manager class and from the detail view controller I call getData() function. Everything works fine.
But, when I push and pop repeatedly, I see rapid increase in the memory and after 10-15 attempts, I get memory warning. However in the AppDelegate I am managing it to show an Alert message and adding a delay timer for 8 seconds. But however after 20-25 attempts app crashes due to memory warning.
In viewWillDisappear(), I cancel any ongoing requests also. But I couldn't able to stop memory warning issue. I commented the part where I call the request, I see no issues, even memory consumption is less.
I welcome ideas.
The problem is you are never removing the requests that you append to the member variable 'requests'.
You will need to ensure to remove the request when you either cancel it or when the request completes successfully.
Do the following modifications-
func cancelRequests() {
if requests.count > 0 {
for request in requests {
request.cancel()
}
}
requests.removeAll() //Delete all canseled requests
}
also
func getData(completion: (dataSet: [Data]?, error: NSError?) -> Void) {
let request = Alamofire.request(.GET, "http://request")
.response { (request, response, data, error) in
dispatch_async(dispatch_get_main_queue(), {
if(error == nil) {
if let response = data, data = (try? NSJSONSerialization.JSONObjectWithData(response, options: [])) as? [NSDictionary] {
var dataSet = [Data]()
for (_, dictionary) in data.enumerate() {
let lat = dictionary["Latitude"]
let lng = dictionary["Longitude"]
let id = dictionary["ID"] as! Int
let data = Data(lat: lat!, long: lng!, id: shuttleID)
dataSet.append(data)
}
requests.removeObject(request)
completion(dataSet: dataSet, error: nil)
}
} else {
requests.removeObject(request)
completion(dataSet: nil, error: error) }
})
}
requests.append(request)
}
Add this Handy extension on Array to remove item to your code:
// Swift 2 Array Extension
extension Array where Element: Equatable {
mutating func removeObject(object: Element) {
if let index = self.indexOf(object) {
self.removeAtIndex(index)
}
}
mutating func removeObjectsInArray(array: [Element]) {
for object in array {
self.removeObject(object)
}
}
}
On analysis, I found that the memory warning was not due to the Alamofire request. It was due to MKMapView. Loading a MKMapView, zooming in and zooming out consumes more memory. So, in viewWillDisappear I did the fix.
override func viewWillDisappear(animated:Bool){
super.viewWillDisappear(animated)
self.applyMapViewMemoryFix()
}
func applyMapViewMemoryFix(){
switch (self.mapView.mapType) {
case MKMapType.Hybrid:
self.mapView.mapType = MKMapType.Standard
break;
case MKMapType.Standard:
self.mapView.mapType = MKMapType.Hybrid
break;
default:
break;
}
self.mapView.showsUserLocation = false
self.mapView.delegate = nil
self.mapView.removeFromSuperview()
self.mapView = nil
}
Courtesy - Stop iOS 7 MKMapView from leaking memory

Resources