CoreLocation Framework for PyObjC

12.1 · active · verified Tue Apr 14

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

Install

Imports

Quickstart

This quickstart demonstrates how to perform forward geocoding using `CLGeocoder` from the `CoreLocation` framework. It shows the typical PyObjC pattern of initializing Objective-C objects, setting up a Python delegate to handle asynchronous callbacks, and running a `NSRunLoop` to process events until the operation completes. Note that CoreLocation APIs require network access for geocoding and user permission prompts on macOS.

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}")

view raw JSON →