CoreLocation Framework for PyObjC
pyobjc-framework-corelocation provides Python wrappers for Apple's CoreLocation framework on macOS. It is part of the PyObjC project, a bidirectional bridge enabling Python scripts to interact with Objective-C libraries, including macOS Cocoa frameworks. This framework offers interfaces for obtaining a machine's physical location, allowing for geo-aware applications. The current version is 12.1 and it maintains an active release cadence, typically aligning with macOS SDK updates and Python version support.
Warnings
- breaking PyObjC frequently drops support for older Python versions. PyObjC 12.0 dropped support for Python 3.9, and PyObjC 11.0 dropped Python 3.8. Ensure your Python version is compatible with the PyObjC version you are installing.
- breaking PyObjC 11.1 changed how initializer methods (`init` family) are modeled, now correctly reflecting that they 'steal' a reference to `self` and return a new one, as per clang's ARC documentation. This affects object lifecycle and reference counting.
- gotcha PyObjC is a macOS-specific library and will not install or run on other operating systems. The frameworks it wraps (like CoreLocation) are Apple-proprietary and only available on macOS.
- gotcha Unlike Objective-C, where sending a message to `nil` (equivalent to Python `None`) is a no-op, attempting to call a method on a Python `None` object (which PyObjC translates from `nil`) will raise an `AttributeError`.
- gotcha CoreLocation services require specific user permissions and proper binary signing on macOS. Unsigned or ad-hoc signed applications may not be able to access location data, and users will be prompted for permission upon first access.
- gotcha CoreLocation delegate methods are invoked on the `NSRunLoop` of the thread where the `CLLocationManager` object was initialized. If you are not in a GUI application's main thread, you must explicitly run a `NSRunLoop` for delegate callbacks to be processed.
Install
-
pip install pyobjc-framework-corelocation
Imports
- CoreLocation
import CoreLocation
Quickstart
import objc
from CoreLocation import CLGeocoder
from Foundation import NSRunLoop, NSDate, NSObject
import time
class GeocoderDelegate(NSObject):
# This is a delegate class to handle geocoding results asynchronously.
# In a real application, you might use a more robust threading/event loop setup.
def init(self):
self = super(GeocoderDelegate, self).init()
if not self: return None
self.results = {"placemarks": [], "error": None}
self.completed = False
return self
def geocoder_didGeocodePlacemarks_error_(self, geocoder, placemarks, error):
if error:
self.results["error"] = error.localizedDescription()
elif placemarks:
self.results["placemarks"] = placemarks
self.completed = True
def forward_geocode(address: str) -> list[dict]:
with objc.autorelease_pool():
geocoder = CLGeocoder.alloc().init()
delegate = GeocoderDelegate.alloc().init()
# Call the Objective-C method with a Python delegate
geocoder.geocodeAddressString_completionHandler_(address,
lambda placemarks, error: delegate.geocoder_didGeocodePlacemarks_error_(geocoder, placemarks, error))
# Keep the runloop active until the geocoding is complete
# This is a blocking loop for demonstration; for GUI apps, the main runloop handles this.
timeout = time.time() + 10 # 10-second timeout
while not delegate.completed and time.time() < timeout:
NSRunLoop.currentRunLoop().runMode_beforeDate_(
"NSDefaultRunLoopMode", NSDate.dateWithTimeIntervalSinceNow_(0.1)
)
if not delegate.completed:
raise TimeoutError("Geocoding request timed out.")
if delegate.results["error"]:
raise Exception(f"Geocoding error: {delegate.results['error']}")
formatted_results = []
for pm in delegate.results["placemarks"]:
loc = pm.location()
if loc:
coord = loc.coordinate()
formatted_results.append({
"latitude": coord.latitude,
"longitude": coord.longitude,
"name": pm.name(),
"locality": pm.locality(),
"country": pm.country()
})
return formatted_results
if __name__ == "__main__":
try:
locations = forward_geocode("1 Infinite Loop, Cupertino, CA")
if locations:
print("\nLocation found:")
for loc in locations:
for key, value in loc.items():
if value:
print(f" {key}: {value}")
else:
print("No locations found.")
except Exception as e:
print(f"Error: {e}")