Skip to content

Instantly share code, notes, and snippets.

@Ptujec
Created May 7, 2022 21:35
Show Gist options
  • Save Ptujec/3c875971a6887da5db2fa7315b1f8c18 to your computer and use it in GitHub Desktop.
Save Ptujec/3c875971a6887da5db2fa7315b1f8c18 to your computer and use it in GitHub Desktop.
Trying to display contacts in LaunchBar sorted by distance … but struggle with async part
#!/usr/bin/env swift
/*
Contacts Location Action for LaunchBar
by Christian Bender (@ptujec)
2022-05-05
Copyright see: https://github.com/Ptujec/LaunchBar/blob/master/LICENSE
*/
import AppKit
import Contacts
import CoreLocation
import Foundation
let arguments = Array(CommandLine.arguments.dropFirst())
// let arguments = [String]()
var argument = ""
if arguments.count == 0 {
argument = "My Location" // Should be current location
// https://www.zerotoappstore.com/how-to-get-current-location-in-swift.html
} else {
argument = arguments[0]
}
var resultJSON = [[String: Any]]()
let keysToFetch = [CNContactGivenNameKey, CNContactFamilyNameKey, CNContactPostalAddressesKey, CNContactOrganizationNameKey] as [CNKeyDescriptor]
var contacts = [CNContact]()
let request = CNContactFetchRequest(keysToFetch: keysToFetch)
let contactStore = CNContactStore()
do {
// // Only Contacts that fit argument
// let predicate = CNContact.predicateForContacts(matchingName: arguments[0])
// // let predicate = CNContact.predicateForContacts(matchingName: "Schneider") // change later to get all contacts and use argument for Address
// let contacts = try contactStore.unifiedContacts(matching: predicate, keysToFetch: keysToFetch)
// print(contacts)
// if contacts.count == 0 {
// exit(0)
// }
// All Contacts
try contactStore.enumerateContacts(with: request) {
contact, _ in
// Array containing all unified contacts from everywhere
contacts.append(contact)
}
for contact in contacts {
// print(contact)
var contactName = ""
// TODO: Ersetzen mit PersonNameComponentsFormatter oder CNContactFormatter ? https://swiftwombat.com/how-to-use-personnamecomponentsformatter-in-swift/
// https://developer.apple.com/documentation/foundation/personnamecomponentsformatter
if contact.familyName.isEmpty && contact.givenName.isEmpty {
contactName = contact.organizationName
} else if !contact.familyName.isEmpty && !contact.givenName.isEmpty {
contactName = "\(contact.givenName) \(contact.familyName)"
} else if !contact.familyName.isEmpty && contact.givenName.isEmpty {
contactName = contact.familyName
} else if contact.familyName.isEmpty && !contact.givenName.isEmpty {
contactName = contact.givenName
}
let contactAddressCount = contact.postalAddresses.count
// print(contactAddressCount)
var contactAddressString = ""
if contactAddressCount > 0 { // wenn kein FamilyName dann Firma
let contactAddress = contact.postalAddresses[0]
contactAddressString = CNPostalAddressFormatter
.string(from: contactAddress.value, style: .mailingAddress)
.replacingOccurrences(of: "\n", with: ", ")
// Insert Code for Distance form Contact Address to Address enter by argument[0] or default (current address)
// --------------------------------------
// func getCoordinates(from address: String, completion: @escaping ((CLLocationCoordinate2D) -> Void)) {
// let geoCoder = CLGeocoder()
// geoCoder.geocodeAddressString(address) { placemarks, _ in
// guard let placemarks = placemarks, let location = placemarks.first?.location else { return }
// DispatchQueue.main.async {
// completion(location.coordinate)
// }
// }
// }
//
// var coo1: CLLocation = CLLocation(latitude: 0.0, longitude: 0.0)
//
// getCoordinates(from: contactAddressString) { location in
//
// coo1 = CLLocation(latitude: location.latitude, longitude: location.longitude)
//
// var coo2 = CLLocation(latitude: 0, longitude: 0)
//
// getCoordinates(from: argument) { location in
// coo2 = CLLocation(latitude: location.latitude, longitude: location.longitude)
//
// print(coo2)
//
// let distanceInMeters = coo1.distance(from: coo2) // result is in meters
// let distanceInKm = distanceInMeters / 1000
//
// let formatter = NumberFormatter()
// formatter.maximumFractionDigits = 1
// formatter.minimumFractionDigits = 0
//
// let distanceNice = formatter.string(for: distanceInKm)
//
// print("Distanz: \(distanceNice!) km")
// }
// }
// --------------------------------------
let contactJSON = [
"title": contactName,
"subtitle": contactAddressString,
"badge": "Distance to \(argument)", // Distance goes here e.g. "1,5 Km"
"icon": "contactTemplate",
]
resultJSON.append(contactJSON)
}
}
func dictSort(dict1: [String: Any], dict2: [String: Any]) -> Bool {
guard let i0 = dict1["title"] as? String, // change title to badge when there is an actuall value
let i1 = dict2["title"] as? String else { return false }
return i0 < i1
}
let sortedArray = resultJSON.sorted { dictSort(dict1: $0, dict2: $1) }
// Serialize to JSON
let jsonData = try JSONSerialization.data(withJSONObject: sortedArray)
// Convert to a string and print
if let JSONString = String(data: jsonData, encoding: String.Encoding.utf8) {
print(JSONString)
}
} catch {
NSLog("Failed to fetch contact, error: \(error)")
}
@Ptujec
Copy link
Author

Ptujec commented May 10, 2022

Thanks. Now I see my mistake. I did not understand that main.swift needed to be in "Sources/…"

@marcomasser
Copy link

Ah, right. I forgot to mention that. Sorry about that.

@Ptujec
Copy link
Author

Ptujec commented May 10, 2022

No worries. Glad it works. First time using a debugger ;) I set a breakpoint at line 151 and got "No debug session". On line 141 there are results … I can actually quicklook the coordinates. So it seems like the second getCoordinates is a problem somehow?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment