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
}
Related
When we try to connect with MacCatalyst, the system asks for a password every connection, but when working on a project created for only MacOS, it connects directly without asking for a password. The output of the “set” functions in the KeychainWrapper class is the same for both projects, but when I compare both keys in keychain access, there are differences as show in the attachment and my codes
Keychain Screenshot
Here is my IKEv2 connection codes:
public func connectIKEv2(config: Configuration, onError: #escaping (String)->Void) {
let p = NEVPNProtocolIKEv2()
p.authenticationMethod = NEVPNIKEAuthenticationMethod.none
p.deadPeerDetectionRate = NEVPNIKEv2DeadPeerDetectionRate.medium
p.disableRedirect = false
p.enableRevocationCheck = false
p.enablePFS = false
p.useExtendedAuthentication = true
p.remoteIdentifier = config.server
p.useConfigurationAttributeInternalIPSubnet = false
p.serverAddress = config.server
p.username = config.account
p.passwordReference = config.getPasswordRef()
loadProfile { _ in
self.manager.protocolConfiguration = p
self.manager.onDemandRules = [NEOnDemandRuleConnect()]
self.manager.isOnDemandEnabled = true
self.manager.isEnabled = true
self.saveProfile { success in
if !success {
onError("Unable to save vpn profile")
return
}
else {
print("Mayank: Profile saved")
}
self.loadProfile() { success in
if !success {
onError("Unable to load profile")
return
}
let result = self.startVPNTunnel()
if !result {
onError("Can't connect")
}
else {
print("Mayank: connecting with result")
print(result)
}
}
}
}
}
Here is KeychainWrapper set functions:
#discardableResult open func set(_ value: String, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
if let data = value.data(using: .utf8) {
return set(data, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
#discardableResult open func set(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
keychainQueryDictionary[SecValueData] = value
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
} else {
keychainQueryDictionary[SecAttrAccessible] = KeychainItemAccessibility.whenUnlocked.keychainAttrValue
}
let status: OSStatus = SecItemAdd(keychainQueryDictionary as CFDictionary, nil)
if status == errSecSuccess {
return true
} else if status == errSecDuplicateItem {
return update(value, forKey: key, withAccessibility: accessibility)
} else {
return false
}
}
private func update(_ value: Data, forKey key: String, withAccessibility accessibility: KeychainItemAccessibility? = nil) -> Bool {
var keychainQueryDictionary: [String:Any] = setupKeychainQueryDictionary(forKey: key, withAccessibility: accessibility)
let updateDictionary = [SecValueData:value]
if let accessibility = accessibility {
keychainQueryDictionary[SecAttrAccessible] = accessibility.keychainAttrValue
}
let status: OSStatus = SecItemUpdate(keychainQueryDictionary as CFDictionary, updateDictionary as CFDictionary)
if status == errSecSuccess {
return true
} else {
return false
}
}
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()
})
}
I have An Array With Dictionaries example :
(
{
Email = "kate-bell#mac.com";
Name = "Kate Bell";
Number = "(555) 564-8583";
},
{
Email = "d-higgins#mac.com";
Name = "Daniel Higgins";
Number = "555-478-7672";
}
)
And i want to filter this dictionary according to Key "Name"
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
let predicate = NSPredicate(format:"Name == %#", searchText)
let filteredArray = (arrContact as NSMutableArray).filteredArrayUsingPredicate(predicate)
print(filteredArray)
if(filteredArray.count == 0){
searchActive = false;
} else {
searchActive = true;
}
tblData.reloadData()
}
I am always getting empty array in result from above swift code. Please help me to resolve this issue. Thanks
I suggest using Swift's filter instead:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
let filteredArray = arrContact.filter { $0["Name"] == searchText }
print(filteredArray)
if filteredArray.isEmpty {
searchActive = false
} else {
searchActive = true
}
tblData.reloadData()
}
And as mentioned by #LeoDabus in his comment, you can even simplify further:
func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
let filteredArray = arrContact.filter { $0["Name"] == searchText }
print(filteredArray)
searchActive = !filteredArray.isEmpty
tblData.reloadData()
}
Try This for swift 3
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
// Put your key in predicate that is "Name"
let searchPredicate = NSPredicate(format: "Name CONTAINS[C] %#", searchText)
let array = (arrContact as NSArray).filtered(using: searchPredicate)
print ("array = \(array)")
if(array.count == 0){
searchActive = false;
} else {
searchActive = true;
}
self.aTable.reloadData()
}
Swift
Get list of item/array from model class.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if self.searchBarItems.text == "" {
self.arrFilter = self.arrayDictionarySubModel
} else {
self.arrFilter.removeAll()
let filteredArray = self.arrayDictionarySubModel.filter { ($0.itemTitle?.contains(self.searchBarItems.text ?? ""))!}
print("filtered array:- ", filteredArray[0].itemTitle!)
self.arrFilter = filteredArray
}
self.searchItemTableView.reloadData()
}
where "name" is the key name
var namePredicate = NSPredicate(format: "Name like %#", String(searchText));
let searchPredicate = NSPredicate(format: "Name CONTAINS[C] %#", searchText)
arrrDict = self.arrrDict.filter { searchPredicate.evaluate(with: $0) };
you will get the result in dictionary specialy made to get contats from the phone book
I've been looking all over and can't seem to find an equivalent of String.unpack (ruby) for Swift. For example, I am trying to write these two statements in Swift:
offset = digest.unpack('#19C').first & 0x2f
value = digest.unpack("#{offset}#L").first
If anyone knows how I could accomplish this it would be very helpful, thank you!
EDIT:
Code so far:
import Foundation
class PasswordViewController: UIViewController {
#IBOutlet weak var simpleLabel: UILabel!
var email:String!
var LRA:String!
var sysid:String = "SECRET"
override func viewDidLoad() {
super.viewDidLoad()
var x:Int = 1446569017
reset_code(x)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
func reset_secret()->String {
return sysid.hmac(.SHA512, key: "THISISAKEY")
}
func reset_input(t:Int)->String{
var time:String = String(t)
var input:[String] = [sysid, email, time]
var stringrep:String = "|".join(input)
return stringrep
}
func reset_hmac(t:Int)->String{
var firstTime:String = reset_secret().hmac(.SHA256, key: reset_input(t))
return reset_secret().hmac(.SHA256, key: firstTime)
}
func reset_code(t:Int){
var time:Int = t / 43200
var digest:String = reset_hmac(t)
simpleLabel.text = digest
}
}
enum CryptoAlgorithm {
case SHA256, SHA512
var HMACAlgorithm: CCHmacAlgorithm {
var result: Int = 0
switch self {
case .SHA256: result = kCCHmacAlgSHA256
case .SHA512: result = kCCHmacAlgSHA512
}
return CCHmacAlgorithm(result)
}
var digestLength: Int {
var result: Int32 = 0
switch self {
case .SHA256: result = CC_SHA256_DIGEST_LENGTH
case .SHA512: result = CC_SHA512_DIGEST_LENGTH
}
return Int(result)
}
}
extension String {
func hmac(algorithm: CryptoAlgorithm, key: String) -> String {
let str = self.cStringUsingEncoding(NSUTF8StringEncoding)
let strLen = Int(self.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
let digestLen = algorithm.digestLength
let result = UnsafeMutablePointer<CUnsignedChar>.alloc(digestLen)
let keyStr = key.cStringUsingEncoding(NSUTF8StringEncoding)
let keyLen = Int(key.lengthOfBytesUsingEncoding(NSUTF8StringEncoding))
CCHmac(algorithm.HMACAlgorithm, keyStr!, keyLen, str!, strLen, result)
let digest = stringFromResult(result, length: digestLen)
result.dealloc(digestLen)
return digest
}
private func stringFromResult(result: UnsafeMutablePointer<CUnsignedChar>, length: Int) -> String {
var hash = NSMutableString()
for i in 0..<length {
hash.appendFormat("%02x", result[i])
}
return String(hash)
}
}
//
// ViewController.swift
// Arithmetic Calculator
//
// Created by Mapondera, Tanaka on 10/7/15.
// Copyright (c) 2015 Mapondera, Tanaka. All rights reserved.
//
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var calculatorDisplay: UILabel!
var isEnteredNumber = false
var firstNumber:Double = 0.0
var secondNumber:Double = 0.0
var operation = ""
var result:Double = 0.0
#IBAction func numberPunched(sender: AnyObject) {
// number
var number = sender.currentTitle
if isEnteredNumber {
calculatorDisplay.text = calculatorDisplay.text! + number!!
} else {
calculatorDisplay.text = number
isEnteredNumber = true
}
}
#IBAction func calculationPunched(sender: AnyObject) {
// operation
isEnteredNumber = false
firstNumber = calculatorDisplay.text?
// There is an error code for this line above that reads "cannot assign a value of type string to value of type double"...?
operation = sender.currentTitle!!
}
#IBAction func equalsPunched(sender: AnyObject) {
// equals
isEnteredNumber = false
var result = 0.0
secondNumber = calculatorDisplay.text?
// Error code for line above that reads "? must be followed by call, member lookup or subcrpit"...?
if operation == "+" {
result = firstNumber + secondNumber
} else if operation == "-" {
result = firstNumber - secondNumber
} else if operation == "x" {
result = firstNumber * secondNumber
} else if operation == "/" {
result = firstNumber / secondNumber
} /*else if operation == "^" {
result = pow(firstNumber, secondNumber)
}*/
calculatorDisplay.text = "\(result)"
}
#IBAction func changeSignPunched(sender: AnyObject) {
isEnteredNumber = false
firstNumber = calculatorDisplay.text?
// There is an Error code for the line above that reads "? must be followed by call, member lookup or subscrpit"...?
result = firstNumber * (-1)
calculatorDisplay.text = "\(result)"
}
#IBAction func clearPunched(sender: AnyObject) {
firstNumber = 0
secondNumber = 0
isEnteredNumber = false
result = 0
calculatorDisplay.text = "\(result)"
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
You're trying to assign a String value to a variable you've already declared to be a Double. Try something like this for the lines that are giving you errors:
let firstNumberString = calculatorDisplay.text ?? ""
firstNumber = Double(firstNumberString) ?? 0.0
The ?? above is called the nil coalescing operator. It will unwrap an optional on the left side or return a default value on the right.