EventKit Unit testing - stall on main thread - swift

I need to create multiple calendar events for Unit Test in Xcode. The call needs to be async, because of the access rights request:
eventStore.requestAccess(to: EKEntityType.event, completion: {
granted, error in
//create events
})
This is done in setUp() in a loop to create multiple events. The problem is that not all the completion blocks are done when the test starts running. I have implemented the waitForExpectations() method, to make it wait till the counter meets the requirements. But I get the following error message:
Stall on main thread
Here is the relevant code - setUp:
override func setUp()
{
super.setUp()
if let filepath = Bundle.main.path(forResource: "MasterCalendar", ofType: "csv") {
do {
let expectationCreation = expectation(description: "create events expectation")
let contents = try String(contentsOfFile: filepath)
//print(contents)
let testDataArray = importTestDataArrayFrom(file: contents)
createTestEvents(testDataArray: testDataArray)
{ (eventsArray) in
self.testEvents = eventsArray
print("finished creating events")
expectationCreation.fulfill()
}
waitForExpectations(timeout: 10.0) { (error) in
if error != nil {
XCTFail((error?.localizedDescription)!)
}
}
} catch {
print("contents could not be loaded")
}
} else {
print("example.txt not found!")
}
}
Create events:
func createTestEvents(testDataArray: [TestData], completion: #escaping (_ eventArray: [EKEvent]) -> Void) -> Void
{
let eventStore = EKEventStore()
var testEvents = [EKEvent]()
var index = 0
for testDataEvent in testDataArray
{
eventStore.requestAccess(to: EKEntityType.event, completion: {
granted, error in
if (granted) && (error == nil) {
print("===GRATNED, CREATING EVENT-", index)
index += 1
let event = EKEvent.init(eventStore: eventStore)
event.title = testDataEvent.combinedFields
event.startDate = Date()
event.endDate = Date()
event.isAllDay = false
var attendees = [EKParticipant]()
for i in 0 ..< 5 {
if let attendee = self.createParticipant(email: "test\(i)#email.com") {
attendees.append(attendee)
}
}
event.setValue(attendees, forKey: "attendees")
try! eventStore.save(event, span: .thisEvent)
let meetingObj = Parser.parse(calendarEvent: event)
testDataEvent.meeting = meetingObj
testEvents.append(event)
if(testEvents.count == testDataArray.count){
completion (testEvents)
}
}
})
}
}

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.

Cannot share with UICloudSharingController; vanishes with "uploading" message

while presenting the UICloudSharingController on top of a view, it presents the screen and when I select the messages option to send a message to a person whom I want to share with, it gives a spinning wheel with "uploading" message and vanishes - attached.
However when I go to cloudkit dashboard the root record has been shared. But I cannot share it with specific person. Is it because it has shared global? How can I fix it?
self.shareInfraRecord(zoneID: appDelegate.privateContactZoneID, completion: { (status) in
if ( status == false) {
return
}
})
func shareInfraRecord(zoneID: CKRecordZone.ID, completion: #escaping(Bool) -> Void) {
if let rootRecord = self.rootRecord {
if self.rootRecord?.share == nil {
let sharingController = UICloudSharingController { (controller, preparationHandler: #escaping (CKShare?, CKContainer?, Error?) -> Void) in
let shareID = CKRecord.ID(recordName: UUID().uuidString, zoneID: zoneID)
var share = CKShare(rootRecord: rootRecord, shareID: shareID)
share[CKShare.SystemFieldKey.title] = Cloud.ShareInfrastructure.ContactShareTitleKey as CKRecordValue?
share[CKShare.SystemFieldKey.shareType] = Cloud.ShareInfrastructure.ContactShareTypeKey as CKRecordValue?
let modifyRecZoneOp = CKModifyRecordsOperation(recordsToSave:[rootRecord, share], recordIDsToDelete: nil)
modifyRecZoneOp.modifyRecordsCompletionBlock = { (records, recordID, error) in
if error != nil {
if let ckerror = error as? CKError {
if let serverVersion = ckerror.serverRecord as? CKShare {
share = serverVersion
}
completion(false)
}
}
preparationHandler(share, self.defaultContainer, error)
}
self.privateDB?.add(modifyRecZoneOp)
}
sharingController.availablePermissions = [.allowReadOnly, .allowPrivate]
sharingController.delegate = self
sharingController.popoverPresentationController?.sourceView = self.view
self.present(sharingController, animated:true, completion:nil)
} else {
let shareRecordID = rootRecord.share!.recordID
let fetchRecordsOp = CKFetchRecordsOperation(recordIDs: [shareRecordID])
fetchRecordsOp.fetchRecordsCompletionBlock = { recordsByRecordID, error in
guard error == nil, let share = recordsByRecordID?[shareRecordID] as? CKShare else {
if let ckerror = error as? CKError {
self.aErrorHandler.handleCkError(ckerror: ckerror)
//self.saveToCloudKitStatus(recordName: myRecordName, success: false)
}
completion(false)
return
}
DispatchQueue.main.async {
let sharingController = UICloudSharingController(share: share, container: self.defaultContainer!)
completion(true)
//completionHandler(sharingController)
}
}
self.privateDB?.add(fetchRecordsOp)
}
}
}
This might be a bit late but I was running into this issue too, while using NSPersistentCloudKitContainer and it seems the issue was just making sure that my iCloud container name in the Capabilities section of the settings matched my app bundle name ie iCloud.com.goddamnyouryan.MyApp

Why does the CKFetchRecordsOperation not work?

This operation does not retrieve anything. The CkFetchRecordsCOmpletion block does not get called? Thanks for your time!
public func getRecord(recordID: CKRecord.ID, completion: #escaping (CKRecord?, CKError?) -> Void) {
let operation = CKFetchRecordsOperation(recordIDs: [recordID])
operation.fetchRecordsCompletionBlock = { (records, error) in
// Checking for potential errors
if let error = error {
completion(nil, error as? CKError)
print(error)
}
if let record = records?[recordID] {
completion(record, nil)
}
}
operation.qualityOfService = .utility
let privateDatabase = CKContainer(identifier: "something").privateCloudDatabase
privateDatabase.add(operation)
}

cancel button using MBProgressView

I am trying to use cancel button with the MBProgressView. I am getting the error "cannot convert value of type '()' to expected argument type 'Selector'"
hud.button.addTarget(hud.progressObject, action: cancelButton(), for: .touchUpInside)
I have also tried doing this:
hud.button.addTarget(hud.progressObject, action: #selector(cancelButton), for: .touchUpInside)
and I got the error "Argument of #selector cannot refer to local function 'cancelButton()'".
Can anyone explain to me what am i doing wrong?
cancelButton should be in viewDidLoad or at least I need to find a way to access what's inside viewDidload, because I need to use hud and snapshot.progress to cancel the download:
override func viewDidLoad() {
super.viewDidLoad()
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.orintation = UIInterfaceOrientationMask.allButUpsideDown
if book?.bookPath != book?.bookPath {
print("HERE \(book?.bookPath)")
loadReader(filePaht: (book?.bookPath)!)
} else {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let strName = book?.id
let filePath = "\(documentsPath)/"+strName!+".pdf"
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
loadReader(filePaht: filePath)
return;
}
print("DOWNLOAD #1")
let reference = FIRStorage.storage().reference(forURL: (self.book?.bookURL)!)
let downloadTask = reference.data(withMaxSize: 50 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
} else {
if ((try! data?.write(to: URL.init(fileURLWithPath: filePath, isDirectory: false))) != nil) {
self.db.upDate(id: (self.book?.id)!, bookPath: filePath)
self.loadReader(filePaht: filePath)
}
}
}
downloadTask.observe(.resume) { (snapshot) -> Void in
// Download resumed, also fires when the download starts
}
downloadTask.observe(.pause) { (snapshot) -> Void in
// Download paused
}
downloadTask.observe(.progress) { (snapshot) -> Void in
DispatchQueue.global(qos: .default).async(execute: {() -> Void in
self.showHUDWithCancel("Downloading")
DispatchQueue.main.async(execute: {() -> Void in
})
})
self.hud.progressObject = snapshot.progress
}
downloadTask.observe(.success) { (snapshot) -> Void in
// Download completed successfully
print("Download Success")
SwiftLoader.hide()
}
downloadTask.observe(.failure) { (snapshot) -> Void in
//Download failed
print("Download failed")
}
}
}
func showHUDWithCancel(_ aMessage: String) {
self.hud = MBProgressHUD.showAdded(to: self.view, animated: true)
self.hud.mode = MBProgressHUDMode.annularDeterminate
self.hud.label.text = aMessage
self.hud.detailsLabel.text = "Tap to cancel"
let tap = UITapGestureRecognizer(target: self, action: #selector(cancelButton))
self.hud.addGestureRecognizer(tap)
}
func cancelButton() {
self.hud.hide(animated: true)
self.hud.progressObject?.cancel()
print("cancel button is working")
}
This is the Cancel Button function
func cancelButton() {
MBProgressHUD.hide(for: view, animated: true)
snapshot.progress?.pause()
}
Try this -
Call below showHUDWithCancel from where you want to add hud with Cancel.
class ViewController: UIViewController {
var hud = MBProgressHUD()
override func viewDidLoad() {
super.viewDidLoad()
}
func showHUDWithCancel(_ aMessage: String) {
self.hud = MBProgressHUD.showAdded(to: self.view, animated: true)
self.hud.label.text = aMessage
self.hud.detailsLabel.text = "Tap to cancel"
let tap = UITapGestureRecognizer(target: self, action: #selector(cancelButton))
self.hud.addGestureRecognizer(tap)
}
func cancelButton() {
self.hud.hide(animated: true)
// do your other stuff here.
}
}
Add this code within your viewDidLoad it will work.
override func viewDidLoad() {
super.viewDidLoad()
let appdelegate = UIApplication.shared.delegate as! AppDelegate
appdelegate.orintation = UIInterfaceOrientationMask.allButUpsideDown
if book?.bookPath != book?.bookPath {
print("HERE \(book?.bookPath)")
loadReader(filePaht: (book?.bookPath)!)
} else {
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
let strName = book?.id
let filePath = "\(documentsPath)/"+strName!+".pdf"
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath) {
loadReader(filePaht: filePath)
return;
}
print("DOWNLOAD #1")
let reference = FIRStorage.storage().reference(forURL: (self.book?.bookURL)!)
downloadTask = reference.data(withMaxSize: 50 * 1024 * 1024) { (data, error) -> Void in
if (error != nil) {
} else {
if ((try! data?.write(to: URL.init(fileURLWithPath: filePath, isDirectory: false))) != nil) {
self.db.upDate(id: (self.book?.id)!, bookPath: filePath)
self.loadReader(filePaht: filePath)
}
}
}
downloadTask.observe(.resume) { (snapshot) -> Void in
// Download resumed, also fires when the download starts
}
downloadTask.observe(.pause) { (snapshot) -> Void in
// Download paused
}
downloadTask.observe(.progress) { (snapshot) -> Void in OperationQueue.main.addOperation {
OperationQueue.main.addOperation {
self.hud.progressObject = snapshot.progress
self.showHUDWithCancel("Downloading")
}
}
}
downloadTask.observe(.success) { (snapshot) -> Void in OperationQueue.main.addOperation {
// Download completed successfully
print("Download Success")
OperationQueue.main.addOperation {
SwiftLoader.hide()
}
}
}
downloadTask.observe(.failure) { (snapshot) -> Void in OperationQueue.main.addOperation {
//Download failed
print("Download failed")
OperationQueue.main.addOperation {
_ = self.navigationController?.popViewController(animated: false)
}
}
}
}
}
Move definition of downloadTask outside of the viewDidLoad method scope into the class itself. This way you'll be able to access task directly, not via snapshot passed in observers, or progress attached to either downloadTask or progressHUD. Doing so you could access task from any method of your view controller including cancelButton():
task.pause()
instead of
snapshot.progress?.pause()
Final code could look like:
class ViewController: UIViewController {
var downloadTask: FIRStorageDownloadTask!
...
override func viewDidLoad() {
super.viewDidLoad()
...
let reference = FIRStorage.storage().reference(forURL: (self.book?.bookURL)!)
downloadTask = reference...
...
}
}
NOTICE: For those of you who use the latest version of MBProgressView, the button documentation has been changed:
/**
* A button that is placed below the labels. Visible only if a target / action is added and a title is assigned..
*/
So, the creation should look something like the following:
class Tools {
static func popLoadingDialog(viewParent: UIView,
label: String,
cancelTarget: Any? = nil,
cancelSelector: Selector? = nil) -> MBProgressHUD {
let loadingNotification = MBProgressHUD.showAdded(to: viewParent, animated: true)
loadingNotification.mode = MBProgressHUDMode.indeterminate
loadingNotification.label.text = label
if(cancelSelector != nil) {
loadingNotification.button.setTitle("Cancel", for: .normal)
loadingNotification.button.addTarget(cancelTarget, action: cancelSelector!, for: .touchUpInside)
}
return loadingNotification
}
}
and call it:
loadingIndicator = Tools.createLoadingDialog(viewParent: view,
label: "Please wait...",
cancelTarget: self,
cancelSelector: #selector(onCancelClick))
loadingIndicator?.show(animated: true)
}
#objc func onCancelClick(){
// do something when the user click on cancel...
}

Can't pass value? (CoreData and API-call)

I have some trouble with part of my Swift Application. I am trying to make two requests to a REST API (Spark's API) and save the requested variables as CoreData. However, I can not get the values passed propably from one function to the other, even though it's all within the same class.
So here is how it should work.
The function startTimer calls a series of functions every 60 seconds.
var timer: dispatch_source_t!
//Calls the getData function twice every 60 seconds. First to get the Posturevalue and then to get the Time value.
func startTimer() {
let queue = dispatch_queue_create("que", nil)
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC, 1 * NSEC_PER_SEC)
dispatch_source_set_event_handler(timer) {
self.getData("Posturevalue")
let Posturevalue:Int = self.GSValHolder
self.getData("Time")
let Time:Int = self.GSValHolder
let CoreID:String = self.GSCoreID
self.saveCoreData(Time, PostureVal: Posturevalue, CoreID: CoreID)
}
dispatch_resume(timer)
}
First call is to the getData function, which sends an handles the JSON response. I know this is working, cause I can print the variables and call the set funtion.
//Sends a request to the URL to receive JSON data.
func getData(Variblename:String)
{
let dataurl:String = "https://api.spark.io/v1/devices/"+DeviceID+"/"+Variblename+"/?access_token="+Accesstoken
if let url = NSURL(string: dataurl) {
let request = NSURLRequest(URL: url)
initialiseTaskForGettingData(request, element: "results")
}
}
//Handles the JSON data. Get the DeviceID and Variblevalue (Posture Value and Time value)
func initialiseTaskForGettingData(request: NSURLRequest, element:String)
{
let task = session.dataTaskWithRequest(request) {data, response, downloadError in
if let error = downloadError
{
}
else
{
var parsingError: NSError?
let parsedResult: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &parsingError)
if let coreData = parsedResult as? NSDictionary {
if let VariableVal = (coreData as NSDictionary)["result"] as? Int {
if let coreInfo = coreData["coreInfo"] as? NSDictionary {
if let deviceID = (coreInfo as NSDictionary)["deviceID"] as? String {
println(deviceID)
println(VariableVal)
self.GSValHolder = VariableVal
}
}
}
}
}
}
task.resume()
}
Then do I try to get the variable and assign it to a new variable. This part keep causing me trouble and won't work.
//Get/Set functions to pass AccessToken and CoreID from CoreInfoView to the Model.
var AccessToken:String!
var CoreID:String!
var GSAccessToken:String {
set (newAccessToken) {
self.AccessToken = newAccessToken
}
get {
return AccessToken
}
}
var GSCoreID:String {
set (newCoreID) {
self.CoreID = newCoreID
}
get {
return CoreID
}
}
var ValHolder:Int!
var GSValHolder:Int {
set (newVal) {
self.ValHolder = newVal
}
get {
return ValHolder
}
}
If it worked, the startTimer function would do the same over again in order to get the other variable. Finally I wan't to call the saveCoreData function and save the variables.
func saveCoreData(Time:Int, PostureVal:Int, CoreID:String) {
var appDel:AppDelegate = (UIApplication.sharedApplication().delegate as! AppDelegate)
var context:NSManagedObjectContext = appDel.managedObjectContext!
var request = NSFetchRequest(entityName: "Coredata")
var newCore = NSEntityDescription.insertNewObjectForEntityForName("Coredata", inManagedObjectContext: context) as! NSManagedObject
newCore.setValue(Time, forKey: "time")
newCore.setValue(PostureVal, forKey: "posturevalue")
context.save(nil)
println(newCore)
}
Hope it all makes sense and you are able to help me out.
Regards Mads R. Svendsen,
________________________________UPDATE_________________________________________
So I did a bit of reading and watched some tutorials on completionHandling and asynchronous, which helped a lot. My code now looks like this and works.
var timer: dispatch_source_t!
//Calls the getData function twice every 60 seconds. First to get the Posturevalue and then to get the Time value.
func startTimer() {
let queue = dispatch_queue_create("que", nil)
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue)
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 60 * NSEC_PER_SEC, 1 * NSEC_PER_SEC)
dispatch_source_set_event_handler(timer) {
var Time:Int!
var Posturevalue:Int!
var CoreID:String!
self.getData("Posturevalue", completionHandler: { (result) -> Void in
if (result == true) {
Posturevalue = self.GSValHolder
self.getData("Time", completionHandler: { (result) -> Void in
if (result == true) {
Time = self.GSValHolder
CoreID = self.GSCoreID
self.saveCoreData(Time, PostureVal: Posturevalue, CoreID: CoreID)
}
})
}
})
}
dispatch_resume(timer)
}
// Temporary constants to compose the URL. Has to be replaced with the values passed on from the CoreInfoView.
let DeviceID:String = "51ff72065082554944270887"
let Accesstoken:String = "09f54420e11539de00c13284f9fec31d3fba410d"
let session = NSURLSession.sharedSession()
//Sends a request to the URL to receive JSON data.
func getData(Variblename:String, completionHandler: (result:Bool!) -> Void)
{
let dataurl:String = "https://api.spark.io/v1/devices/"+DeviceID+"/"+Variblename+"/?access_token="+Accesstoken
if let url = NSURL(string: dataurl) {
let request = NSURLRequest(URL: url)
initialiseTaskForGettingData(request, element: "results", completionHandler: { (result) -> Void in
if (result == true) {
completionHandler(result: true)
}
else {
completionHandler(result: false)
}
})
}
}
//Handles the JSON data. Get the DeviceID and Variblevalue (Posture Value and Time value)
func initialiseTaskForGettingData(request: NSURLRequest, element:String, completionHandler: (result:Bool!) -> Void)
{
let task = session.dataTaskWithRequest(request) {data, response, downloadError in
if let error = downloadError
{
completionHandler(result: false)
}
else
{
var parsingError: NSError?
let parsedResult: AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments, error: &parsingError)
if let coreData = parsedResult as? NSDictionary {
if let VariableVal = (coreData as NSDictionary)["result"] as? Int {
if let coreInfo = coreData["coreInfo"] as? NSDictionary {
if let deviceID = (coreInfo as NSDictionary)["deviceID"] as? String {
println(deviceID)
println(VariableVal)
self.GSValHolder = VariableVal
completionHandler(result: true)
}
}
}
}
}
}
task.resume()
}
However, I figured out that the dataTaskWithRequest can be written with a completionHandler, but I could not make it work. I was wondering if that might make the code a bit more simple?