Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread in swift - swift

I have given DispatchQueue.main.async {} where it necessary but when i give break point from dataTask here it says
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Modifications to the layout engine must not be performed from a background thread after it has been accessed from the main thread.'
Cannot be called with asCopy = NO on non-main thread.
class EventsViewController: UIViewController {
#IBOutlet weak var backBtn: UIButton!
var eventsListArray = [AnyObject]()
var eventType: String?
var eventList : EventsModel? = nil
#IBOutlet weak var eventsTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
getAllEventsList()
}
func getAllEventsList() {
DispatchQueue.main.async {
let deviceId: String = (UIDevice.current.identifierForVendor?.uuidString)!
let personalId: String = UserDefaults.standard.string(forKey: "regUserID") ?? ""//KeychainWrapper.standard.string(forKey: "USERID") ?? ""
let headers = ["deviceid": deviceId,"userType": "personal","key": personalId]
DispatchQueue.main.async {
let string = Constants.GLOBAL_URL + "/get/allevents"
var urlComponents = URLComponents(string: string)
let eventStatus = self.eventType
let requestEventType = URLQueryItem(name: "eventstatus", value: eventStatus)
urlComponents?.queryItems = [requestEventType]
let urlStr = urlComponents?.url
let request = NSMutableURLRequest(url: urlStr!, cachePolicy: .useProtocolCachePolicy,timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers as! [String : String]
let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
if error == nil {
let httpResponse = response as? HTTPURLResponse
if httpResponse!.statusCode == 200 {
do {
let jsonObject = try JSONSerialization.jsonObject(with: data!, options: .mutableLeaves) as! [String :AnyObject]
print("publish event \(jsonObject)")
self.eventList = EventsModel.init(fromDictionary: jsonObject)
DispatchQueue.main.async {
if self.eventList?.events.count != 0 {
DispatchQueue.main.async {
self.eventsTableView.reloadData()
}
}
else {
DispatchQueue.main.async {
Constants.showAlertView(alertViewTitle: "", Message: "No Events \(self.eventType)", on: self)
self.eventList?.events.removeAll()
self.eventsTableView.reloadData()
}
}
}
} catch { print(error.localizedDescription) }
} else {
Constants.showAlertView(alertViewTitle: "", Message: "Something went wrong, Please try again", on: self)
}
}
})
dataTask.resume()
}
}
}
}

You've probably missed a few spots where you're trying to present an alert when errors are thrown. Why don't you just enter the main queue right after the data request is complete.
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
DispatchQueue.main.async {
if error == nil {
//...
}
}
})

Related

Why does webSocketTask.receive never complete and how do I force completion in my Swift app?

I have a Swift app that uses a web socket to download stock price information from a public API. I send a message through the socket to subscribe to various stock price changes then wait for a continuous stream of messages to be received but when I turn off wifi the call to the message receive function, webSocketTask.receive, never returns. How can I force abort of the message receive function so that I alert the user that the network connection has been lost.
Here is my NetworkServices class;
import UIKit
import Network
protocol NetworkServicesDelegate: AnyObject {
func sendStockInfo(stocksInfo: [String: StockInfo])
}
final class NetworkServices: NSObject {
static let sharedInstance = NetworkServices()
var urlSession: URLSession?
var webSocketTask: URLSessionWebSocketTask?
var stocksInfo: [String: StockInfo] = [:]
var socketResults: [String: [StockInfo]] = [:]
weak var delegate: NetworkServicesDelegate?
var timer: Timer?
var stockSymbols: [String] = []
private let queue = DispatchQueue.global(qos: .background)
private let monitor = NWPathMonitor()
public private(set) var isConnected: Bool = false
public private(set) var connectionType: ConnectionType = .unknown
enum ConnectionType {
case wifi
case cellular
case wiredEthernet
case unknown
}
public func startMonitoring() {
monitor.start(queue: queue)
monitor.pathUpdateHandler = { path in
self.isConnected = path.status == .satisfied
self.getConnectionType(path)
print("DEBUG: isConnected = \(self.isConnected); connectionType = \(self.connectionType)")
if self.isConnected == false {
}
}
}
public func stopMonitoring() {
monitor.cancel()
}
private func getConnectionType(_ path: NWPath) {
if path.usesInterfaceType(.wifi) {
connectionType = .wifi
} else if path.usesInterfaceType(.cellular) {
connectionType = .cellular
} else if path.usesInterfaceType(.wiredEthernet) {
connectionType = .wiredEthernet
} else {
connectionType = .unknown
}
}
func fetchStockInfo(symbols: [String], delegate: CompanyPriceListVC) {
stockSymbols = symbols
self.delegate = delegate
let configuration = URLSessionConfiguration.default
configuration.waitsForConnectivity = false
urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: OperationQueue.main)
webSocketTask = urlSession?.webSocketTask(with: FINNHUB_SOCKET_STOCK_INFO_URL!)
webSocketTask?.delegate = self
webSocketTask!.resume()
for symbol in symbols {
let string = FINNHUB_SOCKET_MESSAGE_STRING + symbol + "\"}"
let message = URLSessionWebSocketTask.Message.string(string)
if isConnected {
webSocketTask!.send(message) { error in
if let error = error {
print("DEBUG: Error sending message: \(error)")
}
self.receiveMessage()
}
} else {
// Post notification to view controllers that connection has been lost
let name = Notification.Name(rawValue: isNotConnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
// try to re-connect
// successful re-connect?
// No, keep trying. Yes, call send message method again
}
}
}
private func receiveMessage() {
if isConnected {
self.webSocketTask?.receive { result in
print("DEBUG: Inside closure.")
switch result {
case .failure(let error):
print("DEBUG: Error receiving message: \(error.localizedDescription)")
case .success(.string(let jsonData)):
guard let stockData = jsonData.data(using: .utf8) else { return }
self.socketResults = [:]
self.stocksInfo = [:]
let decoder = JSONDecoder()
do {
let socketData = try decoder.decode(SocketData.self, from: stockData)
guard let stockInfoData = socketData.data else { return }
for stockInfo in stockInfoData {
let symbol = stockInfo.symbol
if self.socketResults[symbol] == nil {
self.socketResults[symbol] = [StockInfo]()
}
self.socketResults[symbol]?.append(stockInfo)
}
for (symbol, stocks) in self.socketResults {
for item in stocks {
if self.stocksInfo[symbol] == nil {
self.stocksInfo[symbol] = item
} else if item.timestamp > self.stocksInfo[symbol]!.timestamp {
self.stocksInfo[symbol] = item
}
}
}
self.delegate?.sendStockInfo(stocksInfo: self.stocksInfo)
self.receiveMessage()
} catch {
print("DEBUG: Error converting JSON: \(error)")
}
default:
print("DEBUG: default")
}
}
print("DEBUG: Got here 1")
} else {
print("DEBUG: Got here 2")
// Post notification to view controllers that connection has been lost
let name = Notification.Name(rawValue: isNotConnectedNotificationKey)
NotificationCenter.default.post(name: name, object: nil)
// try to re-connect.
// successful reconnect?
// No, keep trying. Yes, call receive message method again.
}
}
func closeWebSocketConnection() {
webSocketTask?.cancel(with: .goingAway, reason: nil)
}
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print("DEBUG: didCompleteWithError called: error = \(error)")
}
}
func fetchCompanyInfo(symbol: String, completion: #escaping (CompanyInfo?, UIImage?)->()) {
let urlString = FINNHUB_HTTP_COMPANY_INFO_URL_STRING + symbol + "&token=" + FINNHUB_API_TOKEN
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
print("Error fetching company info: \(error)")
}
guard let data = data else { return }
let decoder = JSONDecoder()
do {
let companyInfo = try decoder.decode(CompanyInfo.self, from: data)
guard let logoURL = URL(string: companyInfo.logo) else { return }
let task = URLSession.shared.dataTask(with: logoURL) { data, response, error in
if let error = error {
print("Error fetching logo image: \(error)")
}
guard let data = data else { return }
guard let logoImage = UIImage(data: data) else { return }
completion(companyInfo, logoImage)
}
task.resume()
} catch {
print("Error decoding JSON: \(error)")
completion(nil, nil)
}
}
task.resume()
}
}
extension NetworkServices: URLSessionTaskDelegate, URLSessionWebSocketDelegate, URLSessionDelegate {
func urlSession(_ session: URLSession, taskIsWaitingForConnectivity task: URLSessionTask) {
print("DEBUG: inside taskIsWaitingForConnectivity")
}
func urlSession(_ session: URLSession, didBecomeInvalidWithError error: Error?) {
print("DEBUG: didBecomeInvalidWithError: error = \(String(describing: error?.localizedDescription))")
}
func urlSession(_ session: URLSession, webSocketTask: URLSessionWebSocketTask, didCloseWith closeCode: URLSessionWebSocketTask.CloseCode, reason: Data?) {
let reasonString: String
if let reason = reason, let string = String(data: reason, encoding: .utf8) {
reasonString = string
} else {
reasonString = ""
}
print("DEBUG: didCloseWith called: close code is \(closeCode), reason is \(String(describing: reasonString))")
}
func urlSessionDidFinishEvents(forBackgroundURLSession session: URLSession) {
print("DEBUG: urlSessionDidFinishEvents called")
}
}
I have tried placing code in the pathUpdateHandler closure to cancel the task on the task object and finish current tasks and cancel on the URLSession object but neither works.
I needed to post a notification when path.status == .satisfied. The notification goes to the custom UIViewController class which then calls the FetchStockInfo(symbols:delegate) method.

AlertController view is not in the window hierarchy

i am a beginner in swift, i was trying to use an UIAlertController but i have got an error and the UiAlertController do not show up and i can see an error in the console that says
2021-06-20 18:16:28.494162+0200 Bicycall[50670:543648] [Presentation] Attempt to present <UIAlertController: 0x7fbaca034e00> on <Bicycall.login: 0x7fbabb01d800> (from <Bicycall.login: 0x7fbabb01d800>) whose view is not in the window hierarchy.
2021-06-20 18:16:28.494339+0200 Bicycall[50670:543648] [Presentation] Attempt to present <UIAlertController: 0x7fbac9862800> on <Bicycall.login: 0x7fbabb01d800> (from <Bicycall.login: 0x7fbabb01d800>) whose view is not in the window hierarchy.
Here what i have tried:
import UIKit
import CoreData
import AVKit
import AVFoundation
import TKSubmitTransition
class login: UIViewController {
#IBOutlet weak var passwordView: UIView!
#IBOutlet weak var emailView: UIView!
var id: Int?
var name: String?
var lastname: String?
var email:String?
var password:String?
var phone:String?
var u = ConnectedUser()
var BR = BaseUrl.baseUrl
var player: AVPlayer?
let videoURL: NSURL = Bundle.main.url(forResource: "Bikers", withExtension: "mp4")! as NSURL
func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
return topMostViewController
}
func presentTestAlert(_ title: String, _ message: String){
let alert = UIAlertController(title: title , message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), style: .default, handler: { _ in
NSLog("The \"OK\" alert occured.")
}))
DispatchQueue.main.async {
self.getTopMostViewController()?.present(alert, animated: true, completion: nil)
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.DeleteAllData()
player = AVPlayer(url: videoURL as URL)
player?.actionAtItemEnd = .none
player?.isMuted = true
let playerLayer = AVPlayerLayer(player: player)
playerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
playerLayer.zPosition = -1
playerLayer.frame = view.frame
view.layer.addSublayer(playerLayer)
player?.play()
NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: .main) { [weak self] _ in
self?.player?.seek(to: CMTime.zero)
self?.player?.play()
// Do any additional setup after loading the view.
}
emailView.layer.cornerRadius = 30.0
emailView.layer.borderWidth = 0.5
emailView.layer.borderColor = UIColor.white.cgColor
passwordView.layer.cornerRadius = 30.0
passwordView.layer.borderWidth = 0.5
passwordView.layer.borderColor = UIColor.white.cgColor
}
//widgets
#IBOutlet weak var txtEmail: UITextField!
#IBOutlet weak var txtPassword: UITextField!
//Actions
#IBAction func btnLogin(_ sender: Any){
//get
/*
guard let url = URL(string: "http://localhost:3000/bikes") else {
return
}
let session = URLSession.shared
session.dataTask(with: url) { ( data , response ,error) in
if let response = response {
print(response)
}
if let data = data {
print(data)
do
{
let json = try JSONSerialization.jsonObject(with: data, options: [])
print(json)
}catch{
print(error)
}
}
}.resume()
*/
//post
guard let url = URL(string: BR+"/login") else {
return
}
let bodyparameters = ["email": txtEmail.text, "password": txtPassword.text]
if (txtEmail.text!.isEmpty || txtPassword.text!.isEmpty ){
self.presentTestAlert("Wrong credentials","Email and Password must not be empty")
}else{
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
guard let httpBody = try? JSONSerialization.data(withJSONObject: bodyparameters, options: []) else{
return
}
request.httpBody = httpBody
let session = URLSession.shared
session.dataTask(with: request) { (data,response,error) in
let status = (response as! HTTPURLResponse).statusCode
//print(response)
print(status)
if let response = response {
let status = (response as! HTTPURLResponse).statusCode
//print(response)
print(status)
}
if((status) == 200){
self.presentTestAlert("Connection Success", " internet connection")
print(" Connection Successssssssssssssss")
if let data = data {
do {
//let json = try JSONSerialization.jsonObject(with: data, options: [])
// print(json);
print(data)
let user = try JSONDecoder().decode(User.self, from: data)
DispatchQueue.main.async {
self.id = user.user_id
self.name = user.name
self.lastname = user.lastname
self.email = user.email
self.password = user.password
self.phone = user.phone
print(self.id!)
print(self.email!)
if(user.user_id != 0){
self.saveUser()
self.DisplayConnectedUser()
self.performSegue(withIdentifier: "HomeSegue", sender: "nil")
}else{
self.presentTestAlert("Login Failed","Wrong credentials")
}
}
}catch{
print(error)
}
}
}else {
self.presentTestAlert("No Connection", "No internet connection")
print(" Connection Nooooooooooooooooooooooooo")
}
}.resume()
}
}
func DeleteAllData(){
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let managedContext = appDelegate.persistentContainer.viewContext
let DelAllReqVar = NSBatchDeleteRequest(fetchRequest: NSFetchRequest<NSFetchRequestResult>(entityName: "Users"))
do {
try managedContext.execute(DelAllReqVar)
}
catch {
print(error)
}
}
func saveUser() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//represente l'ORM
let persistentContainer = appDelegate.persistentContainer
let managedContext = persistentContainer.viewContext
let entityDescription = NSEntityDescription.entity(forEntityName: "Users" , in: managedContext)
let object = NSManagedObject(entity: entityDescription! , insertInto: managedContext )
object.setValue(id! , forKey: "user_id" )
object.setValue(email! , forKey: "email" )
object.setValue(password! , forKey: "password" )
object.setValue(name! , forKey: "name" )
object.setValue(lastname! , forKey: "lastname" )
object.setValue(phone! , forKey: "phone" )
do {
try managedContext.save()
print("INSERT SUCCESSFULLY")
print(id!)
}
catch {
print("INSERT ERROR")
}
}
#IBAction func btnSignup(_ sender: Any) {
performSegue(withIdentifier: "signupSegue", sender: "nil")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
}
func DisplayConnectedUser() {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
//represente l'ORM
let persistentContainer = appDelegate.persistentContainer
let managedContext = persistentContainer.viewContext //retourne NSManagedObject toujours
//la requete retourne un NSManagedObject
let request = NSFetchRequest<NSManagedObject>(entityName : "Users")
//execution de la requete
do {
let result = try managedContext.fetch(request)
for item in result {
print(item.value(forKey: "user_id") as! Int )
print(item.value(forKey: "email") as! String)
self.u.user_id = (item.value(forKey: "user_id") as! Int)
self.u.email = (item.value(forKey: "email") as! String)
self.u.password = (item.value(forKey: "password") as! String)
self.u.name = (item.value(forKey: "name") as! String)
self.u.lastname = (item.value(forKey: "lastname") as! String)
self.u.phone = (item.value(forKey: "phone") as! String)
print(self.u.user_id!)
print(self.u.email!)
print(self.u.password!)
print(self.u.name!)
print(self.u.lastname!)
print(self.u.phone!)
}
}
catch {
print("NO DATA FOUND , Error")
}
}
}
What is annoying me is that none of my alerts worked! i read some other answer in stackoverflow but the solutions did not work for me
What i am trying is to make my alerts working good
Any help will be so appreciated
It seems you want to present the alert over the video player (playerLayer) but you are calling your alert over the layer behind the playerLayer, it will give you the error that you're getting.
With reference to Swift 4 Attempt to present ViewController whose view is not in the window hierarchy
Add this function in your code
func getTopMostViewController() -> UIViewController? {
var topMostViewController = UIApplication.shared.keyWindow?.rootViewController
while let presentedViewController = topMostViewController?.presentedViewController {
topMostViewController = presentedViewController
}
return topMostViewController
}
And replace (Line 33)
self.present(alert, animated: true, completion: nil)
with
DispatchQueue.main.async {
getTopMostViewController()?.present(alert, animated: true, completion: nil)
}

terminating with uncaught exception of type NSException, when I run my code for my camera

When I try to run my camera I get an error message that says following
terminating with uncaught exception of type NSException and "Multiple audio/video AVCaptureInputs are not currently supported"
I have been watching for solutions on stackOverflow but haven't been succesful.
Tried to redirect my outlet my I can't understand where the problem occurs. Therefor I tried to put some breakpoint to find it but didn't manage
let captureSession = AVCaptureSession()
var previewLayer:CALayer!
var captureDevice:AVCaptureDevice!
var takePhoto = false
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
prepareCamera()
}
func prepareCamera() {
captureSession.sessionPreset = AVCaptureSession.Preset.photo
let availableDevices = AVCaptureDevice.DiscoverySession(deviceTypes: [.builtInWideAngleCamera], mediaType: AVMediaType.video, position: .back).devices
captureDevice = availableDevices.first
beginSession()
}
func beginSession () {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: captureDevice)
captureSession.addInput(captureDeviceInput)
}catch {
print(error.localizedDescription)
}
let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.previewLayer = previewLayer
self.view.layer.addSublayer(self.previewLayer)
self.previewLayer.frame = self.view.layer.frame
captureSession.startRunning()
let dataOutput = AVCaptureVideoDataOutput()
dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString):NSNumber(value:kCVPixelFormatType_32BGRA)] as [String : Any]
dataOutput.alwaysDiscardsLateVideoFrames = true
if captureSession.canAddOutput(dataOutput) {
captureSession.addOutput(dataOutput)
}
captureSession.commitConfiguration()
let queue = DispatchQueue(label: "com.brianadvent.captureQueue")
dataOutput.setSampleBufferDelegate(self, queue: queue)
}
#IBAction func takePhoto(_ sender: Any) {
takePhoto = true
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
if takePhoto {
takePhoto = false
if let image = self.getImageFromSampleBuffer(buffer: sampleBuffer) {
let photoVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "PhotoVC") as! Viewcontroller2
photoVC.takenPhoto = image
DispatchQueue.main.async {
self.present(photoVC, animated: true, completion: {
self.stopCaptureSession()
})
}
}
}
}
func getImageFromSampleBuffer (buffer:CMSampleBuffer) -> UIImage? {
if let pixelBuffer = CMSampleBufferGetImageBuffer(buffer) {
let ciImage = CIImage(cvPixelBuffer: pixelBuffer)
let context = CIContext()
let imageRect = CGRect(x: 0, y: 0, width: CVPixelBufferGetWidth(pixelBuffer), height: CVPixelBufferGetHeight(pixelBuffer))
if let image = context.createCGImage(ciImage, from: imageRect) {
return UIImage(cgImage: image, scale: UIScreen.main.scale, orientation: .right)
}
}
return nil
}
func stopCaptureSession () {
self.captureSession.stopRunning()
if let inputs = captureSession.inputs as? [AVCaptureDeviceInput] {
for input in inputs {
self.captureSession.removeInput(input)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
struct Constants {
static let apiKey = "AIzaSyDtaJ5eU24rbnHsG9pb1STOizDJvqcaj5E"
static let bundleId = "com.felibundle"
static let searchEngineId = "016628067786358079133:2gm9usqzouc"
}
#IBAction func pish(_ sender: Any) {
googleSearch(term: "George Bush") { results in
print(results)}
}
func googleSearch(term: String, callback:#escaping ([(title: String, url: String)]?) -> Void) {
let urlString = String(format: "https://cse.google.com/cse?cx=016628067786358079133:2gm9usqzouc", term, Constants.searchEngineId, Constants.apiKey)
let encodedUrl = urlString.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)
guard let url = URL(string: encodedUrl ?? urlString) else {
print("invalid url \(urlString)")
return
}
let request = NSMutableURLRequest(url: url, cachePolicy: .useProtocolCachePolicy, timeoutInterval: 10)
request.httpMethod = "GET"
request.setValue(Constants.bundleId, forHTTPHeaderField: "X-Ios-Bundle-Identifier")
let session = URLSession.shared
let datatask = session.dataTask(with: request as URLRequest) { (data, response, error) in
guard
error == nil,
let data = data,
let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String : Any]
else {
callback(nil)
return
}
guard let items = json?["items"] as? [[String : Any]], items.count > 0 else {
print("no results")
return
}
callback(items.map { ($0["title"] as! String, $0["formattedUrl"] as! String) })
}
datatask.resume()
}
}
Your code runs fine at my end.
However, this kind of error arrives when we try to add multiple input device to the same session. Make sure you are not adding AVCaptureInputs object elsewhere in your project

Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10136bb50) - swift

I got this error when I want to send value with GET method:
Thread 1: EXC_BREAKPOINT (code=1, subcode=0x10136bb50)
To get values:
var flname = self.txt_field_flname.text
var job_title = self.txt_field_job_title.text
var mobile = self.txt_field_mobile.text
var des = self.txt_field_des.text
var lat = self.lat
var lon = self.lon
self.sendNewJob(fname: flname!, title: job_title!, mobile: mobile!, des: des!, lat: String(lat), lon: String(lon) )
func sendNewJob(fname:String,title:String,mobile:String,des:String,
lat:String,lon:String)
{
print("fname \(fname) title \(title) mobile \(mobile) des \(des) lat \(lat) lon \(lon)") //output is well
RestApiManager.sharedInstance.sendNewJob(fname: fname,title: title,mobile:mobile,
des:des,lat:lat,lon:lon) { (json: JSON) in
}
}
func sendNewJob(fname:String,title:String,mobile:String,des:String,
lat:String,lon:String,onCompletion: #escaping (JSON) -> Void) {
let route = baseURL+"up=1&Name=\(fname)&BusinessName=\(title)&MobileNumber=\(mobile)&latitude=\(lat)&longitude=\(lon)&Description=\(des)"
makeHTTPGetRequest(path: route, onCompletion: { json, err in
onCompletion(json as JSON)
})
}
// MARK: Perform a GET Request
private func makeHTTPGetRequest(path: String, onCompletion: #escaping ServiceResponse) {
let request = NSMutableURLRequest(url: NSURL(string: path)! as URL) // line of my error
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, error as NSError?)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
}
This happens when the code executes a nil value. Here the code NSURL(string: path)! value might be nil. You can use optional binding (if let) to check whether the NSURL is a valid one. It happens when the string is not valid and does not make a valid URL.
You can use like this :
private func makeHTTPGetRequest(path: String, onCompletion: #escaping ServiceResponse) {
if let encodedPath = path.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) {
if let url = URL(string: encodedPath) {
let request = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: request as URLRequest, completionHandler: {data, response, error -> Void in
if let jsonData = data {
let json:JSON = JSON(data: jsonData)
onCompletion(json, error as NSError?)
} else {
onCompletion(nil, error as NSError?)
}
})
task.resume()
} else {
print("url is nil")
onCompletion(nil)
}
} else {
print("unable to encode url")
onCompletion(nil)
}
}

How to wait for completion dataTaskWithRequest - Swift?

I try to compare files (tiles) replace the old file on the phone new file from the site, if necessary. But returnPathFile in func pathDir is assigned reference to a file without waiting for the check for and download a new file. How do I get to wait until the completion of testing and loading (if necessary) of the new file?
let urls = { (x: UInt, y: UInt, zoom: UInt) -> NSURL in
let pathtodir = "tiles/\(zoom)/\(x)"
let pathtofile = "\(y).png"
let url = FileManagement.pathDir(pathtodir, file: pathtofile)
return url
}
layerTile = GMSURLTileLayer(URLConstructor: urls)
layerTile.map = self.viewMap!
Method of analyzing whether there is a file in iPhone:
class func pathDir(dir: String, file: String) -> NSURL {
var returnPathFile = NSURL(string: "http://****.***/\(dir)/\(file)")
let url = NSURL(string: "http://*****.***/\(dir)/\(file)")
let pathFile = applicTempDir().stringByAppendingPathComponent("\(dir)/\(file)")
let documentsUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()).URLByAppendingPathComponent("\(dir)/\(file)")
var attributesToInt: Int!
if filemgr.fileExistsAtPath("\(pathFile)") {
returnPathFile = NSURL(fileURLWithPath: pathFile)
let fileManager = NSFileManager.defaultManager()
do {
let attributes = try fileManager.attributesOfItemAtPath(pathFile)["NSFileCreationDate"] as! NSDate
attributesToInt = Int(attributes.timeIntervalSinceReferenceDate)
}
catch let error as NSError {
print("Ooops! Something went wrong: \(error)")
}
JsonData.isConnectedToNetwork(url!, dateFileOnTemp: attributesToInt, urlFileOnTemp: documentsUrl, pathFileOnTemp: pathFile, completion:{(path:String, error:NSError!) in
})
if filemgr.fileExistsAtPath("\(pathFile)") {
returnPathFile = NSURL(fileURLWithPath: pathFile)
return returnPathFile!
} else {
return returnPathFile!
}
}
}
Method compere two files:
class func isConnectedToNetwork(url: NSURL, dateFileOnTemp: Int, urlFileOnTemp: NSURL, pathFileOnTemp: String, completion:(path:String, error:NSError!) -> Void) {
var dates: String!
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "HEAD"
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
if let response = response as? NSHTTPURLResponse {
dates = response.allHeaderFields["Last-Modified"] as! String
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "EEE',' dd MMM yyyy HH':'mm':'ss 'GMT'"
dateFormatter.timeZone = NSTimeZone(abbreviation: "GMT")
let dateFileOnSite = dateFormatter.dateFromString(dates)
let dateFileOnSiteInt = Int(dateFileOnSite!.timeIntervalSinceReferenceDate)
if dateFileOnSiteInt > dateFileOnTemp {
FileManagement.reDelFile(pathFileOnTemp)
JsonData.reLoadFileAsync(url, destinationUrl: urlFileOnTemp as NSURL, completion:{(path:String, error:NSError!) in
})
}
}
}
else {
completion(path: urlFileOnTemp.path!, error:error)
}
})
task.resume()
}
Method loads new file:
class func reLoadFileAsync(url: NSURL, destinationUrl: NSURL, completion:(path:String, error:NSError!) -> Void) {
let sessionConfig = NSURLSessionConfiguration.defaultSessionConfiguration()
let session = NSURLSession(configuration: sessionConfig, delegate: nil, delegateQueue: nil)
let request = NSMutableURLRequest(URL: url)
request.HTTPMethod = "GET"
let task = session.dataTaskWithRequest(request, completionHandler: { (data: NSData?, response: NSURLResponse?, error: NSError?) -> Void in
if (error == nil) {
if let response = response as? NSHTTPURLResponse {
if response.statusCode == 200 {
if data!.writeToURL(destinationUrl, atomically: true) {
completion(path: destinationUrl.path!, error:error)
} else {
let error = NSError(domain:"Error saving file", code:1001, userInfo:nil)
completion(path: destinationUrl.path!, error:error)
}
}
}
}
else {
completion(path: destinationUrl.path!, error:error)
}
})
task.resume()
}
Try to use NSOperationQueue and add your operations accordingly to the queue. This thread might help you NSOperation and NSOperationQueue working thread vs main thread