What could possibly cause a memory leak in this code? - swift

I've been trying to find out what causes memory leaks, but in Instruments, I am being told that the memory leak happens at seriesDownloadingQueue.addOperation(downloadOperation) and in the class that calls the method getSeries(), which starts the updating process.
I'm completely new to asynchronous programming and memory leaks. I've read some posts on stack overflow about finding and fixing the leaks. I've also read an article from http://www.raywenderlich.com on how to use Instruments. This app is a database preloader. It downloads and processes information. The .sqlite file will be used in a mobile app.
Below is the code which allegedly causes the memory leak. If you need more information, I'll provide it.
import Foundation
import CoreData
class SerieFetcher: NSObject{
dynamic var didGetSeries: Bool = false
static var doneSeries: Int = 0
func getSeries(){
var seriesDownloadingQueue: NSOperationQueue{
let val = NSOperationQueue()
val.maxConcurrentOperationCount = 32
val.name = "Serie Downloading & Processing Queue"
return val
}
var defaultSessionConfiguration:NSURLSessionConfiguration{
let val = NSURLSessionConfiguration.defaultSessionConfiguration()
val.HTTPMaximumConnectionsPerHost = 20
return val
}
let defaultSession: NSURLSession = NSURLSession(configuration: defaultSessionConfiguration,delegate: nil, delegateQueue: seriesDownloadingQueue)
if let countries = fetchCountries(){
for country in countries{
if let url = NSURL(string:(BASE_URL + "series/"+CAT_STAMPS+"producer/\(country.0)")){
let downloadOperation = downloadSeriesOperation(downloadURL: url, countryObjectID: country.1, countryCount: countries.count, defaultSession: defaultSession , completionHandler: { [weak self](didGetSeries) in
if(didGetSeries == true){
self!.didGetSeries = didGetSeries
print("Finished Downloading Series")
}
})
downloadOperation.completionBlock = nil
seriesDownloadingQueue.addOperation(downloadOperation)
}
}
}
}
func fetchCountries() -> [Int: NSManagedObjectID]?{
let fetchRequest = NSFetchRequest(entityName: "Country")
fetchRequest.resultType = .DictionaryResultType
let objectIDDesc = NSExpressionDescription()
objectIDDesc.name = "objectID"
objectIDDesc.expression = NSExpression.expressionForEvaluatedObject()
objectIDDesc.expressionResultType = .ObjectIDAttributeType
fetchRequest.propertiesToFetch = ["countryID",objectIDDesc]
fetchRequest.returnsDistinctResults = true
do{
let results = try managedContext.executeFetchRequest(fetchRequest) as! [NSDictionary]
var countryIDs: [Int: NSManagedObjectID] = [:]
for result in results{
let countryID: Int = result.valueForKey("countryID") as! Int
let objectID: NSManagedObjectID = result.valueForKey("objectID") as! NSManagedObjectID
countryIDs.updateValue(objectID, forKey: countryID)
}
return countryIDs
}catch let error as NSError{
print(error.localizedDescription)
}
return nil
}
}
class downloadSeriesOperation: NSOperation{
let countryObjectID:NSManagedObjectID
let downloadURL:NSURL
let countryCount:Int
let defaultSession:NSURLSession
let completionHandler: (didGetSeries: Bool) -> Void
init(downloadURL:NSURL, countryObjectID: NSManagedObjectID,countryCount: Int, defaultSession:NSURLSession, completionHandler: (didGetSeries:Bool) -> Void){
self.downloadURL = downloadURL
self.countryObjectID = countryObjectID
self.countryCount = countryCount
self.defaultSession = defaultSession
self.completionHandler = completionHandler
}
override func main() {
let dataTask = defaultSession.dataTaskWithURL(downloadURL, completionHandler: { (data, response, error) in
let privateMOC = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
privateMOC.persistentStoreCoordinator = managedContext.persistentStoreCoordinator
privateMOC.undoManager = nil
var parsedData: NSArray?
do{
parsedData = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions()) as? NSArray
}catch let error as NSError{
print(error.localizedDescription)
}
for val in parsedData!{
if let serieID = Int(val[0] as! NSString as String){
if let serieName = val[1] as? NSString as? String{
if serieID == 0{
continue
}
let serie = NSEntityDescription.insertNewObjectForEntityForName("Serie", inManagedObjectContext: privateMOC) as! Serie
serie.country = privateMOC.objectWithID(self.countryObjectID) as? Country
serie.serieID = serieID
serie.serieName = serieName
}
}
}
privateMOC.performBlock({
do{
try privateMOC.save()
privateMOC.reset()
}catch let error as NSError{
print(error.localizedDescription)
}
})
SerieFetcher.doneSeries += 1
print(self.countryCount,"/",SerieFetcher.doneSeries)
if(SerieFetcher.doneSeries == self.countryCount){
self.completionHandler(didGetSeries: true)
}
})
dataTask.resume() //Start the Download
}
}

Related

How to work with async functions swift? Completion handlers [duplicate]

This question already has answers here:
Run code only after asynchronous function finishes executing
(2 answers)
Closed 5 years ago.
Im trying to wait for the function to process in order to show my image. I have try many things but none of this worked. I know this is an async function and basically i have to wait in order to get the right values but I dont know how to fix this function right here. I hope you can help me out. Thank you!
func createListProductsGood(Finished() -> void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
self.productPhoto.image = UIImage(data: data as Data)
}}
}
}
})
finished()
}
Edited:
This is how my viewDidLoad looks like:
override func viewDidLoad() {
super.viewDidLoad()
setAcceptedOrRejected()
createListProductsGood{_ in
}
}
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active
DispatchQueue.main.async {
self.productPhoto.image = UIImage(data: data as Data)
}
}}
}
}
})
}
This is my second method:
func setAcceptedOrRejected() {
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
if self.ProductId == snapshot.key{
self.texto = prod["NotInterested"] as! String
self.refProducts.child("Products").child(self.ProductId).updateChildValues(["NotInterested": self.texto + ", " + self.userUID])
} })
}
You should change:
func createListProductsGood(Finished() -> void) {
to:
func createListProductsGood(finished: #escaping (_ something: SomeType) -> Void) {
or to be more specific:
func createListProductsGood(finished: #escaping (_ imageData: Data) -> Void) {
then wherever in your function you get the image, you call
finished(imageData)
so you can pass the imageData through a closure to where its needed.
then you call this function like this:
createListProductsGood{ imageData in
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.productPhoto.image = image
}
}
Also:
it's not convention to use Finished(), you should use finished()
using void is wrong. You must use Void or ()
If you're having problems with closures and completionHandlers, I recommend you first try getting your hands dirty with a simple UIAlertController. See here. Try creating an action with a closure, e.g. see here
EDIT :
Thanks to Leo's comments:
func createListProductsGood(finished: #escaping(_ imageData: Data?, MyError?) -> Void) {
let value: Data?
let error = MyError.someError("The error message")
refProducts.child("Products").queryOrderedByKey().observe(.childAdded, with: { snapshot in
let prod = snapshot.value as! NSDictionary
let active = snapshot.key
let rejected = prod["NotInterested"] as! String
let photoURL = prod["photoURL"] as! String
var findit = false
// print(rejected)
if (rejected != self.userUID){
//print(active)
if rejected.contains(","){
var pointsArr = rejected.components(separatedBy: ",")
for x in pointsArr{
if x.trimmingCharacters(in: NSCharacterSet.whitespaces) == self.userUID {
// print("dont show")
findit = true
return
}
}
if (findit == false){
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data, nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}else{
print(active)
if let url = NSURL(string: photoURL) {
if let data = NSData(contentsOf: url as URL) {
self.ProductId = active // REMOVE
self.productPhoto.image = UIImage(data: data as Data) // REMOVE
finished(data,nil) //ADD
}else{
finished(nil,error) //ADD
}
}
}
}
})
}
And then you call it like:
createListProductsGood { imageData, error in guard let value = imageData, error == nil else { // present an alert and pass the error message return }
...
let image = UIImage(data: imageData)
// update UI from main Thread:
DispatchQueue.main.async {
self.ProductId = active
self.productPhoto.image = image } }
Basically this way the createListProductsGood takes in 2 closures, one for if the image is present, another for if an error was returned.

Delay in Updating UILabel from API call

I am updating my UILabels from an API Call. I have an API Client file which pulls the needed information and then passes it over to the DataStore and then parses through the information and then creates movie Objects. The collectionViewController accesses the information without a problem but when it passes the information to the next view controller, a problem occurs. The information does not appear on the view controller and then when I click back in the information will appear. There is some sort of delay in the information from the API Client or something which I can't seem to figure out.
//Second API call to get information for the Second View Controller (Detail View Controller)
class func getDescriptiveMovieResultsFromSearch(movieID: String, completion:(NSDictionary)-> ())
{
var descriptiveDictionary: [String: String] = [:]
let searchURL = "https://www.omdbapi.com/?i=\(movieID)&?plot=short"
let nsurl = NSURL(string: searchURL)
//convert the url into an NSURL
guard let unwrappedNSURL = nsurl else {print("ERROR OCCURRED HERE"); return}
//unwrap the nsurl using guard let
//let session = NSURLSession.sharedSession()
let request = NSMutableURLRequest(URL: unwrappedNSURL)
//creation of the request
request.HTTPMethod = "GET"
//By Default everythiing is a GET request if you are getting information and you don't need it
//request has an HTTPMethod of type "GET" to obtain information
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) { (data, response, error) in
guard let unwrappedData = data else {print("Error occurred here"); return}
if let responseDictionary = try? NSJSONSerialization.JSONObjectWithData(unwrappedData, options: []) as? NSDictionary
{
let castedResponseDictionary = responseDictionary as? [String : String]
guard let unwrappedResponseDictionary = castedResponseDictionary else {print("This did not work!"); return}
descriptiveDictionary = unwrappedResponseDictionary
}
completion(descriptiveDictionary)
}
task.resume()
}
//MovieDataStore function to parse through the information
/Second API Call
func getDescriptiveMovieInformationWith(movie: Movie, Completion: (Bool) -> ())
{
guard let unwrappedimdbID = movie.imdbID else {print("AN ERROR OCCURRED HERE"); return}
OMDBAPIClient.getDescriptiveMovieResultsFromSearch(unwrappedimdbID) { (descriptiveResponseDictionary) in
let desMovieDirector = descriptiveResponseDictionary["Director"] as? String
let desMovieWriters = descriptiveResponseDictionary["Writer"] as? String
let desMovieActors = descriptiveResponseDictionary["Actors"] as? String
let desMovieShortPlot = descriptiveResponseDictionary["Plot"] as? String
let desMovieimbdRating = descriptiveResponseDictionary["imdbRating"] as? String
//unwrapping each of the of the json information
guard let
unwrappedDesMovieDirector = desMovieDirector,
unwrappedDesMovieWriters = desMovieWriters,
unwrappedDesMovieActors = desMovieActors,
unwrappedDesMovieShortPlot = desMovieShortPlot,
unwrappedDesMovieimbdRating = desMovieimbdRating
else {print("AN ERROR OCCURRED HERE!"); return}
movie.director = unwrappedDesMovieDirector
movie.writers = unwrappedDesMovieWriters
movie.actors = unwrappedDesMovieActors
movie.shortPlot = unwrappedDesMovieShortPlot
movie.imdbRating = unwrappedDesMovieimbdRating
print("******************************************")
print("Movie Director: \(movie.director)")
print("Movie writers: \(movie.writers)")
print("Movie actors: \(movie.actors)")
print("Movie shortPlot: \(movie.shortPlot)")
print("Movie imdbRating: \(movie.imdbRating)")
print("******************************************")
//Completion(true)
Completion(true)
}
}
//Detail View Controller
/Second API Call
func getDescriptiveMovieInformationWith(movie: Movie, Completion: (Bool) -> ())
{
guard let unwrappedimdbID = movie.imdbID else {print("AN ERROR OCCURRED HERE"); return}
OMDBAPIClient.getDescriptiveMovieResultsFromSearch(unwrappedimdbID) { (descriptiveResponseDictionary) in
let desMovieDirector = descriptiveResponseDictionary["Director"] as? String
let desMovieWriters = descriptiveResponseDictionary["Writer"] as? String
let desMovieActors = descriptiveResponseDictionary["Actors"] as? String
let desMovieShortPlot = descriptiveResponseDictionary["Plot"] as? String
let desMovieimbdRating = descriptiveResponseDictionary["imdbRating"] as? String
//unwrapping each of the of the json information
guard let
unwrappedDesMovieDirector = desMovieDirector,
unwrappedDesMovieWriters = desMovieWriters,
unwrappedDesMovieActors = desMovieActors,
unwrappedDesMovieShortPlot = desMovieShortPlot,
unwrappedDesMovieimbdRating = desMovieimbdRating
else {print("AN ERROR OCCURRED HERE!"); return}
movie.director = unwrappedDesMovieDirector
movie.writers = unwrappedDesMovieWriters
movie.actors = unwrappedDesMovieActors
movie.shortPlot = unwrappedDesMovieShortPlot
movie.imdbRating = unwrappedDesMovieimbdRating
print("******************************************")
print("Movie Director: \(movie.director)")
print("Movie writers: \(movie.writers)")
print("Movie actors: \(movie.actors)")
print("Movie shortPlot: \(movie.shortPlot)")
print("Movie imdbRating: \(movie.imdbRating)")
print("******************************************")
//Completion(true)
Completion(true)
}
}
//detail view Controller
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.blackColor()
self.titleLabel.textColor = UIColor.yellowColor()
self.yearLabel.textColor = UIColor.yellowColor()
self.directorLabel.textColor = UIColor.yellowColor()
self.writersLabel.textColor = UIColor.yellowColor()
self.actorsLabel.textColor = UIColor.yellowColor()
self.shortPlotLabel.textColor = UIColor.yellowColor()
self.imdbIDLabel.textColor = UIColor.yellowColor()
self.typeLabel.textColor = UIColor.yellowColor()
self.imdbRating.textColor = UIColor.yellowColor()
stackViewLabel.translatesAutoresizingMaskIntoConstraints = false
stackViewLabel.topAnchor.constraintEqualToAnchor(self.topImage.bottomAnchor, constant: 5).active = true
stackViewLabel.widthAnchor.constraintEqualToAnchor(self.view.widthAnchor, multiplier: 1.00).active = true
stackViewLabel.heightAnchor.constraintEqualToAnchor(self.view.heightAnchor, multiplier: 0.50).active = true
stackViewLabel.leftAnchor.constraintEqualToAnchor(self.view.leftAnchor).active = true
//unwrapped Movie Object
guard let unwrappedMovieObject = movieObject else {print("AN ERROR OCCURRED HERE!"); return}
self.store.getDescriptiveMovieInformationWith(unwrappedMovieObject) { (isWorking) in
if isWorking {
dispatch_async(dispatch_get_main_queue()){
guard let unwrappedPosterURL = unwrappedMovieObject.posterURL else {print("AN ERROR OCCURRED HERE"); return}
if unwrappedPosterURL == "N/A"{
self.topImage.image = UIImage.init(named: "star_PNG1592")
}
else {
if let url = NSURL(string: unwrappedPosterURL){
if let data = NSData(contentsOfURL: url){
//print("I have an image to display")
self.topImage.image = UIImage.init(data: data)
}
}
}
self.titleLabel.text = unwrappedMovieObject.title
self.yearLabel.text = unwrappedMovieObject.year
self.imdbIDLabel.text = unwrappedMovieObject.imdbID
self.typeLabel.text = unwrappedMovieObject.type
guard let
unwrappedDirector = unwrappedMovieObject.director,
unwrappedWriters = unwrappedMovieObject.writers,
unwrappedActors = unwrappedMovieObject.actors,
unwrappedShortPlot = unwrappedMovieObject.shortPlot,
unwrappedRating = unwrappedMovieObject.imdbRating
else {print("PROPERTIES WERE UNWRAPPED"); return}
self.directorLabel.text = unwrappedDirector
self.writersLabel.text = unwrappedWriters
self.actorsLabel.text = unwrappedActors
self.shortPlotLabel.text = unwrappedShortPlot
self.imdbRating.text = unwrappedRating
}
}
else{
print("AN ERROR OCCURRED HERE")
}
}
}
NSURLSession.sharedSession().dataTaskWithRequest(request)
I'm not sure what callback queue the sharedSession uses, If its not the main thread you will need to
DispatchQueue.main.async {
completion(descriptiveDictionary)
}

Need to adjust NSJSONSerialization to iOS10

After upgrading to iOS10 users started complaining about crashes of my app.
I am testing it with iOS10 on the simulator and indeed the app crashes with a message saying "Could not cast value of type '__NSArrayI' to 'NSMutableArray'". Here's my code, please help:
import Foundation
protocol getAllListsModel: class {
func listsDownloadingComplete(downloadedLists: [ContactsList])
}
class ListsDownloader: NSObject, NSURLSessionDataDelegate{
//properties
weak var delegate: getAllListsModel!
var data : NSMutableData = NSMutableData()
func downloadLists() {
let urlPath: String = "http://..."
let url: NSURL = NSURL(string: urlPath)!
var session: NSURLSession!
let configuration = NSURLSessionConfiguration.ephemeralSessionConfiguration() //defaultSessionConfiguration()
session = NSURLSession(configuration: configuration, delegate: self, delegateQueue: nil)
let task = session.dataTaskWithURL(url)
task.resume()
}
func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
self.data.appendData(data);
}
func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
if error != nil {
print("Failed to download data")
}else {
self.parseJSON()
print("Lists downloaded")
}
}
func parseJSON() {
var jsonResult: NSMutableArray = NSMutableArray()
do{
try jsonResult = NSJSONSerialization.JSONObjectWithData(self.data, options:NSJSONReadingOptions.AllowFragments) as! NSMutableArray
} catch let error as NSError {
print(error)
}
var jsonElement: NSDictionary = NSDictionary()
var downloadedLists: [ContactsList] = []
for i in 0...jsonResult.count-1 {
jsonElement = jsonResult[i] as! NSDictionary
let tempContactsList = ContactsList()
//the following insures none of the JsonElement values are nil through optional binding
let id = jsonElement["id"] as? String
let name = jsonElement["name"] as? String
let pin = jsonElement["pin"] as? String
let lastUpdated = jsonElement["created"] as? String
let listAdminDeviceID = jsonElement["admin"] as? String
tempContactsList.id = id
tempContactsList.name = name
tempContactsList.pin = pin
tempContactsList.lastUpdated = lastUpdated
tempContactsList.listAdmin = listAdminDeviceID
downloadedLists.append(tempContactsList)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.listsDownloadingComplete(downloadedLists)
})
}
}
Even in iOS 9, there was no guarantee NSJSONSerialization.JSONObjectWithData(_:options:) would return mutable object or not. You should have specified NSJSONReadingOptions.MutableContainers.
And in your code, you are not modifying jsonResult, which means you have no need to declare it as NSMutableArray. Just replace NSMutableArray to NSArray, and then you have no need to specify NSJSONReadingOptions.MutableContainers.
But as vadian is suggesting, you better use Swift types rather than NSArray or NSDictionary. This code should work both in iOS 9 and 10.
func parseJSON() {
var jsonResult: [[String: AnyObject]] = [] //<- use Swift type
do{
try jsonResult = NSJSONSerialization.JSONObjectWithData(self.data, options: []) as! [[String: AnyObject]] //<- convert to Swift type, no need to specify options
} catch let error as NSError {
print(error)
}
var downloadedLists: [ContactsList] = []
for jsonElement in jsonResult { //<- your for-in usage can be simplified
let tempContactsList = ContactsList()
//the following insures none of the JsonElement values are nil through optional binding
let id = jsonElement["id"] as? String
let name = jsonElement["name"] as? String
let pin = jsonElement["pin"] as? String
let lastUpdated = jsonElement["created"] as? String
let listAdminDeviceID = jsonElement["admin"] as? String
tempContactsList.id = id
tempContactsList.name = name
tempContactsList.pin = pin
tempContactsList.lastUpdated = lastUpdated
tempContactsList.listAdmin = listAdminDeviceID
downloadedLists.append(tempContactsList)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate.listsDownloadingComplete(downloadedLists)
})
}
Try this and check it on iOS 10 devices.
(The as! conversion would cause some weird crashes when your server is malfunctioning, but that would be another issue, so I keep it there.)

Extra argument 'error' in call in swift

I am new to swift so please treat me as beginner.
I am following tutorial, this is pretty old tutorial and it has used GoogleMap framework whereas I am doing it with pod. In func geocodeAddress in MapTasks.swift file I am getting error called
Extra argument 'error' in call
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let request = NSMutableURLRequest(URL: geocodingResultsData)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(let data, let response, let error) in
if let _ = response as? NSHTTPURLResponse {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if error != nil {
print("error=\(error!)")
return
}
if let parseJSON = json {
}
} catch {
print(error)
}
}
}
task.resume()
else {
// Get the response status.
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
}
})
}
else {
completionHandler(status: "No valid address.", success: false)
}
}
So far I know is I am getting this error because of the diffrent version of swift. Tutorial I am following is written in old version of swift and I am doing it in new
In Swift 2.0, you cannot add 'error' argument in NSJSONSerialization method, you need to use try-catch statement as follows:
func geocodeAddress(address: String!, withCompletionHandler completionHandler: ((status: String, success: Bool) -> Void)) {
if let lookupAddress = address {
var geocodeURLString = baseURLGeocode + "address=" + lookupAddress
geocodeURLString = geocodeURLString.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding)!
let geocodeURL = NSURL(string: geocodeURLString)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let geocodingResultsData = NSData(contentsOfURL: geocodeURL!)
let request = NSMutableURLRequest(URL: geocodeURL!)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
(let data, let response, let error) in
if let _ = response as? NSHTTPURLResponse {
do {
let dictionary = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if error != nil {
print("error=\(error!)")
return
}
if let parseJSON = dictionary {
let status = dictionary["status"] as! String
if status == "OK" {
let allResults = dictionary["results"] as! Array<Dictionary<NSObject, AnyObject>>
self.lookupAddressResults = allResults[0]
// Keep the most important values.
self.fetchedFormattedAddress = self.lookupAddressResults["formatted_address"] as! String
let geometry = self.lookupAddressResults["geometry"] as! Dictionary<NSObject, AnyObject>
self.fetchedAddressLongitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lng"] as! NSNumber).doubleValue
self.fetchedAddressLatitude = ((geometry["location"] as! Dictionary<NSObject, AnyObject>)["lat"] as! NSNumber).doubleValue
completionHandler(status: status, success: true)
}
else {
completionHandler(status: status, success: false)
}
}
} catch {
print(error)
}
}
}
task.resume()
})
}
}

Creating and assigning a new NSManagedObject to a new NSManagedObject *sometimes* fails

I am creating a new NSManagedObject called "translation". Within the translation I need to create two additional NSManagedObjects called "phrase". Sometimes one of the phrase assignments will throw an error, but when I inspect the values they all look like they were created just fine. What gives???
Creating A Translation Object:
func getOrCreateTranslation(package: Package?, data: NSDictionary) -> Translation {
let translationId = data["id"] as! NSNumber
if let translation = self.getTranslation(translationId) {
return translation
} else {
let context = LocalDataStorage().context
let translation = NSEntityDescription.insertNewObjectForEntityForName("Translation", inManagedObjectContext: context) as! Translation
translation.id = translationId
let fromPhrase = data["from_phrase"]! as! NSDictionary
let toPhrase = data["to_phrase"]! as! NSDictionary
let pm = PhraseManager()
//*******
// *SOMETIMES* ONE OF THESE LINES FAIL WITH BAD_EXC_ACCESS code=1
translation.fromPhrase = pm.getOrCreatePhrase(fromPhrase)
translation.toPhrase = pm.getOrCreatePhrase(toPhrase)
//******
if package != nil {
package!.addTranslationObject(translation)
}
return translation
}
}
Creating A Phrase Object:
func getOrCreatePhrase(data: NSDictionary) -> Phrase {
// check if phrase exists
let phraseId = data["id"] as! NSNumber
if let phrase = self.getPhrase(phraseId) {
return phrase
} else {
let context = localDataStorage.context
let lm = LanguageManager()
let phrase = NSEntityDescription.insertNewObjectForEntityForName("Phrase", inManagedObjectContext: context) as! Phrase
phrase.id = phraseId
phrase.text = data["text"] as! String
phrase.audioUrl = data["audio_url"] as? String
let code = data["language"]!["language_code"] as! String
phrase.language = lm.getLanguageFromCode(code)
return phrase
}
}
Call Made to API:
func getPackageTranslations(package: Package, completion: ([Translation])-> Void) {
let currentLanguage: Language = LanguageManager().getCurrentLanguage()!
let urlString = baseAPIString + "/groups/\(package.id!)/translations/?language_code=\(currentLanguage.code)"
let session = NSURLSession.sharedSession()
let serachUrl = NSURL(string: urlString)
let task = session.dataTaskWithURL(serachUrl!) {
(data, response, error) -> Void in
if error != nil {
print(error?.localizedDescription)
} else {
let jsonData: NSDictionary!
do {
jsonData = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as! NSDictionary
} catch _ {
jsonData = NSDictionary()
}
let groupTranslationsData = jsonData["group_translations"] as! [NSDictionary]
var translations = [Translation]()
let context = LocalDataStorage().context
for groupTranslation in groupTranslationsData {
let translationData = groupTranslation["translation"] as! NSDictionary
let translation = TranslationManager().getOrCreateTranslation(package, data: translationData)
if translation.packages?.containsObject(package) == false {
//package.addTranslationObject(translation!)
//translation!.addPackageObject(package)
}
translations.append(translation)
}
do {
try context.save()
} catch {
print("There was a problem saving translation ")
}
dispatch_async(dispatch_get_main_queue(), {
completion(translations)
})
}
}
task.resume()
}
CoreData Context Class:
class LocalDataStorage {
let appDelegate: AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let context: NSManagedObjectContext!
init() {
context = appDelegate.managedObjectContext
}
}
This issue occurs when you create a NSManagedObjectContext with a concurrency pattern that it should be interacted on and you perform actions on the it on a thread different from the concurrency pattern specified during its initialization.
The completion block of NSURLSession.dataTaskWithURL is run on another thread, so you must dispatch to the type of thread specified in the context creation to perform any operation successfully on it.
If the concurrency type of your context is MainQueueConcurrencyType which is used in most cases, you must perform the context save method on the main queue.
dispatch_async(dispatch_get_main_queue()) {
do {
try context.save()
} catch {
print("There was a problem saving translation ")
}
completion(translations)
}