Returning from a function in a closure - xcode

I need to get an array of events for a certain day in Swift. Because EKEventStore.requestAccessToEntityType is run asynchronously, I am using dispatch_group_notify to wait for the completion handler of EKEventStore.requestAccessToEntityType to complete so I can do something with it then return it. But I can't return from the function inside the closure I pass to dispatch_group_notify because it tries the return from the closure, not the function. Here is my code
class func eventsOn(date: NSDate) -> [Event] {
var events = [Event]()
var ekEvents = [EKEvent]()
var dispatch_group = dispatch_group_create()
dispatch_group_enter(dispatch_group)
Event.eventStore.requestAccessToEntityType(EKEntityTypeEvent, completion: {
gotAccess, error in
let cal = NSCalendar.currentCalendar()
var comps = cal.components(.CalendarUnitYear | .CalendarUnitMonth | .CalendarUnitDay, fromDate: date)
let aFewHoursAgo = NSDateComponents()
aFewHoursAgo.hour = -5
var daStartDate = cal.dateFromComponents(comps)!
daStartDate = cal.dateByAddingComponents(aFewHoursAgo, toDate: daStartDate, options: .allZeros)!
var aDay = NSDateComponents()
aDay.day = 1
var daEndDate = cal.dateByAddingComponents(aDay, toDate: daStartDate, options: .allZeros)!
daEndDate = daEndDate.dateByAddingTimeInterval(-1)!
let p = Event.eventStore.predicateForEventsWithStartDate(daStartDate, endDate: daEndDate, calendars: [Event.eventStore.defaultCalendarForNewEvents])
let tmp = Event.eventStore.eventsMatchingPredicate(p) as [EKEvent]?
if let _ = tmp {
ekEvents = tmp!
}
dispatch_group_leave(dispatch_group)
})
let queue = dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)
dispatch_group_notify(dispatch_group, queue) {
println(ekEvents)
let ekEventId = Expression<String>("ekEventId")
let eventId = Expression<Int>("id")
for ekEvent in ekEvents {
let daEventId = Event.db["events"].filter(ekEventId == ekEvent.eventIdentifier).select(eventId).first![eventId]
events.append(Event.eventWithId(daEventId)!)
}
return events
}
}
So where I put return events inside of the block I passed to dispatch_group_notify it tries to return from the closure, but I'd like to return from the function. How can I do this?

Related

Is there a way I can make my script more efficient?

I have created the below script to add a timestamp when a cell equals a specific value. This script repeats for each row but it is not efficient. How can I make this simpler and quicker? Below is an extract of the script, it repeats for each row
function MyFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet()
var sheet = ss.getSheetByName('Overview')
var cell = sheet.getRange('H3').getValue()
var sent = sheet.getRange('p3').getValue()
if (cell == "Full" && sent == "") {
sheet.getRange('p3').setValue(new Date())
}
else if (cell == "Open" && sent == "") {
sheet.getRange('p3').setValue("")
}
}
Try this.
function MyFunction() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Overview');
// get range H3:P were H=8, P=16, 9 columns total or index 0 to 8
var data = sheet.getRange(3,8,sheet.getLastRow()-2,9).getValues(); // get range H3:P
var i = 0;
var row = null;
for( i=0; i<data.length; i++ ) {
row = data[i];
if( ( row[0] === "Full" ) && ( row[8] === "" ) ) {
row[8] = new Date();
}
// Your else doesn't do anything if blank set blank ??
row.splice(0,8); // extract only column P
}
sheet.getRange(3,16,data.length,1).setValues(data);
}
Try using setvalues() method of class range
function MyFunction() {
const ss = SpreadsheetApp.getActive()
const sh = ss.getSheetByName('Overview');
const rg = sh.getDataRange();
const vs = rg.getValues();
let vo = vs.map(r => {
if( r[7] == "Full") r[15]=new Date()
if( r[7] == "Open") r[15]='';
return [r[15]];
});
sh.getRange(2,16,vo.length,1).setValues(vo);
}

How to add column to Sqlite.swift

let tango_id = Expression<Int64>("tango_id")
let e_score = Expression<Double>("e_score")
let interval = Expression<Double>("interval")
let next_revision_date = Expression<Date>("next_revision_date")
let bookmark = Expression<Bool>("bookmark")
let bookmark_date = Expression<Date>("bookmark_date")
let learnt = Expression<Bool>("learnt")
do {
try userdb.run(userTable.create { t in
t.column(tango_id, primaryKey: true)
t.column(e_score)
t.column(interval)
t.column(next_revision_date)
t.column(bookmark)
t.column(learnt)
})
}
catch let error {
print("creation failed: \(error)")
}
I want to add a column for bookmark_date which is defined as Expression

fatal : Array index out of range

Every child has a pick up point location consists of latitude and longitude that i fetch from sqlite database. I have childIdArray which is array of childID's. I want to calculate the estimate time(ETA) from current location of their respective drivers location i fetch from getCurrentLocationOfRespectiveDrivers() method with pickUp point of children that i fetch from sqlite database in form of array of latitude and longitude.
but i get error
fatal error: Array index out of range
in getCurrentLocationOfRespectiveDrivers() method.
Any help would be highly appreciated.. thanks in advance
var etaArray : [String] = []
var estimatedTime : String?
var latitudeArray : [Double] = []
var longitudeArray : [Double] = []
override func viewDidLoad() {
super.viewDidLoad()
let rs = ModelManager.sharedInstance.fetchingPickUpAnnotationsArray(ChildDetailsVC.parentID)
latitudeArray = rs.latPickUp
longitudeArray = rs.longPickUp
print("pickUpLatitudeArray = \(latitudeArray)")
print("pickUpLongitudeArray = \(longitudeArray)")
let result = ModelManager.sharedInstance.fetchingChildren( ChildDetailsVC.parentID)
self.childNameArray = result.childNames
self.childImageArray = result.childImages
self.childIDArray = result.chidIDs
self.childDriverArray = result.childDrivers
getCurrentLocationOfRespectiveDrivers()
for (var i = 0; i < childIDArray.count; i++)
{
etaArray.append("")
}
}
func getCurrentLocationOfRespectiveDrivers()
{
for( var i = 0 ; i < latitudeArray.count ; i++)
{
let url:NSURL = NSURL(string:"http://development.ssntpl.com/gogo_app/api.php?action=getDriverCurrentLocation")!
let request = NSMutableURLRequest(URL:url)
request.HTTPMethod = "POST"
let post:NSString = "child_id=\(childIDArray)"
request.HTTPBody = post.dataUsingEncoding(NSUTF8StringEncoding)
let task = NSURLSession.sharedSession().dataTaskWithRequest(request)
{
data, response, error in
if error != nil
{
print("error is \(error)")
return;
}
do
{
let myJSON = try NSJSONSerialization.JSONObjectWithData(data!, options: .MutableContainers) as? NSDictionary
if let parseJSON = myJSON
{
let Status = parseJSON["status"] as! Int
let Code = parseJSON["code"] as! Int
if (Status == 1 && Code == 200)
{
let Result = parseJSON["result"] as! NSArray
self.etaArray.removeAll()
//here i get the crash fatal error: Array index out of range
let latitudePickUp = self.latitudeArray[i]
let longitudePickUp = self.longitudeArray[i]
let CoordinatePickUp = CLLocation(latitude: latitudePickUp, longitude: longitudePickUp)
for res in Result
{
self.estimatedTime = ""
if (res["exists"] as! Int == 1)
{
let lastLatitude = res["latitude"] as! CLLocationDegrees
let lastLongitude = res["longitude"] as! CLLocationDegrees
let Location : CLLocation = CLLocation(latitude: lastLatitude, longitude: lastLongitude)
let meters:CLLocationDistance = Location.distanceFromLocation(CoordinatePickUp)
print(meters)
let ID = res["child_id"] as! String
let time = round(meters/40000 * 60)
self.estimatedTime = String(time)
self.etaArray.append(self.estimatedTime!)
}
else
{
self.etaArray.append("")
}
}
}
else
{
}
}
}
catch
{
print(error)
}
}
//executing the task
task.resume()
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.tableView.reloadData()
})
}

Reorder UicollectioView items using RealmSwift on drag and drop

I'm Trying reorder UICollectionViewcell Images on drag and drop using RealmSwift As database, My UI is not updating on a drag and drop and strange behaviour, some Images are duplicating , my code is Like this
RealmModel As
class StoryAlbumDM: Object {
dynamic var id = 0
dynamic var type = ""
dynamic var isImage: Int = 0
dynamic var textData = ""
dynamic var imageData: NSData? = nil
dynamic var rowId: Int = 0
dynamic var position: Int = 0
dynamic var storyId: Int = 0
dynamic var isCoverImage: Int = 0
dynamic var imagePath = ""
let allStories = List<StoryAlbumDM>()
}
On drag and drop I'm doing Like this
func collectionView(collectionView: UICollectionView, atIndexPath: NSIndexPath, didMoveToIndexPath toIndexPath: NSIndexPath) {
print("moveItemAtIndexPath")
let fromIndexPath: Int = atIndexPath.row
print("from", fromIndexPath)
let toIndexPathInt: Int = toIndexPath.row
print("To", toIndexPath)
let fromData: StoryAlbumDM!
fromData = realm.objects(StoryAlbumDM.self).filter("position = %d AND storyId = %d", fromIndexPath, self.storyID).first!
let toData: StoryAlbumDM!
toData = realm.objects(StoryAlbumDM.self).filter("position = %d AND storyId = %d", toIndexPath, self.storyID).first!
var tempData = StoryAlbumDM()
self.performSelectorOnMainThread(#selector(StoryViewController.updateSrtoryInRealm), withObject: self.collectionView, waitUntilDone: true)
dispatch_async(dispatch_get_main_queue(), {
self.collectionView.performBatchUpdates({
self.collectionView.reloadData()
}, completion: nil)
})
}
func updateSrtoryInRealm() {
self.tempData.type = self.toData.type
self.tempData.isImage = self.toData.isImage
self.tempData.textData = self.toData.textData
self.tempData.rowId = self.toData.rowId
self.tempData.imageData = self.toData.imageData
self.tempData.position = self.toData.position
self.tempData.storyId = self.toData.storyId
self.tempData.isCoverImage = self.toData.isCoverImage
self.tempData.imagePath = self.toData.imagePath
do {
try! realm.write {
self.toData.type = self.fromData.type
self.toData.isImage = self.fromData.isImage
self.toData.textData = self.fromData.textData
self.toData.rowId = self.fromData.rowId
self.toData.imageData = self.fromData.imageData
self.toData.position = self.fromData.position
self.toData.storyId = self.fromData.storyId
self.toData.isCoverImage = self.fromData.isCoverImage
self.toData.imagePath = self.fromData.imagePath
// title.id = temp.id
self.fromData.type = self.tempData.type
self.fromData.isImage = self.tempData.isImage
self.fromData.textData = self.tempData.textData
self.fromData.rowId = self.tempData.rowId
self.fromData.imageData = self.tempData.imageData
self.fromData.position = self.tempData.position
self.fromData.storyId = self.tempData.storyId
self.fromData.isCoverImage = self.tempData.isCoverImage
self.fromData.imagePath = self.tempData.imagePath
}
//}
}
catch {
print("Printed error : ")
}
Problem: Images Are duplicating, Not updating on UI , Reorder strange behaviour, please help me on this
I answered a similar question recently, but I'll re-explain it here. :)
Easily, the best and quickest way to re-order Realm objects inside a Realm file is to make an overarching List object that holds all of the Realm objects of a given type.
For example in this case, you make another object to hold that allStories value you already created:
// Model class that manages the ordering of story album objects
class StoryAlbumDMList: Object {
let allStories = List<StoryAlbumDM>()
}
// Model class for the actual story album objects
class StoryAlbumDM: Object {
dynamic var id = 0
dynamic var type = ""
dynamic var isImage: Int = 0
dynamic var textData = ""
dynamic var imageData: NSData? = nil
dynamic var rowId: Int = 0
dynamic var position: Int = 0
dynamic var storyId: Int = 0
dynamic var isCoverImage: Int = 0
dynamic var imagePath = ""
}
This way, when you want to re-order the list, all you need to do is re-order them inside this array.
Like I said in the other question, one other way you can do it (Which is not as good, but also doesn't require an extra Realm object) is to add another property named orderedIndex, which simply contains a number indicating the numerical order of these objects. When you want to re-order them, it's simply a matter of re-setting these numbers.
Let me know if you need any more clarification!

Why is one label updating quicker then the other?

I am making my own Spotify controller to learn how to interact with AppleScript and Swift (using Objective-C as a bridge) to make a bigger project later on. The only issue is the current progress time seems to update slightly faster then the time left.
This is what my application looks like:
The time on the left seems to update slightly faster then the time on the right, this means it is not accurate and can be bit strange to look at. How would I be able to fix this so both the progress time and time left update at the same time?
Here is my code:
ViewController.swift
import Cocoa
class ViewController: NSViewController {
#IBOutlet weak var statusLabel: NSTextField!
#IBOutlet weak var songProgress: NSProgressIndicator!
#IBOutlet weak var lblCurrentProgress: NSTextField!
#IBOutlet weak var lblTrackLength: NSTextField!
//let debugger = Debug(appName:"Spotify Menu", debugable: )
var previousScript:String = ""
var toggleScript:String = ""
var nextScript:String = ""
var statusScript:String = ""
var artistScript:String = ""
var songNameScript:String = ""
var trackPosScript:String = ""
var trackLengthScript:String = ""
var trackIDscript:String = ""
var trackName:String = "unknown"
var artistName:String = "unknown"
var playerStatus:String = "unknown"
var trackID:String = "unknown"
var playerPosistion:Double = -1.0
var trackLength:Double = -1.0
var percentThrough:Double = -1.0
let timeFormatter = twoDiffrentTimes()
func main()
{
getStatus()
getSongName()
getArtist()
getTrackLength()
getPlayerPos()
getID()
percentThrough = (playerPosistion/trackLength)*100
if(playerStatus == "playing" && trackName != "unknown" && artistName != "unknown")
{
statusLabel.stringValue = "Spotify is currently playing the song \"\(trackName)\" by \"\(artistName)\""
} else if(playerStatus == "paused"){
statusLabel.stringValue = "Spotify is currently paused"
} else if(playerStatus == "stopped") {
statusLabel.stringValue = "Spotify is not playing any music"
} else {
statusLabel.stringValue = "There is currently an error"
}
//NSLog("The song is currently \(percentThrough.toDecimalPlace("2"))% the way in")
songProgress.doubleValue = percentThrough
timeFormatter.update(playerPosistion, timeTwo: trackLength-playerPosistion)
lblCurrentProgress.stringValue = timeFormatter.timeOneString
lblTrackLength.stringValue = timeFormatter.timeTwoString
}
override func viewDidLoad() {
super.viewDidLoad()
previousScript = readFile("previousTrack")
if(previousScript == "")
{
NSLog("Error finding previous track script")
exit(1)
}
toggleScript = readFile("playPause")
if(toggleScript == "")
{
NSLog("Error finding toggle script")
exit(1)
}
nextScript = readFile("nextTrack")
if(nextScript == "")
{
NSLog("Error going forwared a track")
exit(1)
}
statusScript = readFile("playerStatus")
if(statusScript == "")
{
NSLog("Error going back a track")
statusLabel.stringValue = "Spotify is in an unknown status"
exit(1)
}
artistScript = readFile("getArtist")
if(artistScript == "")
{
NSLog("Cannot get artist script")
exit(1)
}
songNameScript = readFile("getSongName")
if(songNameScript == "")
{
NSLog("Cannot get song name script")
exit(1)
}
trackPosScript = readFile("playerPosition")
if(trackPosScript == "")
{
NSLog("Error getting track posistion script")
exit(1)
}
trackLengthScript = readFile("durationOfSong")
if(trackLengthScript == "")
{
NSLog("Error getting track length script")
exit(1)
}
trackIDscript = readFile("trackID")
if(trackIDscript == "")
{
NSLog("Error getting")
}
main()
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: Selector("main"), userInfo: nil, repeats: true)
}
override var representedObject: AnyObject? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func previous(sender: AnyObject)
{
runAppleScript(previousScript)
}
#IBAction func toggle(sender: AnyObject)
{
runAppleScript(toggleScript)
}
#IBAction func next(sender: AnyObject)
{
let nextFile = readFile("nextTrack")
if(nextFile == "")
{
NSLog("Error going forwared a track")
} else {
runAppleScript(nextFile)
}
}
func getPlayerPos()
{
playerPosistion = runAppleScript(trackPosScript).toDouble()
}
func getTrackLength()
{
trackLength = runAppleScript(trackLengthScript).toDouble()
}
func getSongName()
{
trackName = runAppleScript(songNameScript)
}
func getArtist()
{
artistName = runAppleScript(artistScript)
}
func getID()
{
trackID = runAppleScript(trackIDscript)
}
func getStatus()
{
let resultData = runAppleScript(statusScript)
if(resultData == "playing" || resultData == "paused" || resultData == "stopped")
{
playerStatus = resultData
} else {
playerStatus = "unknown"
}
}
}
classes.swift
import Foundation
class twoDiffrentTimes
{
var timeOne:Double = 0.0
var iMinuteOne:Int = 0
var iSecondsOne:Int = 0
var minuteOne:String = ""
var secondsOne:String = ""
var timeOneString:String = ""
var uSecondsOne:Double = 0.0
var temp:Int = 0
var timeTwo:Double = 0.0
var iMinuteTwo:Int = 0
var iSecondsTwo:Int = 0
var minuteTwo:String = ""
var secondsTwo:String = ""
var timeTwoString:String = ""
var uSecondsTwo:Double = 0.0
func update(timeOne:Double, timeTwo:Double)
{
//First Time
self.temp = Int(timeOne)
self.iMinuteOne = temp / 60
self.iSecondsOne = temp % 60
self.minuteOne = "\(iMinuteOne)"
if(self.iSecondsOne < 10)
{
self.secondsOne = "0\(self.iSecondsOne)"
} else {
self.secondsOne = "\(self.iSecondsOne)"
}
self.timeOneString = "\(self.iMinuteOne).\(self.secondsOne)"
//Second Time
self.temp = Int(timeTwo)
self.iMinuteTwo = temp / 60
self.iSecondsTwo = temp % 60
self.minuteTwo = "\(iMinuteTwo)"
if(self.iSecondsTwo < 10)
{
self.secondsTwo = "0\(self.iSecondsTwo)"
} else {
self.secondsTwo = "\(self.iSecondsTwo)"
}
self.timeTwoString = "\(self.iMinuteTwo).\(self.secondsTwo)"
}
}
extensions.swift
import Foundation
extension String {
func toDouble() -> Double {
if let unwrappedNum = Double(self) {
return unwrappedNum
} else {
// Handle a bad number
print("Error converting \"" + self + "\" to Double")
return 0.0
}
}
}
extension Double {
func toDecimalPlace(f: String) -> String {
return NSString(format: "%0.\(f)f", self) as String
}
func decimalValue() -> Int
{
if let decimalString = String(self).componentsSeparatedByString(".").last, decimal = Int(decimalString) {
return decimal // 535
} else {
return -1
}
}
}
functions.swift
import Foundation
func readFile(fileName: String) -> String{
if let filepath = NSBundle.mainBundle().pathForResource(fileName, ofType: "txt") {
do {
let contents = try NSString(contentsOfFile: filepath, usedEncoding: nil) as String
print(contents)
return contents
} catch {
NSLog("Content Could Not Be Loaded")
return ""
}
} else {
NSLog("File \(fileName).\("txt") could not be found")
return ""
}
}
func runAppleScript(script:String) -> String
{
let theResult:String
let errorInfo = AutoreleasingUnsafeMutablePointer<NSDictionary?>()
let startAtLoginScript: NSAppleScript = NSAppleScript(source: script)!
let theDiscriptor:NSAppleEventDescriptor = startAtLoginScript.executeAndReturnError(errorInfo)
if let _ = theDiscriptor.stringValue
{
theResult = theDiscriptor.stringValue!
} else {
theResult = ""
}
return theResult
}
errorClases.swift
import Foundation
enum MyAppleScriptError: ErrorType {
case ExecutingScriptFailed
case GettingStringValueFailed
}
Last but not least durationOfSong.txt (AppleScript)
tell application "Spotify"
set songLength to (duration of current track) / 1000 as string
return songLength
end tell
And playerPosition
tell application "Spotify"
set currentPosistion to player position as string
return currentPosistion
end tell
Edit:
Updated code
I recommend you learn how to call AppleScript handlers directly from Swift via the AppleScript-ObjC bridge. That will work vastly better than mucking about with code strings and NSAppleScript, which is slow and nasty and not a good way to do it. Tutorial here:
http://macscripter.net/viewtopic.php?id=43127
--
ETA:
"I have successful moved NSAppleScript to Swift2. Only thing is that still doesn't fix the issue. it is still out of timing."
Well, that's annoying. FWIW, I'd ditch that twoDiffrntTimes class as it doesn't do anything that can't be done in a single-line function, and change the end of main() to this:
func main() {
...
lblCurrentProgress.stringValue = formatTime(Int(playerPosistion))
lblTrackLength.stringValue = formatTime(Int(trackLength-playerPosistion))
}
func formatTime(secs: Int) -> String {
return NSString(format: "%i.%02i", secs / 60, secs % 60) as String
}

Resources