Swiftpack.co - mapbox/turf-swift as Swift Package

Swiftpack.co is a collection of thousands of indexed Swift packages. Search packages.
mapbox/turf-swift v2.0.0
A Swift language port of Turf.js.
⭐️ 144
🕓 2 weeks ago
iOS macOS watchOS tvOS
.package(url: "https://github.com/mapbox/turf-swift.git", from: "v2.0.0")

Turf for Swift

📱iOS     🖥💻macOS     📺tvOS     ⌚️watchOS     Linux     Documentation     Carthage compatible     CocoaPods     SPM compatible    

A spatial analysis library written in Swift for native iOS, macOS, tvOS, watchOS, and Linux applications, ported from Turf.js.


Turf requires Xcode 12.0 or above and supports the following minimum deployment targets:

  • iOS 10.0 and above
  • macOS 10.12 (Sierra) and above
  • tvOS 10.0 and above
  • watchOS 3.0 and above

Alternatively, you can incorporate Turf into a command line tool without Xcode on any platform that Swift supports, including Linux.

If your project is written in Objective-C, you’ll need to write a compatibility layer between turf-swift and your Objective-C code. If your project is written in Objective-C++, you may be able to use spatial-algorithms as an alternative to Turf.


Although a stable release of this library is not yet available, prereleases are available for installation using any of the popular Swift dependency managers.


To install Turf using CocoaPods:

  1. Specify the following dependency in your Podfile:
    pod 'Turf', '~> 2.0'
  2. Run pod repo update if you haven’t lately.
  3. Run pod install and open the resulting Xcode workspace.
  4. Add import Turf to any Swift file in your application target.


To install Turf using Carthage:

  1. Add the following dependency to your Cartfile:
    github "mapbox/turf-swift" ~> 2.0
  2. Run carthage bootstrap.
  3. Follow the rest of Carthage’s integration instructions. Your application target’s Embedded Frameworks should include Turf.framework.
  4. Add import Turf to any Swift file in your application target.

Swift Package Manager

To install Turf using the Swift Package Manager, add the following package to the dependencies in your Package.swift file:

.package(url: "https://github.com/mapbox/turf-swift.git", from: "2.0.0")

Then import Turf in any Swift file in your module.

Available functionality

This work-in-progress port of Turf.js contains the following functionality:

Turf.js Turf for Swift
turf-along#along LineString.coordinateFromStart(distance:)
turf-area#area Polygon.area
turf-bearing#bearing CLLocationCoordinate2D.direction(to:)
LocationCoordinate2D.direction(to:) on Linux
turf-bezier-spline#bezierSpline LineString.bezier(resolution:sharpness:)
turf-boolean-point-in-polygon#booleanPointInPolygon Polygon.contains(_:ignoreBoundary:)
turf-center#center Polygon.center
turf-center-of-mass#centerOfMass Polygon.centerOfMass
turf-centroid#centroid Polygon.centroid
turf-circle#circle Polygon(center:radius:vertices:)
turf-destination#destination CLLocationCoordinate2D.coordinate(at:facing:)
LocationCoordinate2D.coordinate(at:facing:) on Linux
turf-distance#distance CLLocationCoordinate2D.distance(to:)
LocationCoordinate2D.distance(to:) on Linux
turf-helpers#polygon Polygon(_:)
turf-helpers#lineString LineString(_:)
turf-helpers#degreesToRadians CLLocationDegrees.toRadians()
LocationDegrees.toRadians() on Linux
turf-helpers#radiansToDegrees CLLocationDegrees.toDegrees()
LocationDegrees.toDegrees() on Linux
turf-length#length LineString.distance(from:to:)
turf-line-intersect#lineIntersect intersection(_:_:)
turf-line-slice#lineSlice LineString.sliced(from:to:)
turf-line-slice-along#lineSliceAlong LineString.trimmed(from:distance:)
turf-midpoint#midpoint mid(_:_:)
turf-nearest-point-on-line#nearestPointOnLine LineString.closestCoordinate(to:)
turf-polygon-to-line#polygonToLine LineString(_:)
turf-simplify#simplify LineString.simplify(tolerance:highestQuality:)
turf-polygon-smooth#polygonSmooth Polygon.smooth(iterations:)
LocationDirection.difference(from:) on Linux
LocationDirection.wrap(min:max:) on Linux


turf-swift also contains a GeoJSON encoder/decoder with support for Codable.

// Decode an unknown GeoJSON object.
let geojson = try JSONDecoder().decode(GeoJSONObject.self, from: data)
guard case let .feature(feature) = geojson,
      case let .point(point) = feature.geometry else {

// Decode a known GeoJSON object.
let featureCollection = try JSONDecoder().decode(FeatureCollection.self, from: data)

// Initialize a Point feature and encode it as GeoJSON.
let coordinate = CLLocationCoordinate2D(latitude: 0, longitude: 1)
let point = Point(coordinate)
let pointFeature = Feature(geometry: .point(point))
let data = try JSONEncoder().encode(pointFeature)
let json = String(data: data, encoding: .utf8)

  "type": "Feature",
  "geometry": {
    "type": "Point",
    "coordinates": [


Stars: 144
Last commit: 6 days ago

Ad: Job Offers

iOS Software Engineer @ Perry Street Software
Perry Street Software is Jack’d and SCRUFF. We are two of the world’s largest gay, bi, trans and queer social dating apps on iOS and Android. Our brands reach more than 20 million members worldwide so members can connect, meet and express themselves on a platform that prioritizes privacy and security. We invest heavily into SwiftUI and using Swift Packages to modularize the codebase.

Submit a free job ad (while I'm testing this). The analytics numbers for this website are here.

Release Notes

3 weeks ago

Changes since v2.0.0-rc.1:


  • ⚠️ Turf requires Xcode 12.0 or above to build from source. (#152)


  • ⚠️ Replaced the GeoJSON class and GeoJSON protocol with a unified GeoJSONObject enumeration. Use JSONDecoder instead of the GeoJSON.parse(_:) or GeoJSON.parse<T: GeoJSONObject>(_:from:) method. (#154)

    if let feature = try GeoJSON.parse(data)?.decodedFeature,
       case let .lineString(lineString) = feature.geometry { … }
    if case let .feature(feature) = try JSONDecoder().decode(GeoJSONObject.self, from: data),
       case let .lineString(lineString) = feature.geometry { … }
  • ⚠️ Removed the FeatureCollection.identifier and FeatureCollection.properties properties with no replacement. These properties had been represented in GeoJSON by foreign members, which are not yet implemented. If you had been relying on the identifier or properties foreign members of FeatureCollection objects, move the data to each individual feature in the collection. (#154)

    let uuid = UUID().description
    featureCollection.identifier = .string(uuid)
    let uuid = UUID().description
    for feature in featureCollection.features {
        $0.identifier = .string(uuid)
  • ⚠️ The Feature.properties property is now a JSONObject? (in other words, [String: JSONValue?]?). JSONObject is type-checked at compile time instead of runtime, but you can initialize it using a literal or full-width conversion from Any?. Code that builds a JSON object using literals will have to be modified to either specify a JSONValue case for each value or call the JSONObject(rawValue:) initializer. (#154)

    feature.properties = [
        "name": "Wapakoneta",
        "population": 9_957,
        "favorite": isFavorite,
    let isBigCity = (feature.properties?["population"] as? Double).flatMap { $0 > 10_000 }
    feature.properties = [
        "name": "Wapakoneta",
        "population": 9_957,
        "favorite": .boolean(isFavorite),
    var isBigCity: Bool?
    if case let .number(population) = feature.properties?["population"] {
        isBigCity = population > 10_000
  • ⚠️ The Feature.geometry property is now optional. (#154)

  • ⚠️ Removed the Geometry.type property. Use pattern matching (case let) instead. (#154)

    if geometry.type == .Point { … }
    if case .point = geometry { … }
  • ⚠️ Removed the Geometry.value property. This type erasure is unnecessary and can potentially become a source of bugs. Use pattern matching instead. (#154)

    if let point = geometry.value as? Point { … }
    if case let .point(point) = geometry { … }
  • ⚠️ Removed the Number enumeration in favor of a Double-typed FeatureIdentifier.number(_:) case. JSON doesn’t distinguish between integers and double-precision floating point numbers. Any distinction in the type system or encoded JSON is purely cosmetic. (#154)

    let randomNumber = Int.random(in: 0...255)
    feature.identifier = .number(.int(randomNumber))
    if let number = feature.identifier?.value as? Int {
        print("You rolled a \(number)!")
    let randomNumber = Int.random(in: 0...255)
    feature.identifier = .number(Double(randomNumber))
    if let .number(number) = feature.identifier {
        print("You rolled a \(Int(number))!")
  • Feature and FeatureCollection now conform to the Equatable protocol. (#154)

  • Each geometric type, such as Point, now conforms to the Codable and Equatable protocols. (#154)

  • BoundingBox now conforms to the Hashable protocol. (#154)


  • Fixed an issue where the LineString.simplify(tolerance:highestQuality:) method returned a highest-quality result even if the highestQuality parameter was set to false. (#152)
  • Fixed an issue where the Polygon.simplify(tolerance:highestQuality:) method incorrectly applied the tolerance. (#152)
  • Fixed an issue where the Polygon.simplify(tolerance:highestQuality:) method failed to simplify the polygon at all if any of the linear rings was a triangle. (#152)

Swiftpack is being maintained by Petr Pavlik | @ptrpavlik | @swiftpackco | API | Analytics