-
-
Save Ptujec/3c875971a6887da5db2fa7315b1f8c18 to your computer and use it in GitHub Desktop.
| #!/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)") | |
| } |
Thanks again @marcomasser. I get the following error when trying to run it in xcode like you described:
Showing Recent Errors Only
Ld /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Products/Debug/nearbyContacts normal (in target 'nearbyContacts' from project 'nearbyContacts')
cd /Users/hischa/Developer/Xcode/nearbyContacts
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -target arm64-apple-macos11.0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX12.3.sdk -L/Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Products/Debug -L/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/usr/lib -F/Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Products/Debug -F/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -iframework /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/Library/Frameworks -filelist /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Intermediates.noindex/nearbyContacts.build/Debug/nearbyContacts.build/Objects-normal/arm64/nearbyContacts.LinkFileList -Xlinker -rpath -Xlinker /usr/lib/swift -Xlinker -rpath -Xlinker @executable_path/../lib -Xlinker -object_path_lto -Xlinker /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Intermediates.noindex/nearbyContacts.build/Debug/nearbyContacts.build/Objects-normal/arm64/nearbyContacts_lto.o -Xlinker -export_dynamic -Xlinker -no_deduplicate -fobjc-link-runtime -L/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift/macosx -L/usr/lib/swift -Xlinker -add_ast_path -Xlinker /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Intermediates.noindex/nearbyContacts.build/Debug/nearbyContacts.build/Objects-normal/arm64/nearbyContacts.swiftmodule -Xlinker -dependency_info -Xlinker /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Intermediates.noindex/nearbyContacts.build/Debug/nearbyContacts.build/Objects-normal/arm64/nearbyContacts_dependency_info.dat -o /Users/hischa/Library/Developer/Xcode/DerivedData/nearbyContacts-exksujydirjwvhgojuiezuxfklbc/Build/Products/Debug/nearbyContacts
Undefined symbols for architecture arm64:
"_main", referenced from:
implicit entry/start for main executable
ld: symbol(s) not found for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
I can check later, but did you name the file main.swift?
I did.
Strange. Here’s how it should look when it works. The code here already contains the changes I mentioned, but again, the lookup seems to fail for the argument.
(This isn’t actually a video. GitHub doesn’t seem to allow .zip files here, so just download it and rename it to .zip 🙃)
NearbyContacts.mp4
Thanks. Now I see my mistake. I did not understand that main.swift needed to be in "Sources/…"
Ah, right. I forgot to mention that. Sorry about that.
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?
Ah, and a tip: You can do create a new folder, do a
swift package initin Terminal and then put your script in there and name itmain.swift. Then, change thePackage.swiftto the following:This will then allow you to run this script directly in Xcode so you can use breakpoints and the debugger, which might help a lot when debugging this.