├── .github ├── FUNDING.yml └── workflows │ └── swift.yml ├── .gitignore ├── .swiftpm └── xcode │ └── package.xcworkspace │ └── contents.xcworkspacedata ├── .travis.yml ├── Classes ├── Converters.swift ├── DateTimeParsers.swift ├── GPXAuthor.swift ├── GPXBounds.swift ├── GPXCompression.swift ├── GPXCopyright.swift ├── GPXElement.swift ├── GPXEmail.swift ├── GPXError.swift ├── GPXExtensions.swift ├── GPXExtensionsElement.swift ├── GPXFix.swift ├── GPXLegacy.swift ├── GPXLink.swift ├── GPXMetadata.swift ├── GPXParser.swift ├── GPXPerson.swift ├── GPXPoint.swift ├── GPXPointSegment.swift ├── GPXRawElement.swift ├── GPXRoot.swift ├── GPXRoute.swift ├── GPXRoutePoint.swift ├── GPXRouteProtocol.swift ├── GPXTrack.swift ├── GPXTrackPoint.swift ├── GPXTrackSegment.swift ├── GPXWaypoint.swift ├── GPXWaypointProtocol.swift └── NSMutableString+XML.swift ├── CoreGPX title.png ├── CoreGPX.podspec ├── Example ├── CoreGPX.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ ├── xcbaselines │ │ └── 607FACE41AFB9204008FA782.xcbaseline │ │ │ ├── 8290985D-7441-4CE0-9256-ED960E0DA457.plist │ │ │ └── Info.plist │ │ └── xcschemes │ │ ├── CoreGPX Tests.xcscheme │ │ └── CoreGPX-Example.xcscheme ├── CoreGPX.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings ├── GPXKit │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── LaunchScreen.xib │ │ └── Main.storyboard │ ├── CoreGPX Playground.playground │ │ ├── Contents.swift │ │ ├── contents.xcplayground │ │ └── timeline.xctimeline │ ├── CreationViewController.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Info.plist │ ├── ParseViewController.swift │ └── ViewController.swift ├── Podfile ├── Podfile.lock ├── Pods │ ├── Alternate Targets │ │ ├── CoreGPX-macOS │ │ │ ├── CoreGPX_macOS.h │ │ │ └── Info.plist │ │ ├── CoreGPX-macOSTests │ │ │ ├── CoreGPX_macOSTests.swift │ │ │ └── Info.plist │ │ └── CoreGPX-watchOS │ │ │ ├── CoreGPX_watchOS.h │ │ │ └── Info.plist │ ├── Local Podspecs │ │ ├── CoreGPX.podspec.json │ │ └── GPXKit.podspec.json │ ├── Manifest.lock │ ├── Pods.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── CoreGPX-macOS.xcscheme │ │ │ ├── CoreGPX-watchOS.xcscheme │ │ │ └── CoreGPX.xcscheme │ └── Target Support Files │ │ ├── CoreGPX │ │ ├── CoreGPX-dummy.m │ │ ├── CoreGPX-prefix.pch │ │ ├── CoreGPX-umbrella.h │ │ ├── CoreGPX.modulemap │ │ ├── CoreGPX.xcconfig │ │ └── Info.plist │ │ ├── Pods-CoreGPX_Example │ │ ├── Info.plist │ │ ├── Pods-CoreGPX_Example-acknowledgements.markdown │ │ ├── Pods-CoreGPX_Example-acknowledgements.plist │ │ ├── Pods-CoreGPX_Example-dummy.m │ │ ├── Pods-CoreGPX_Example-frameworks.sh │ │ ├── Pods-CoreGPX_Example-resources.sh │ │ ├── Pods-CoreGPX_Example-umbrella.h │ │ ├── Pods-CoreGPX_Example.debug.xcconfig │ │ ├── Pods-CoreGPX_Example.modulemap │ │ └── Pods-CoreGPX_Example.release.xcconfig │ │ └── Pods-CoreGPX_Tests │ │ ├── Info.plist │ │ ├── Pods-CoreGPX_Tests-acknowledgements.markdown │ │ ├── Pods-CoreGPX_Tests-acknowledgements.plist │ │ ├── Pods-CoreGPX_Tests-dummy.m │ │ ├── Pods-CoreGPX_Tests-frameworks.sh │ │ ├── Pods-CoreGPX_Tests-resources.sh │ │ ├── Pods-CoreGPX_Tests-umbrella.h │ │ ├── Pods-CoreGPX_Tests.debug.xcconfig │ │ ├── Pods-CoreGPX_Tests.modulemap │ │ └── Pods-CoreGPX_Tests.release.xcconfig └── Tests │ ├── GPXTest-DualLinks.gpx │ ├── GPXTest-TrackPointOnly.gpx │ ├── Info.plist │ ├── Tests.swift │ └── wptError.gpx ├── Extras └── GPX+CLLocation.swift ├── GPXKit ├── Assets │ └── .gitkeep └── Classes │ └── .gitkeep ├── LICENSE ├── Package.swift ├── Package@swift-5.2.swift ├── README.md ├── _Pods.xcodeproj └── docs ├── Classes.html ├── Classes ├── GPXAuthor.html ├── GPXBounds.html ├── GPXCompression.html ├── GPXCompression │ ├── lossyOptions.html │ └── lossyTypes.html ├── GPXCopyright.html ├── GPXElement.html ├── GPXEmail.html ├── GPXExtensions.html ├── GPXExtensionsElement.html ├── GPXLegacyRoot.html ├── GPXLegacyRoute.html ├── GPXLegacyTrack.html ├── GPXLegacyTrackPoint.html ├── GPXLegacyTrackSegment.html ├── GPXLegacyWaypoint.html ├── GPXLink.html ├── GPXMetadata.html ├── GPXParser.html ├── GPXPerson.html ├── GPXPoint.html ├── GPXPointSegment.html ├── GPXRoot.html ├── GPXRoute.html ├── GPXRoutePoint.html ├── GPXTrack.html ├── GPXTrackPoint.html ├── GPXTrackSegment.html └── GPXWaypoint.html ├── Enums.html ├── Enums ├── GPXFix.html └── GPXVersion.html ├── Protocols.html ├── Protocols ├── GPXRootElement.html ├── GPXRouteType.html └── GPXWaypointProtocol.html ├── Structs.html ├── Structs ├── GPXError.html └── GPXError │ ├── coordinates.html │ ├── coordinates │ └── reason.html │ └── parser.html ├── badge.svg ├── css ├── highlight.css └── jazzy.css ├── docsets ├── CoreGPX.docset │ └── Contents │ │ ├── Info.plist │ │ └── Resources │ │ ├── Documents │ │ ├── Classes.html │ │ ├── Classes │ │ │ ├── GPXAuthor.html │ │ │ ├── GPXBounds.html │ │ │ ├── GPXCompression.html │ │ │ ├── GPXCompression │ │ │ │ ├── lossyOptions.html │ │ │ │ └── lossyTypes.html │ │ │ ├── GPXCopyright.html │ │ │ ├── GPXElement.html │ │ │ ├── GPXEmail.html │ │ │ ├── GPXExtensions.html │ │ │ ├── GPXExtensionsElement.html │ │ │ ├── GPXLegacyRoot.html │ │ │ ├── GPXLegacyRoute.html │ │ │ ├── GPXLegacyTrack.html │ │ │ ├── GPXLegacyTrackPoint.html │ │ │ ├── GPXLegacyTrackSegment.html │ │ │ ├── GPXLegacyWaypoint.html │ │ │ ├── GPXLink.html │ │ │ ├── GPXMetadata.html │ │ │ ├── GPXParser.html │ │ │ ├── GPXPerson.html │ │ │ ├── GPXPoint.html │ │ │ ├── GPXPointSegment.html │ │ │ ├── GPXRoot.html │ │ │ ├── GPXRoute.html │ │ │ ├── GPXRoutePoint.html │ │ │ ├── GPXTrack.html │ │ │ ├── GPXTrackPoint.html │ │ │ ├── GPXTrackSegment.html │ │ │ └── GPXWaypoint.html │ │ ├── Enums.html │ │ ├── Enums │ │ │ ├── GPXFix.html │ │ │ └── GPXVersion.html │ │ ├── Protocols.html │ │ ├── Protocols │ │ │ ├── GPXRootElement.html │ │ │ ├── GPXRouteType.html │ │ │ └── GPXWaypointProtocol.html │ │ ├── Structs.html │ │ ├── Structs │ │ │ ├── GPXError.html │ │ │ └── GPXError │ │ │ │ ├── coordinates.html │ │ │ │ ├── coordinates │ │ │ │ └── reason.html │ │ │ │ └── parser.html │ │ ├── css │ │ │ ├── highlight.css │ │ │ └── jazzy.css │ │ ├── img │ │ │ ├── carat.png │ │ │ ├── dash.png │ │ │ └── gh.png │ │ ├── index.html │ │ ├── js │ │ │ ├── jazzy.js │ │ │ └── jquery.min.js │ │ └── search.json │ │ └── docSet.dsidx └── CoreGPX.tgz ├── img ├── carat.png ├── dash.png └── gh.png ├── index.html ├── js ├── jazzy.js └── jquery.min.js ├── search.json └── undocumented.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [vincentneo] 4 | -------------------------------------------------------------------------------- /.github/workflows/swift.yml: -------------------------------------------------------------------------------- 1 | name: CoreGPX CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - '*' 10 | env: 11 | DEVELOPER_DIR: /Applications/Xcode_13.2.1.app/Contents/Developer 12 | jobs: 13 | iOS: 14 | name: iOS Build 15 | runs-on: macos-12 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Build CoreGPX iOS 19 | run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace "Example/CoreGPX.xcworkspace" -scheme "CoreGPX" -destination "OS=15.2,name=iPhone 12 Pro" clean build | xcpretty 20 | 21 | macOS: 22 | name: macOS Build 23 | runs-on: macos-12 24 | strategy: 25 | matrix: 26 | config: 27 | - { name: "macOS", scheme: "CoreGPX-macOS" } 28 | - { name: "Mac Catalyst", scheme: "CoreGPX" } 29 | steps: 30 | - uses: actions/checkout@v2 31 | - name: ${{ matrix.config['name'] }} 32 | run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace "Example/CoreGPX.xcworkspace" -scheme "${{ matrix.config['scheme'] }}" -destination "platform=macOS" clean build | xcpretty 33 | 34 | watchOS: 35 | name: watchOS Build 36 | runs-on: macos-12 37 | steps: 38 | - uses: actions/checkout@v2 39 | - name: Build CoreGPX watchOS 40 | run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace "Example/CoreGPX.xcworkspace" -scheme "CoreGPX-watchOS" -destination "OS=9.1,name=Apple Watch Series 7 (45mm)" clean build | xcpretty 41 | 42 | example: 43 | name: Build Example & Test 44 | runs-on: macos-12 45 | steps: 46 | - uses: actions/checkout@v2 47 | - name: Build Example App 48 | run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace "Example/CoreGPX.xcworkspace" -scheme "CoreGPX-Example" -destination "OS=15.2,name=iPhone 12 Pro" clean build | xcpretty 49 | - name: Run test cases 50 | run: set -o pipefail && env NSUnbufferedIO=YES xcodebuild -workspace "Example/CoreGPX.xcworkspace" -scheme "CoreGPX Tests" -destination "OS=15.2,name=iPhone 12 Pro" test | xcpretty 51 | 52 | linux: 53 | name: Linux Build 54 | runs-on: ubuntu-latest 55 | container: 56 | image: swift:latest 57 | steps: 58 | - uses: actions/checkout@v2 59 | - name: Build 60 | run: | 61 | cd Classes 62 | swift build 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | 22 | # Bundler 23 | .bundle 24 | 25 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 26 | # Carthage/Checkouts 27 | 28 | Carthage/Build 29 | 30 | # We recommend against adding the Pods directory to your .gitignore. However 31 | # you should judge for yourself, the pros and cons are mentioned at: 32 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 33 | # 34 | # Note: if you ignore the Pods directory, make sure to uncomment 35 | # `pod install` in .travis.yml 36 | # 37 | # Pods/ 38 | 39 | # Swift Package Manager 40 | .swiftpm/ 41 | -------------------------------------------------------------------------------- /.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * https://www.objc.io/issues/6-build-tools/travis-ci/ 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | os: osx 6 | osx_image: xcode11.3 7 | language: swift 8 | env: 9 | global: 10 | - LC_CTYPE=en_US.UTF-8 11 | - LANG=en_US.UTF-8 12 | - WORKSPACE=Example/CoreGPX.xcworkspace 13 | - IOS_FRAMEWORK_SCHEME="CoreGPX" 14 | - MACOS_FRAMEWORK_SCHEME="CoreGPX-macOS" 15 | - WATCHOS_FRAMEWORK_SCHEME="CoreGPX-watchOS" 16 | - EXAMPLE_SCHEME="CoreGPX-Example" 17 | - TEST_SCHEME="CoreGPX Tests" 18 | matrix: 19 | - DESTINATION="OS=6.1.1,name=Apple Watch Series 5 - 44mm" SCHEME="$WATCHOS_FRAMEWORK_SCHEME" RUN_TESTS="NO" BUILD_EXAMPLE="NO" SDK="watchsimulator6.1" 20 | 21 | - DESTINATION="OS=13.3,name=iPhone 11" SCHEME="$IOS_FRAMEWORK_SCHEME" RUN_TESTS="NO" BUILD_EXAMPLE="YES" SDK="iphonesimulator13.2" 22 | - DESTINATION="OS=13.3,name=iPhone 11" SCHEME="$IOS_FRAMEWORK_SCHEME" RUN_TESTS="YES" BUILD_EXAMPLE="NO" SDK="iphonesimulator13.2" 23 | - DESTINATION="OS=10.3.1,name=iPhone 6 Plus" SCHEME="$IOS_FRAMEWORK_SCHEME" RUN_TESTS="NO" BUILD_EXAMPLE="NO" SDK="iphonesimulator13.2" 24 | 25 | - DESTINATION="arch=x86_64" SCHEME="$MACOS_FRAMEWORK_SCHEME" RUN_TESTS="NO" BUILD_EXAMPLE="NO" SDK="macosx10.15" 26 | 27 | script: 28 | - set -o pipefail 29 | - xcodebuild -version 30 | - xcodebuild -showsdks 31 | 32 | - xcodebuild -workspace "$WORKSPACE" -list 33 | 34 | - xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -destination "$DESTINATION" -sdk "$SDK" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty; 35 | 36 | - if [ $RUN_TESTS == "YES" ]; then 37 | xcodebuild -workspace "$WORKSPACE" -scheme "$TEST_SCHEME" -destination "$DESTINATION" -sdk "$SDK" -configuration Release ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty; 38 | else 39 | xcodebuild -workspace "$WORKSPACE" -scheme "$SCHEME" -destination "$DESTINATION" -sdk "$SDK" -configuration Release ONLY_ACTIVE_ARCH=NO build | xcpretty; 40 | fi 41 | 42 | - if [ $BUILD_EXAMPLE == "YES" ]; then 43 | xcodebuild -workspace "$WORKSPACE" -scheme "$EXAMPLE_SCHEME" -destination "$DESTINATION" -sdk "$SDK" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty; 44 | fi 45 | -------------------------------------------------------------------------------- /Classes/Converters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Conversions.swift 3 | // CoreGPX 4 | // 5 | // Created by Vincent on 21/3/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Provides conversions, when required. 11 | /// 12 | /// Meant for internal conversions use only. 13 | internal final class Convert { 14 | 15 | // MARK:- Number conversion 16 | 17 | /// For conversion from optional `String` type to optional `Int` type 18 | /// 19 | /// - Parameters: 20 | /// - string: input string that should be a number. 21 | /// - Returns: 22 | /// A `Int` that will be nil if input `String` is nil. 23 | /// 24 | static func toInt(from string: String?) -> Int? { 25 | guard let NonNilString = string else { 26 | return nil 27 | } 28 | return Int(NonNilString) 29 | } 30 | 31 | /// For conversion from optional `String` type to optional `Double` type 32 | /// 33 | /// - Parameters: 34 | /// - string: input string that should be a number. 35 | /// - Returns: 36 | /// A `Double` that will be nil if input `String` is nil. 37 | /// 38 | static func toDouble(from string: String?) -> Double? { 39 | guard let NonNilString = string else { 40 | return nil 41 | } 42 | return Double(NonNilString) 43 | } 44 | 45 | 46 | 47 | // MARK:- Date & Time Formatting 48 | 49 | /// Immutable date formatter (UTC Time) for consistency within CoreGPX. 50 | private static let dateFormatter: DateFormatter = { 51 | let formatter = DateFormatter() 52 | 53 | // Timezone should be 0 as GPX uses UTC time, not local time. 54 | formatter.timeZone = TimeZone(secondsFromGMT: 0) 55 | formatter.locale = Locale(identifier: "en_US_POSIX") 56 | 57 | // dateTime(YYYY-MM-DDThh:mm:ssZ) 58 | formatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'" 59 | 60 | return formatter 61 | }() 62 | 63 | /// For conversion from optional `Date` to optional `String` 64 | /// 65 | /// - Parameters: 66 | /// - date: can be of any date and time, which will be converted to a formatted ISO8601 date and time string. 67 | /// 68 | /// - Returns: 69 | /// Formatted string according to ISO8601, which is of **"yyyy-MM-ddTHH:mm:ssZ"** 70 | /// 71 | /// This method is currently heavily used for generating of GPX files / formatted string, as the native `Date` type must be converted to a `String` first. 72 | /// 73 | static func toString(from dateTime: Date?) -> String? { 74 | 75 | guard let validDate = dateTime else { 76 | return nil 77 | } 78 | 79 | return dateFormatter.string(from: validDate) 80 | } 81 | 82 | /// Immutable year formatter (UTC Time) for consistency within CoreGPX. 83 | private static let yearFormatter: DateFormatter = { 84 | let formatter = DateFormatter() 85 | 86 | // Timezone should be 0 as GPX uses UTC time, not local time. 87 | formatter.timeZone = TimeZone(secondsFromGMT: 0) 88 | 89 | // dateTime(YYYY-MM-DDThh:mm:ssZ) 90 | formatter.dateFormat = "yyyy" 91 | 92 | return formatter 93 | }() 94 | 95 | /// For conversion from optional year `Date` to optional `String` 96 | /// 97 | /// - Parameters: 98 | /// - date: can be of any year, which will be converted to a string representing the year. 99 | /// 100 | /// - Returns: 101 | /// Formatted string as per: "yyyy", a year. 102 | /// 103 | /// This method is used for generating of GPX files / formatted string if `GPXCopyright` is used. Represents the year of copyright. 104 | /// 105 | static func toString(fromYear year: Date?) -> String? { 106 | 107 | guard let validYear = year else { 108 | return nil 109 | } 110 | 111 | return yearFormatter.string(from: validYear) 112 | } 113 | 114 | } 115 | 116 | -------------------------------------------------------------------------------- /Classes/DateTimeParsers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXDateTime.swift 3 | // CoreGPX 4 | // 5 | // Created on 23/3/19. 6 | // 7 | // Original code from: http://jordansmith.io/performant-date-parsing/ 8 | // Modified to better suit CoreGPX's functionalities. 9 | // 10 | 11 | import Foundation 12 | 13 | 14 | /** 15 | Date Parser for use when parsing GPX files, containing elements with date attributions. 16 | 17 | It can parse ISO8601 formatted date strings, along with year strings to native `Date` types. 18 | 19 | Formerly Named: `ISO8601DateParser` & `CopyrightYearParser` 20 | */ 21 | final class GPXDateParser { 22 | 23 | // MARK:- Supporting Variables 24 | 25 | #if !os(Linux) 26 | /// Caching Calendar such that it can be used repeatedly without reinitializing it. 27 | private static var calendarCache = [Int : Calendar]() 28 | /// Components of Date stored together 29 | private var components = DateComponents() 30 | #endif // !os(Linux) 31 | 32 | // MARK:- Individual Date Components 33 | 34 | private let year = UnsafeMutablePointer.allocate(capacity: 1) 35 | private let month = UnsafeMutablePointer.allocate(capacity: 1) 36 | private let day = UnsafeMutablePointer.allocate(capacity: 1) 37 | private let hour = UnsafeMutablePointer.allocate(capacity: 1) 38 | private let minute = UnsafeMutablePointer.allocate(capacity: 1) 39 | private let second = UnsafeMutablePointer.allocate(capacity: 1) 40 | 41 | deinit { 42 | year.deallocate() 43 | month.deallocate() 44 | day.deallocate() 45 | hour.deallocate() 46 | minute.deallocate() 47 | second.deallocate() 48 | } 49 | 50 | // MARK:- String To Date Parsers 51 | 52 | /// Parses an ISO8601 formatted date string as native Date type. 53 | func parse(date string: String?) -> Date? { 54 | guard let NonNilString = string else { 55 | return nil 56 | } 57 | 58 | #if os(Linux) 59 | return ISO8601DateFormatter().date(from: NonNilString) 60 | #else // os(Linux) 61 | _ = withVaList([year, month, day, hour, minute, 62 | second], { pointer in 63 | vsscanf(NonNilString, "%d-%d-%dT%d:%d:%dZ", pointer) 64 | 65 | }) 66 | 67 | 68 | components.year = year.pointee 69 | components.minute = minute.pointee 70 | components.day = day.pointee 71 | components.hour = hour.pointee 72 | components.month = month.pointee 73 | components.second = second.pointee 74 | 75 | if let calendar = Self.calendarCache[0] { 76 | return calendar.date(from: components) 77 | } 78 | 79 | var calendar = Calendar(identifier: .gregorian) 80 | calendar.timeZone = TimeZone(secondsFromGMT: 0)! 81 | Self.calendarCache[0] = calendar 82 | return calendar.date(from: components) 83 | #endif 84 | } 85 | 86 | /// Parses a year string as native Date type. 87 | func parse(year string: String?) -> Date? { 88 | guard let NonNilString = string else { 89 | return nil 90 | } 91 | 92 | _ = withVaList([year], { pointer in 93 | vsscanf(NonNilString, "%d", pointer) 94 | 95 | }) 96 | 97 | #if os(Linux) 98 | return DateComponents(year: year.pointee).date 99 | #else // os(Linux) 100 | components.year = year.pointee 101 | 102 | if let calendar = Self.calendarCache[1] { 103 | return calendar.date(from: components) 104 | } 105 | 106 | var calendar = Calendar(identifier: .gregorian) 107 | calendar.timeZone = TimeZone(secondsFromGMT: 0)! 108 | Self.calendarCache[1] = calendar 109 | return calendar.date(from: components) 110 | #endif 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /Classes/GPXAuthor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXAuthor.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 22/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Author type, holds information of the creator of the GPX file. A subclass of `GPXPerson`. 11 | public final class GPXAuthor: GPXPerson { 12 | 13 | // MARK:- Initializers 14 | 15 | public convenience init(name: String? = nil, email: GPXEmail? = nil, link: GPXLink? = nil) { 16 | self.init() 17 | self.name = name 18 | self.email = email 19 | self.link = link 20 | } 21 | 22 | /// Default Initializer 23 | public required init() { 24 | super.init() 25 | } 26 | 27 | /// Inits native element from raw parser value 28 | override init(raw: GPXRawElement) { 29 | super.init(raw: raw) 30 | } 31 | 32 | /// Decoder handling (from superclass) 33 | required public init(from decoder: Decoder) throws { 34 | try super.init(from: decoder) 35 | } 36 | 37 | // MARK:- Tag 38 | 39 | override func tagName() -> String { 40 | return "author" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Classes/GPXBounds.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXBounds.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 22/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A value type that represents bounds based off GPX v1.1 schema's `boundsType`. 12 | 13 | This is meant for having two pairs of longitude and latitude, signifying the maximum and minimum, defining the extent / boundaries of a particular element. 14 | */ 15 | public final class GPXBounds: GPXElement, Codable { 16 | 17 | /// Codable Implementation 18 | private enum CodingKeys: String, CodingKey { 19 | case minLatitude = "minlat" 20 | case maxLatitude = "maxlat" 21 | case minLongitude = "minlon" 22 | case maxLongitude = "maxlon" 23 | } 24 | 25 | /// Minimum latitude of boundaries to a element. 26 | public var minLatitude: Double? 27 | /// Maximum latitude of boundaries to a element. 28 | public var maxLatitude: Double? 29 | /// Minimum longitude of boundaries to a element. 30 | public var minLongitude: Double? 31 | /// Maximum longitude of boundaries to a element. 32 | public var maxLongitude: Double? 33 | 34 | // MARK:- Initalizers 35 | 36 | /// Default initializer. 37 | public required init() { 38 | super.init() 39 | } 40 | 41 | /// Initializes with all values 42 | /// 43 | /// - Parameters: 44 | /// - minLatitude: Minimum latitude 45 | /// - maxLatitude: Maximum latitude 46 | /// - minLongitude: Minimum longitude 47 | /// - maxLongitude: Maximum longitude 48 | public init(minLatitude: Double, maxLatitude: Double, minLongitude: Double, maxLongitude: Double) { 49 | super.init() 50 | self.minLatitude = minLatitude 51 | self.maxLatitude = maxLatitude 52 | self.minLongitude = minLongitude 53 | self.maxLongitude = maxLongitude 54 | } 55 | 56 | /// Inits native element from raw parser value 57 | /// 58 | /// - Parameters: 59 | /// - raw: Raw element expected from parser 60 | init(raw: GPXRawElement) { 61 | self.minLatitude = Convert.toDouble(from: raw.attributes["minlat"]) 62 | self.maxLatitude = Convert.toDouble(from: raw.attributes["maxlat"]) 63 | self.minLongitude = Convert.toDouble(from: raw.attributes["minlon"]) 64 | self.maxLongitude = Convert.toDouble(from: raw.attributes["maxlon"]) 65 | } 66 | 67 | // MARK:- Tag 68 | 69 | override func tagName() -> String { 70 | return "bounds" 71 | } 72 | 73 | // MARK:- GPX 74 | 75 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 76 | let attribute = NSMutableString(string: "") 77 | 78 | if let minLatitude = minLatitude { 79 | attribute.append(" minlat=\"\(minLatitude)\"") 80 | } 81 | if let minLongitude = minLongitude { 82 | attribute.append(" minlon=\"\(minLongitude)\"") 83 | } 84 | if let maxLatitude = maxLatitude { 85 | attribute.append(" maxlat=\"\(maxLatitude)\"") 86 | } 87 | if let maxLongitude = maxLongitude { 88 | attribute.append(" maxlon=\"\(maxLongitude)\"") 89 | } 90 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /Classes/GPXCopyright.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXCopyright.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 22/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A value type for representing copyright info 12 | 13 | Copyight information also includes additional attributes. 14 | 15 | **Supported attributes:** 16 | - Year of first publication 17 | - License of the file 18 | - Author / Copyright Holder's name 19 | 20 | */ 21 | public final class GPXCopyright: GPXElement, Codable { 22 | 23 | /// Year of the first publication of this copyrighted work. 24 | /// 25 | /// This should be the current year. 26 | /// 27 | /// year = Date() 28 | /// // year attribute will be current year. 29 | public var year: Date? 30 | 31 | /// License of the file. 32 | /// 33 | /// A URL linking to the license's documentation, represented with a `String` 34 | public var license: String? 35 | 36 | /// Author / copyright holder's name. 37 | /// 38 | /// Basically, the person who has created this GPX file, which is also the copyright holder, shall them wish to have it as a copyrighted work. 39 | public var author: String? 40 | 41 | // MARK:- Instance 42 | 43 | /// Default Initializer. 44 | public required init() { 45 | super.init() 46 | } 47 | 48 | /// Initializes with author 49 | /// 50 | /// At least the author name must be valid in order for a `GPXCopyright` to be valid. 51 | public init(author: String) { 52 | super.init() 53 | self.author = author 54 | self.year = Date() 55 | } 56 | 57 | /// Inits native element from raw parser value 58 | /// 59 | /// - Parameters: 60 | /// - raw: Raw element expected from parser 61 | init(raw: GPXRawElement) { 62 | for child in raw.children { 63 | switch child.name { 64 | case "year": self.year = GPXDateParser().parse(year: child.text) 65 | case "license": self.license = child.text 66 | default: continue 67 | } 68 | } 69 | self.author = raw.attributes["author"] 70 | } 71 | 72 | // MARK: Tag 73 | 74 | override func tagName() -> String { 75 | return "copyright" 76 | } 77 | 78 | // MARK: GPX XML markup 79 | 80 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 81 | let attribute = NSMutableString(string: "") 82 | 83 | if let author = author { 84 | attribute.append(" author=\"\(author)\"") 85 | } 86 | 87 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 88 | } 89 | 90 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 91 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 92 | self.addProperty(forValue: Convert.toString(fromYear: year), gpx: gpx, tagName: "year", indentationLevel: indentationLevel) 93 | self.addProperty(forValue: license, gpx: gpx, tagName: "license", indentationLevel: indentationLevel) 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /Classes/GPXEmail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXEmail.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 18/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Used for handling email types 12 | 13 | Email is seperated as two variables in order to prevent email harvesting. The GPX v1.1 schema requires that. 14 | 15 | For example, an email of **"yourname@thisisawebsite.com"**, would have an id of **'yourname'** and a domain of **'thisisawebsite.com'**. 16 | */ 17 | public final class GPXEmail: GPXElement, Codable { 18 | 19 | /// Codable Implementation 20 | private enum CodingKeys: String, CodingKey { 21 | case emailID = "id" 22 | case domain 23 | case fullAddress 24 | } 25 | 26 | /// Email ID refers to the front part of the email address, before the **@** 27 | public var emailID: String? 28 | 29 | /// Domain refers to the back part of the email address, after the **@** 30 | public var domain: String? 31 | 32 | /// Full email as a string. 33 | public var fullAddress: String? 34 | 35 | // MARK:- Instance 36 | 37 | public required init() { 38 | super.init() 39 | } 40 | 41 | /// Initialize with a full email address. 42 | /// 43 | /// Seperation to id and domain will be done by this class itself. 44 | /// 45 | /// - Parameters: 46 | /// - email: A full email address. (example: 'name@domain.com') 47 | public init(withFullEmailAddress email: String) { 48 | let splitedEmail = email.components(separatedBy: "@") 49 | self.emailID = splitedEmail[0] 50 | self.domain = splitedEmail[1] 51 | } 52 | 53 | /// Inits native element from raw parser value 54 | /// 55 | /// - Parameters: 56 | /// - raw: Raw element expected from parser 57 | init(raw: GPXRawElement) { 58 | self.emailID = raw.attributes["id"] 59 | self.domain = raw.attributes["domain"] 60 | 61 | guard let id = raw.attributes["id"] else { return } 62 | guard let domain = raw.attributes["domain"] else { return } 63 | self.fullAddress = id + "@" + domain 64 | } 65 | 66 | // MARK:- Tag 67 | override func tagName() -> String { 68 | return "email" 69 | } 70 | 71 | // MARK:- GPX 72 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 73 | let attribute = NSMutableString(string: "") 74 | 75 | if let emailID = emailID { 76 | attribute.append(" id=\"\(emailID)\"") 77 | } 78 | if let domain = domain { 79 | attribute.append(" domain=\"\(domain)\"") 80 | } 81 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /Classes/GPXError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXError.swift 3 | // Pods 4 | // 5 | // Created by Vincent on 4/9/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Throwable errors for GPX library 11 | public struct GPXError { 12 | 13 | /// Coordinates related errors 14 | public enum coordinates: Error { 15 | /// when lat is outside range (-90˚ to 90˚) 16 | case invalidLatitude(dueTo: reason) 17 | /// when lon is outside range (-180˚ to 180˚) 18 | case invalidLongitude(dueTo: reason) 19 | 20 | /// reason of why a coordinate error is thrown. 21 | public enum reason { 22 | /// < -90˚ (lat) / < -180˚ (lon) 23 | case underLimit 24 | /// > 90˚ (lat) / > 180˚ (lon) 25 | case overLimit 26 | } 27 | } 28 | 29 | /// Parser related errors 30 | public enum parser: Error { 31 | /// Thrown when GPX version is < 1.1 32 | case unsupportedVersion 33 | /// When an issue occurred at line, but without further comment. 34 | case issueAt(line: Int) 35 | /// Thrown when issue occurred at line. (Mostly wraps XML parser errors) 36 | case issueAt(line: Int, error: Error) 37 | /// Thrown when file is XML, but not GPX. 38 | case fileIsNotGPX 39 | /// Thrown when file is not XML, let alone GPX. 40 | case fileIsNotXMLBased 41 | /// Thrown when file does not conform schema. (unused) 42 | case fileDoesNotConformSchema 43 | /// Thrown when file is presumed to be empty. 44 | case fileIsEmpty 45 | /// When multiple errors occurred, to give an array of errors. 46 | case multipleErrorsOccurred(_ errors: [Error]) 47 | } 48 | 49 | /// Checks if latitude and longitude is valid (within range) 50 | static func checkError(latitude: Double, longitude: Double) -> Error? { 51 | guard latitude >= -90 && latitude <= 90 else { 52 | if latitude <= -90 { 53 | return GPXError.coordinates.invalidLatitude(dueTo: .underLimit) 54 | } 55 | else { 56 | return GPXError.coordinates.invalidLatitude(dueTo: .overLimit) 57 | } 58 | } 59 | guard longitude >= -180 && longitude <= 180 else { 60 | if longitude <= -180 { 61 | return GPXError.coordinates.invalidLongitude(dueTo: .underLimit) 62 | } 63 | else { 64 | return GPXError.coordinates.invalidLongitude(dueTo: .overLimit) 65 | } 66 | } 67 | 68 | return nil 69 | } 70 | 71 | /// Other errors 72 | enum others: Error { 73 | /// When adding an email that is considered as invalid as per GPX v1.0 schema. 74 | /// 75 | /// Checks against regular expression of: 76 | /// [\p{L}_]+(\.[\p{L}_]+)*@[\p{L}_]+(\.[\p{L}_]+)+ 77 | case invalidEmail 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Classes/GPXExtensions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXExtensions.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 18/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | For adding/obtaining data stored as extensions in GPX file. 12 | 13 | Typical GPX extended data, would have data that should be inbetween the open and close tags of **\** 14 | 15 | This class represents the extended data in a GPX file. 16 | */ 17 | public final class GPXExtensions: GPXElement, Codable { 18 | 19 | /// Extended children tags 20 | public var children = [GPXExtensionsElement]() 21 | 22 | // MARK:- Initializers 23 | 24 | /// Default Initializer. 25 | public required init() { 26 | super.init() 27 | } 28 | 29 | /// For initializing with a raw element. Parser use only. 30 | /// 31 | /// - Parameters: 32 | /// - raw: parser's raw element 33 | init(raw: GPXRawElement) { 34 | super.init() 35 | for child in raw.children { 36 | let tmp = GPXExtensionsElement(raw: child) 37 | children.append(tmp) 38 | } 39 | 40 | } 41 | 42 | // MARK:- Append and Retrieve 43 | 44 | /// Appending children tags to extension tag, easily. 45 | /// 46 | /// - Parameters: 47 | /// - parent: parent tag's name. If you do not wish to have a parent tag, leave it as `nil`. 48 | /// - contents: data to be represented as extended tag and values. 49 | public func append(at parent: String?, contents: [String : String]) { 50 | if let parent = parent { 51 | let parentElement = GPXExtensionsElement(name: parent) 52 | for (key, value) in contents { 53 | let element = GPXExtensionsElement(name: key) 54 | element.text = value 55 | parentElement.children.append(element) 56 | } 57 | children.append(parentElement) 58 | } 59 | else { 60 | for (key, value) in contents { 61 | let element = GPXExtensionsElement(name: key) 62 | element.text = value 63 | children.append(element) 64 | } 65 | } 66 | } 67 | 68 | /// Get a dictionary of data from a parent tag name, easily. 69 | /// 70 | /// - Parameters: 71 | /// - parent: parent tag name, to retrieve from. Leave it as `nil` if parent tag should not be expected. 72 | public func get(from parent: String?) -> [String : String]? { 73 | var data = [String : String]() 74 | 75 | if let parent = parent { 76 | var hasChild = false 77 | for child in children { 78 | if child.name == parent { 79 | data = child.attributes 80 | 81 | for child2 in child.children { 82 | data[child2.name] = child2.text 83 | } 84 | hasChild = true 85 | } 86 | } 87 | if !hasChild { 88 | return nil 89 | } 90 | } 91 | else { 92 | guard let child = children.first else { return nil } 93 | data = child.attributes 94 | data[child.name] = child.text 95 | } 96 | 97 | return data 98 | } 99 | 100 | // MARK:- Subscript 101 | 102 | /** 103 | Access child element in extensions. 104 | 105 | If extended data does not have a parent tag, **i.e**: 106 | 107 | 108 | 50 109 | 110 | 111 | Access it directly by `extensions["tag"]`, and access the text attribute of it. 112 | 113 | If extended data does not have a parent tag, **i.e**: 114 | 115 | 116 | 80 117 | 118 | 119 | Access it directly by `extensions["ParentTag"]["tag"]`, and access the text attribute of it. 120 | 121 | - Parameters: 122 | - name: name of child tag. 123 | */ 124 | public subscript(name: String) -> GPXExtensionsElement { 125 | get { 126 | for child in children { 127 | if child.name == name { 128 | return child 129 | } 130 | } 131 | return GPXExtensionsElement() 132 | } 133 | } 134 | 135 | // MARK:- Tag 136 | override func tagName() -> String { 137 | return "extensions" 138 | } 139 | 140 | // MARK:- Unavailable classes 141 | // Have been removed. 142 | 143 | 144 | // MARK:- GPX 145 | 146 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 147 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 148 | for child in children { 149 | child.gpx(gpx, indentationLevel: indentationLevel) 150 | } 151 | 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Classes/GPXExtensionsElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXExtensionsElement.swift 3 | // Pods 4 | // 5 | // Created by Vincent on 14/7/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A duplicated class of `GPXRawElement` 11 | /// 12 | /// This class is a public class as it is representative of all child extension tag types. 13 | /// 14 | /// It is also inherits `GPXElement`, and therefore, works like any other 'native' element types. 15 | open class GPXExtensionsElement: GPXElement, Codable { 16 | 17 | /// Tag name of extension element. 18 | public var name: String 19 | 20 | /// Text data content of the element. 21 | public var text: String? 22 | 23 | /// Attributes data of the element. 24 | public var attributes = [String : String]() 25 | 26 | /// Children tags of this element. 27 | public var children = [GPXExtensionsElement]() 28 | 29 | /// Easily get child tags via subscript. 30 | public subscript(name: String) -> GPXExtensionsElement { 31 | get { 32 | for child in children { 33 | if child.name == name { 34 | return child 35 | } 36 | } 37 | return GPXExtensionsElement() 38 | } 39 | } 40 | 41 | /// For initializing with a raw element. Parser use only. 42 | /// 43 | /// - Parameters: 44 | /// - raw: parser's raw element 45 | init(raw: GPXRawElement) { 46 | self.name = raw.name 47 | self.text = raw.text 48 | self.attributes = raw.attributes 49 | for child in raw.children { 50 | let tmp = GPXExtensionsElement(raw: child) 51 | self.children.append(tmp) 52 | } 53 | } 54 | 55 | /// Initialize with a tagName. 56 | public init(name: String) { 57 | self.name = name 58 | super.init() 59 | } 60 | 61 | /// Default initializer. 62 | required public init() { 63 | self.name = "Undefined" 64 | } 65 | 66 | // MARK:- GPX File Mutation 67 | 68 | override func tagName() -> String { 69 | return name 70 | } 71 | 72 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 73 | let attribute = NSMutableString(string: "") 74 | if !attributes.isEmpty { 75 | for (key, value) in attributes { 76 | attribute.append(" \(key)=\"\(value)\"") 77 | } 78 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 79 | } 80 | else if let text = text { 81 | self.addProperty(forValue: text, gpx: gpx, tagName: tagName(), indentationLevel: indentationLevel) 82 | } 83 | else { 84 | super.addOpenTag(toGPX: gpx, indentationLevel: indentationLevel) 85 | } 86 | } 87 | 88 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 89 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 90 | 91 | for child in children { 92 | child.gpx(gpx, indentationLevel: indentationLevel) 93 | } 94 | 95 | } 96 | 97 | override func addCloseTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 98 | if text == nil { 99 | gpx.appendCloseTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName()) 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /Classes/GPXFix.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXFix.swift 3 | // CoreGPX 4 | // 5 | // Created by Vincent on 2/9/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Type of GPS fix. 11 | /// 12 | /// - Note: 13 | /// I believe this enum may not be useful as `CoreLocation` API does not appear to state GPS Fix type. 14 | public enum GPXFix: String, Codable { 15 | 16 | /// No Fix 17 | case none = "none" 18 | 19 | /// 2D Fix, position only. 20 | case TwoDimensional = "2d" 21 | 22 | /// 3D Fix, position and elevation. 23 | case ThreeDimensional = "3d" 24 | 25 | /// Differencial GPS fix 26 | case Dgps = "dgps" 27 | 28 | /// Military GPS-equivalent 29 | case Pps = "pps" 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Classes/GPXLink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXLink.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 18/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /// Some common web extensions used for `init(withURL:)` 11 | fileprivate let kCommonWebExtensions: Set = ["htm", "html", "asp", "aspx", "jsp", "jspx", "do", "js", "php", "php3", "php4", "cgi", ".htaccess"] 12 | 13 | /** 14 | A value type that can hold a web link to a external resource, or external information about a certain attribute. 15 | 16 | In addition to having a URL as its attribute, it also accepts the following as child tag: 17 | - type of content 18 | - text of web link (probably a description kind of thing) 19 | */ 20 | public final class GPXLink: GPXElement, Codable { 21 | 22 | /// For Codable use 23 | private enum CodingKeys: String, CodingKey { 24 | case text 25 | case mimetype = "type" 26 | case href 27 | } 28 | 29 | // MARK:- Properties 30 | 31 | /// Text of hyperlink 32 | public var text: String? 33 | 34 | /// Mime type of content (image/jpeg) 35 | public var mimetype: String? 36 | 37 | /// URL of hyperlink 38 | public var href: String? 39 | 40 | // MARK:- Initializers 41 | 42 | /// Default Initializer. 43 | public required init() { 44 | super.init() 45 | } 46 | 47 | /// Initializes with a web link attribute 48 | /// 49 | /// - Parameters: 50 | /// - href: **Hypertext Reference**. Basically, a web link which can be considered as a reference to whichever content, including metadata, waypoints, for example. 51 | /// 52 | public init(withHref href: String) { 53 | self.href = href 54 | } 55 | 56 | /// Initializes with a URL. 57 | /// 58 | /// This initializer is similar to `init(withHref:)`, except, this method checks on whether if the input URL is valid or not. It also modifies the `type` attribute as 'Website' if identified to have a web page extension. 59 | /// 60 | /// - Parameters: 61 | /// - url: input URL, intended as a web link reference. 62 | public init(withURL url: URL?) { 63 | guard let isFileURL = url?.isFileURL else { return } 64 | if !isFileURL { 65 | self.href = url?.absoluteString 66 | guard let pathExtension = url?.pathExtension else { return } 67 | // may not work if web extension is not shown. (etc, using .htaccess) 68 | if kCommonWebExtensions.contains(pathExtension) { 69 | self.mimetype = "Website" 70 | } 71 | } 72 | } 73 | 74 | /// Inits native element from raw parser value 75 | /// 76 | /// - Parameters: 77 | /// - raw: Raw element expected from parser 78 | init(raw: GPXRawElement) { 79 | for child in raw.children { 80 | switch child.name { 81 | case "type": self.mimetype = child.text 82 | case "text": self.text = child.text 83 | default: continue 84 | } 85 | } 86 | self.href = raw.attributes["href"] 87 | } 88 | 89 | public init?(url: URL?, name: String? = nil) { 90 | if url == nil && name == nil { return nil } 91 | self.text = name 92 | self.href = url?.absoluteString 93 | } 94 | 95 | // MARK:- Tag 96 | 97 | override func tagName() -> String { 98 | return "link" 99 | } 100 | 101 | // MARK:- GPX 102 | 103 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 104 | let attribute = NSMutableString(string: "") 105 | 106 | if let href = href { 107 | attribute.append(" href=\"\(href)\"") 108 | } 109 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 110 | } 111 | 112 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 113 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 114 | 115 | self.addProperty(forValue: text, gpx: gpx, tagName: "text", indentationLevel: indentationLevel) 116 | self.addProperty(forValue: mimetype, gpx: gpx, tagName: "type", indentationLevel: indentationLevel) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /Classes/GPXMetadata.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXMetadata.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 22/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A value type that represents the metadata header of a GPX file. 12 | 13 | Information about the GPX file should be stored here. 14 | - Supported Info types: 15 | - Name 16 | - Description 17 | - Author Info 18 | - Copyright 19 | - Date and Time 20 | - Keyword 21 | - Bounds 22 | - Also supports extensions 23 | */ 24 | public final class GPXMetadata: GPXElement, Codable { 25 | 26 | /// Name intended for the GPX file. 27 | public var name: String? 28 | 29 | /// Description about what the GPX file is about. 30 | public var desc: String? 31 | 32 | /// Author, or the person who created the GPX file. 33 | /// 34 | /// Includes other information regarding the author (see `GPXAuthor`) 35 | public var author: GPXAuthor? 36 | 37 | /// Copyright of the file, if required. 38 | public var copyright: GPXCopyright? 39 | 40 | /// A web link, usually one with information regarding the GPX file. 41 | @available(*, deprecated, message: "CoreGPX now support multiple links.", renamed: "links.first") 42 | public var link: GPXLink? { 43 | return links.first 44 | } 45 | 46 | /// Web links, usually containing information regarding the current GPX file which houses this metadata. 47 | public var links = [GPXLink]() 48 | 49 | /// Date and time of when the GPX file is created. 50 | public var time: Date? 51 | 52 | /// Keyword of the GPX file. 53 | public var keywords: String? 54 | 55 | /// Boundaries of coordinates of the GPX file. 56 | public var bounds: GPXBounds? 57 | 58 | /// Extensions to standard GPX, if any. 59 | public var extensions: GPXExtensions? 60 | 61 | 62 | // MARK:- Initializers 63 | 64 | /// Default initializer. 65 | required public init() { 66 | self.time = Date() 67 | super.init() 68 | } 69 | 70 | /// Inits native element from raw parser value 71 | /// 72 | /// - Parameters: 73 | /// - raw: Raw element expected from parser 74 | init(raw: GPXRawElement) { 75 | //super.init() 76 | for child in raw.children { 77 | //let text = child.text 78 | 79 | switch child.name { 80 | case "name": self.name = child.text 81 | case "desc": self.desc = child.text 82 | case "author": self.author = GPXAuthor(raw: child) 83 | case "copyright": self.copyright = GPXCopyright(raw: child) 84 | case "link": self.links.append(GPXLink(raw: child)) 85 | case "time": self.time = GPXDateParser().parse(date: child.text) 86 | case "keywords": self.keywords = child.text 87 | case "bounds": self.bounds = GPXBounds(raw: child) 88 | case "extensions": self.extensions = GPXExtensions(raw: child) 89 | default: continue 90 | } 91 | } 92 | } 93 | 94 | // MARK:- Tag 95 | 96 | override func tagName() -> String { 97 | return "metadata" 98 | } 99 | 100 | // MARK:- GPX 101 | 102 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 103 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 104 | 105 | self.addProperty(forValue: name, gpx: gpx, tagName: "name", indentationLevel: indentationLevel) 106 | self.addProperty(forValue: desc, gpx: gpx, tagName: "desc", indentationLevel: indentationLevel) 107 | 108 | if author != nil { 109 | self.author?.gpx(gpx, indentationLevel: indentationLevel) 110 | } 111 | 112 | if copyright != nil { 113 | self.copyright?.gpx(gpx, indentationLevel: indentationLevel) 114 | } 115 | 116 | for link in links { 117 | link.gpx(gpx, indentationLevel: indentationLevel) 118 | } 119 | 120 | self.addProperty(forValue: Convert.toString(from: time), gpx: gpx, tagName: "time", indentationLevel: indentationLevel) 121 | self.addProperty(forValue: keywords, gpx: gpx, tagName: "keywords", indentationLevel: indentationLevel) 122 | 123 | if bounds != nil { 124 | self.bounds?.gpx(gpx, indentationLevel: indentationLevel) 125 | } 126 | 127 | if extensions != nil { 128 | self.extensions?.gpx(gpx, indentationLevel: indentationLevel) 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /Classes/GPXPerson.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXPerson.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 18/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /// A value type that is designated to hold information regarding the person or organisation who has created the GPX file. 11 | public class GPXPerson: GPXElement, Codable { 12 | 13 | /// Name of person who has created the GPX file. 14 | public var name: String? 15 | 16 | /// The email address of the person who has created the GPX file. 17 | public var email: GPXEmail? 18 | 19 | /// An external website that holds information on the person who has created the GPX file. Additional information may be supported as well. 20 | public var link: GPXLink? 21 | 22 | // MARK:- Initializers 23 | 24 | // Default Initializer. 25 | public required init() { 26 | super.init() 27 | } 28 | 29 | /// Inits native element from raw parser value 30 | /// 31 | /// - Parameters: 32 | /// - raw: Raw element expected from parser 33 | init(raw: GPXRawElement) { 34 | for child in raw.children { 35 | switch child.name { 36 | case "name": self.name = child.text 37 | case "email": self.email = GPXEmail(raw: child) 38 | case "link": self.link = GPXLink(raw: child) 39 | default: continue 40 | } 41 | } 42 | } 43 | 44 | // MARK:- Tag 45 | 46 | override func tagName() -> String { 47 | return "person" 48 | } 49 | 50 | // MARK:- GPX 51 | 52 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 53 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 54 | 55 | self.addProperty(forValue: name, gpx: gpx, tagName: "name", indentationLevel: indentationLevel) 56 | 57 | if email != nil { 58 | self.email?.gpx(gpx, indentationLevel: indentationLevel) 59 | } 60 | 61 | if link != nil { 62 | self.link?.gpx(gpx, indentationLevel: indentationLevel) 63 | } 64 | 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Classes/GPXPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXPoint.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 23/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | * This class (`ptType`) is added to conform with the GPX v1.1 schema. 12 | 13 | `ptType` of GPX schema. Not supported in GPXRoot, nor GPXParser's parsing. 14 | */ 15 | open class GPXPoint: GPXElement, Codable { 16 | 17 | /// Elevation Value in (metre, m) 18 | public var elevation: Double? 19 | /// Time/Date of creation 20 | public var time: Date? 21 | /// Latitude of point 22 | public var latitude: Double? 23 | /// Longitude of point 24 | public var longitude: Double? 25 | 26 | // MARK:- Instance 27 | 28 | /// Default Initializer. 29 | required public init() { 30 | super.init() 31 | } 32 | /// Initialize with latitude and longitude 33 | public init(latitude: Double, longitude: Double) { 34 | super.init() 35 | self.latitude = latitude 36 | self.longitude = longitude 37 | } 38 | 39 | /// Initialize with elevation, time, latitude, longitude 40 | public init(elevation: Double? = nil, time: Date? = nil, latitude: Double? = nil, longitude: Double? = nil) { 41 | super.init() 42 | self.elevation = elevation 43 | self.time = time 44 | self.latitude = latitude 45 | self.longitude = longitude 46 | } 47 | 48 | /// Inits native element from raw parser value 49 | /// 50 | /// - Parameters: 51 | /// - raw: Raw element expected from parser 52 | init(raw: GPXRawElement) { 53 | self.latitude = Convert.toDouble(from: raw.attributes["lat"]) 54 | self.longitude = Convert.toDouble(from: raw.attributes["lon"]) 55 | for child in raw.children { 56 | switch child.name { 57 | case "ele": self.elevation = Convert.toDouble(from: child.text) 58 | case "time": self.time = GPXDateParser().parse(date: child.text) 59 | default: continue 60 | } 61 | } 62 | } 63 | 64 | // MARK:- Tag 65 | 66 | override func tagName() -> String { 67 | return "pt" 68 | } 69 | 70 | // MARK: GPX 71 | 72 | override func addOpenTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 73 | let attribute = NSMutableString(string: "") 74 | if let latitude = latitude { 75 | attribute.append(" lat=\"\(latitude)\"") 76 | } 77 | if let longitude = longitude { 78 | attribute.append(" lon=\"\(longitude)\"") 79 | } 80 | 81 | gpx.appendOpenTag(indentation: indent(forIndentationLevel: indentationLevel), tag: tagName(), attribute: attribute) 82 | } 83 | 84 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 85 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 86 | 87 | self.addProperty(forDoubleValue: elevation, gpx: gpx, tagName: "ele", indentationLevel: indentationLevel) 88 | self.addProperty(forValue: Convert.toString(from: time), gpx: gpx, tagName: "time", indentationLevel: indentationLevel) 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /Classes/GPXPointSegment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXPointSegment.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 23/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | * This class (`ptsegType`) is added to conform with the GPX v1.1 schema. 12 | 13 | `ptsegType` of GPX schema. Not supported in GPXRoot, nor GPXParser's parsing. 14 | */ 15 | open class GPXPointSegment: GPXElement, Codable { 16 | 17 | /// points of segment 18 | public var points = [GPXPoint]() 19 | 20 | // MARK:- Instance 21 | 22 | /// Default initializer. 23 | public required init() { 24 | super.init() 25 | } 26 | 27 | /// Inits native element from raw parser value 28 | /// 29 | /// - Parameters: 30 | /// - raw: Raw element expected from parser 31 | init(raw: GPXRawElement) { 32 | for child in raw.children { 33 | if child.name == "pt" { 34 | points.append(GPXPoint(raw: child)) 35 | } 36 | else { break } 37 | } 38 | } 39 | 40 | /// Initialize with points 41 | public init(points: [GPXPoint]) { 42 | self.points = points 43 | } 44 | 45 | // MARK:- Public Methods 46 | 47 | /// Adds a new point to segment, and returns the added point. 48 | public func newPoint(with latitude: Double, longitude: Double) -> GPXPoint { 49 | 50 | let point = GPXPoint(latitude: latitude, longitude: longitude) 51 | 52 | self.add(point: point) 53 | 54 | return point 55 | } 56 | 57 | /// Appends a point to the point segment 58 | public func add(point: GPXPoint?) { 59 | if let validPoint = point { 60 | points.append(validPoint) 61 | } 62 | } 63 | 64 | /// Appends an array of points to the point segment 65 | public func add(points: [GPXPoint]) { 66 | self.points.append(contentsOf: points) 67 | } 68 | 69 | /// Remove a single point in the point segment 70 | public func remove(point: GPXPoint) { 71 | let contains = points.contains(point) 72 | if contains == true { 73 | if let index = points.firstIndex(of: point) { 74 | points.remove(at: index) 75 | } 76 | } 77 | } 78 | 79 | // MARK:- Tag 80 | 81 | override func tagName() -> String { 82 | return "ptseg" 83 | } 84 | 85 | // MARK:- GPX 86 | 87 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 88 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 89 | 90 | for point in points { 91 | point.gpx(gpx, indentationLevel: indentationLevel) 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /Classes/GPXRawElement.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParserElement.swift 3 | // CoreGPX 4 | // 5 | // Created by Vincent on 2/7/19. 6 | // 7 | // Referenced from GitHub, yahoojapan/SwiftyXMLParser 8 | 9 | import Foundation 10 | 11 | /// Raw element that is for GPXParser to work with. 12 | /// 13 | /// Should not be used as is, and should be disposed off once done with parsing. 14 | final class GPXRawElement { 15 | 16 | /// name tag of element 17 | var name: String 18 | 19 | /// text contents of element 20 | var text: String? 21 | 22 | /// open tag attributes of element 23 | var attributes = [String : String]() 24 | 25 | /// child of element tag 26 | var children = [GPXRawElement]() 27 | 28 | /// init with name tag name 29 | init(name: String) { 30 | self.name = name 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Classes/GPXRoute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXRoute.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 8/12/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Value type that represents a route, or `rteType` in GPX v1.1 schema. 12 | 13 | The route can represent the planned route of a specific trip. 14 | */ 15 | public final class GPXRoute: GPXElement, Codable, GPXRouteType { 16 | 17 | /// For Codable use 18 | private enum CodingKeys: String, CodingKey { 19 | case name 20 | case comment = "cmt" 21 | case desc 22 | case source = "src" 23 | case links = "link" 24 | case type 25 | case extensions 26 | } 27 | 28 | /// Name of the route. 29 | public var name: String? 30 | 31 | /// Additional comment of the route. 32 | public var comment: String? 33 | 34 | /// Description of the route. 35 | public var desc: String? 36 | 37 | /// Source of the route. 38 | public var source: String? 39 | 40 | /// A value type for link properties (see `GPXLink`) 41 | /// 42 | /// Intended for additional information about current route through a web link. 43 | @available(*, deprecated, message: "CoreGPX now support multiple links.", renamed: "links.first") 44 | public var link: GPXLink? { 45 | return links.first 46 | } 47 | 48 | /// A value type for link properties (see `GPXLink`) 49 | /// 50 | /// Intended for additional information about current route through web links. 51 | public var links = [GPXLink]() 52 | 53 | /// Type of route. 54 | public var type: String? 55 | 56 | /// Extensions 57 | public var extensions: GPXExtensions? 58 | 59 | /// Route points in the route. 60 | /// 61 | /// All route points joined represents a route. 62 | @available(*, deprecated, renamed: "points") 63 | public var routepoints: [GPXRoutePoint] { 64 | return points 65 | } 66 | 67 | /// Route points in the route. 68 | /// 69 | /// All route points joined represents a route. 70 | public var points = [GPXRoutePoint]() 71 | 72 | /// Number of route (possibly a tag for the route) 73 | public var number: Int? 74 | 75 | // MARK:- Instance 76 | 77 | /// Default initializer. 78 | public required init() { 79 | super.init() 80 | } 81 | 82 | /// Initializer with values for convenience 83 | /// 84 | /// - Parameters: 85 | /// - name: Name of the route. 86 | /// - comment: Additional comment of the route. 87 | /// - desc: Description of the route. 88 | /// - source: Source of the route. 89 | /// - links: A value type for link properties (see `GPXLink`) 90 | /// Intended for additional information about current route through web links. 91 | /// - type: Type of route. 92 | /// - extensions: Items for extensions to GPX schema (if any). 93 | public init(name: String? = nil, comment: String? = nil, desc: String? = nil, source: String? = nil, links: [GPXLink] = [], type: String? = nil, extensions: GPXExtensions? = nil, points: [GPXRoutePoint] = [], number: Int? = nil) { 94 | self.name = name 95 | self.comment = comment 96 | self.desc = desc 97 | self.source = source 98 | self.links = links 99 | self.type = type 100 | self.extensions = extensions 101 | self.points = points 102 | self.number = number 103 | } 104 | 105 | /// Inits native element from raw parser value 106 | /// 107 | /// - Parameters: 108 | /// - raw: Raw element expected from parser 109 | init(raw: GPXRawElement) { 110 | for child in raw.children { 111 | switch child.name { 112 | case "link": self.links.append(GPXLink(raw: child)) 113 | case "rtept": self.points.append(GPXRoutePoint(raw: child)) 114 | case "name": self.name = child.text 115 | case "cmt": self.comment = child.text 116 | case "desc": self.desc = child.text 117 | case "src": self.source = child.text 118 | case "type": self.type = child.text 119 | case "number": self.number = Convert.toInt(from: child.text) 120 | case "extensions": self.extensions = GPXExtensions(raw: child) 121 | default: continue 122 | } 123 | } 124 | } 125 | 126 | // MARK: Public Methods 127 | 128 | /// Creates a `GPXLink` which is added to the route and also returned. 129 | /// 130 | /// Not recommended for use. Init `GPXRoutePoint` manually, then adding it to route, instead. 131 | func newLink(withHref href: String) -> GPXLink { 132 | let link: GPXLink = GPXLink(withHref: href) 133 | self.links.append(link) 134 | return link 135 | } 136 | 137 | /// Creates a `GPXRoutePoint` which is added to the route and also returned. 138 | /// 139 | /// Not recommended for use. Init `GPXRoutePoint` manually, then adding it to route, instead. 140 | func newRoutePointWith(latitude: Double, longitude: Double) -> GPXRoutePoint { 141 | let routepoint = GPXRoutePoint(latitude: latitude, longitude: longitude) 142 | 143 | self.add(routepoint: routepoint) 144 | 145 | return routepoint 146 | } 147 | 148 | /// Adds a singular route point to the route. 149 | func add(routepoint: GPXRoutePoint?) { 150 | if let validPoint = routepoint { 151 | points.append(validPoint) 152 | } 153 | } 154 | 155 | /// Adds an array of route points to the route. 156 | func add(routepoints: [GPXRoutePoint]) { 157 | self.points.append(contentsOf: routepoints) 158 | } 159 | 160 | /// Removes a route point from the route. 161 | func remove(routepoint: GPXRoutePoint) { 162 | let contains = points.contains(routepoint) 163 | if contains == true { 164 | if let index = points.firstIndex(of: routepoint) { 165 | points.remove(at: index) 166 | } 167 | } 168 | 169 | } 170 | 171 | // MARK:- Tag 172 | 173 | override func tagName() -> String { 174 | return "rte" 175 | } 176 | 177 | // MARK:- GPX 178 | 179 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 180 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 181 | 182 | self.addProperty(forValue: name, gpx: gpx, tagName: "name", indentationLevel: indentationLevel) 183 | self.addProperty(forValue: comment, gpx: gpx, tagName: "comment", indentationLevel: indentationLevel) 184 | self.addProperty(forValue: desc, gpx: gpx, tagName: "desc", indentationLevel: indentationLevel) 185 | self.addProperty(forValue: source, gpx: gpx, tagName: "src", indentationLevel: indentationLevel) 186 | 187 | for link in links { 188 | link.gpx(gpx, indentationLevel: indentationLevel) 189 | } 190 | 191 | self.addProperty(forIntegerValue: number, gpx: gpx, tagName: "number", indentationLevel: indentationLevel) 192 | self.addProperty(forValue: type, gpx: gpx, tagName: "type", indentationLevel: indentationLevel) 193 | 194 | if self.extensions != nil { 195 | self.extensions?.gpx(gpx, indentationLevel: indentationLevel) 196 | } 197 | 198 | for routepoint in points { 199 | routepoint.gpx(gpx, indentationLevel: indentationLevel) 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /Classes/GPXRoutePoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXRoutePoint.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 19/11/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A route point is just like a waypoint or track point, but is suited to be part of a route. 12 | 13 | These route points in collective, forms a valid route. 14 | */ 15 | public final class GPXRoutePoint: GPXWaypoint { 16 | 17 | /// Default initializer 18 | public required init() { 19 | super.init() 20 | } 21 | 22 | // MARK:- Instance 23 | 24 | public override init(latitude: Double, longitude: Double) { 25 | super.init(latitude: latitude, longitude: longitude) 26 | } 27 | 28 | override init(raw: GPXRawElement) { 29 | super.init(raw: raw) 30 | } 31 | 32 | /// For initializing with a `Decoder` 33 | /// 34 | /// Declared here for use of Codable functionalities. 35 | required public init(from decoder: Decoder) throws { 36 | try super.init(from: decoder) 37 | } 38 | 39 | // MARK:- Tag 40 | 41 | override func tagName() -> String { 42 | return "rtept" 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Classes/GPXRouteProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXRouteProtocol.swift 3 | // Pods 4 | // 5 | // Created by Vincent Neo on 13/6/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol GPXRouteType { 11 | /// Name of the route. 12 | var name: String? { get set } 13 | 14 | /// Additional comment of the route. 15 | var comment: String? { get set } 16 | 17 | /// Description of the route. 18 | var desc: String? { get set } 19 | 20 | /// Source of the route. 21 | var source: String? { get set } 22 | 23 | /// Number of route (possibly a tag for the route) 24 | var number: Int? { get set } 25 | } 26 | -------------------------------------------------------------------------------- /Classes/GPXTrack.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXTrack.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 9/12/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | Represents `trkType` of GPX v1.1 schema. 12 | 13 | A track can hold track segments, along with additional information regarding the track. 14 | 15 | Tracks are meant to show the start and finish of a journey, through the track segments that it holds. 16 | */ 17 | public final class GPXTrack: GPXElement, Codable { 18 | 19 | /// for Codable 20 | private enum CodingKeys: String, CodingKey { 21 | case links = "link" 22 | case segments = "trkseg" 23 | case name 24 | case comment = "cmt" 25 | case desc 26 | case source = "src" 27 | case number 28 | case type 29 | case extensions 30 | } 31 | 32 | /// A value type for link properties (see `GPXLink`) 33 | /// 34 | /// Intended for additional information about current route through a web link. 35 | @available(*, deprecated, message: "CoreGPX now support multiple links.", renamed: "links.first") 36 | public var link: GPXLink? { 37 | return links.first 38 | } 39 | 40 | /// A value type for link properties (see `GPXLink`) 41 | /// 42 | /// Holds web links to external resources regarding the current track. 43 | public var links = [GPXLink]() 44 | 45 | /// Array of track segments. Must be included in every track. 46 | @available(*, deprecated, renamed: "segments") 47 | public var tracksegments: [GPXTrackSegment] { 48 | return segments 49 | } 50 | 51 | /// Array of track segments. Must be included in every track. 52 | public var segments = [GPXTrackSegment]() 53 | 54 | /// Name of track. 55 | public var name: String? 56 | 57 | /// Additional comment of track. 58 | public var comment: String? 59 | 60 | /// A full description of the track. Can be of any length. 61 | public var desc: String? 62 | 63 | /// Source of track. 64 | public var source: String? 65 | 66 | /// GPS track number. 67 | public var number: Int? 68 | 69 | /// Type of current track. 70 | public var type: String? 71 | 72 | /// Custom Extensions of track, if needed. 73 | public var extensions: GPXExtensions? 74 | 75 | /// Default Initializer 76 | public required init() { 77 | super.init() 78 | } 79 | 80 | /// Initialize with values 81 | /// 82 | /// - Parameters: 83 | /// - links: A value type for link properties (see `GPXLink`) 84 | /// Holds web links to external resources regarding the current track. 85 | /// - segments: Array of track segments. Must be included in every track. 86 | /// - name: Name of track. 87 | /// - comment: Additional comment of track. 88 | /// - desc: A full description of the track. Can be of any length. 89 | /// - source: Source of track. 90 | /// - number: GPS track number. 91 | /// - type: Type of current track. 92 | /// - extensions: Custom Extensions of track, if needed. 93 | public init(links: [GPXLink] = [], segments: [GPXTrackSegment] = [], name: String? = nil, comment: String? = nil, desc: String? = nil, source: String? = nil, number: Int? = nil, type: String? = nil, extensions: GPXExtensions? = nil) { 94 | self.links = links 95 | self.segments = segments 96 | self.name = name 97 | self.comment = comment 98 | self.desc = desc 99 | self.source = source 100 | self.number = number 101 | self.type = type 102 | self.extensions = extensions 103 | } 104 | /// Inits native element from raw parser value 105 | /// 106 | /// - Parameters: 107 | /// - raw: Raw element expected from parser 108 | init(raw: GPXRawElement) { 109 | for child in raw.children { 110 | switch child.name { 111 | case "link": self.links.append(GPXLink(raw: child)) 112 | case "trkseg": self.segments.append(GPXTrackSegment(raw: child)) 113 | case "name": self.name = child.text 114 | case "cmt": self.comment = child.text 115 | case "desc": self.desc = child.text 116 | case "src": self.source = child.text 117 | case "type": self.type = child.text 118 | case "extensions": self.extensions = GPXExtensions(raw: child) 119 | default: continue 120 | } 121 | } 122 | } 123 | 124 | // MARK:- Public Methods 125 | 126 | /// Initialize a new `GPXLink` to the track. 127 | /// 128 | /// Method not recommended for use. Please initialize `GPXLink` manually and adding it to the track instead. 129 | public func newLink(withHref href: String) -> GPXLink { 130 | let link = GPXLink(withHref: href) 131 | return link 132 | } 133 | 134 | /// Initialize a new `GPXTrackSegement` to the track. 135 | /// 136 | /// Method not recommended for use. Please initialize `GPXTrackSegment` manually and adding it to the track instead. 137 | public func newTrackSegment() -> GPXTrackSegment { 138 | let tracksegment = GPXTrackSegment() 139 | self.add(trackSegment: tracksegment) 140 | return tracksegment 141 | } 142 | 143 | /// Adds a single track segment to the track. 144 | public func add(trackSegment: GPXTrackSegment?) { 145 | if let validTrackSegment = trackSegment { 146 | segments.append(validTrackSegment) 147 | } 148 | } 149 | 150 | /// Adds an array of track segments to the track. 151 | public func add(trackSegments: [GPXTrackSegment]) { 152 | self.segments.append(contentsOf: trackSegments) 153 | } 154 | 155 | /// Removes a tracksegment from the track. 156 | public func remove(trackSegment: GPXTrackSegment) { 157 | let contains = segments.contains(trackSegment) 158 | 159 | if contains == true { 160 | if let index = segments.firstIndex(of: trackSegment) { 161 | segments.remove(at: index) 162 | } 163 | } 164 | } 165 | 166 | /// Initializes a new track point in track, then returns the new track point. 167 | public func newTrackPointWith(latitude: Double, longitude: Double) -> GPXTrackPoint { 168 | var tracksegment: GPXTrackSegment 169 | 170 | if let lastTracksegment = segments.last { 171 | tracksegment = lastTracksegment 172 | } else { 173 | tracksegment = self.newTrackSegment() 174 | } 175 | 176 | return tracksegment.newTrackpointWith(latitude: latitude, longitude: longitude) 177 | } 178 | 179 | // MARK:- Tag 180 | 181 | override func tagName() -> String { 182 | return "trk" 183 | } 184 | 185 | // MARK:- GPX 186 | 187 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 188 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 189 | 190 | self.addProperty(forValue: name, gpx: gpx, tagName: "name", indentationLevel: indentationLevel) 191 | self.addProperty(forValue: comment, gpx: gpx, tagName: "cmt", indentationLevel: indentationLevel) 192 | self.addProperty(forValue: desc, gpx: gpx, tagName: "desc", indentationLevel: indentationLevel) 193 | self.addProperty(forValue: source, gpx: gpx, tagName: "src", indentationLevel: indentationLevel) 194 | 195 | for link in links { 196 | link.gpx(gpx, indentationLevel: indentationLevel) 197 | } 198 | 199 | self.addProperty(forIntegerValue: number, gpx: gpx, tagName: "number", indentationLevel: indentationLevel) 200 | self.addProperty(forValue: type, gpx: gpx, tagName: "type", indentationLevel: indentationLevel) 201 | 202 | if extensions != nil { 203 | self.extensions?.gpx(gpx, indentationLevel: indentationLevel) 204 | } 205 | 206 | for tracksegment in segments { 207 | tracksegment.gpx(gpx, indentationLevel: indentationLevel) 208 | } 209 | 210 | } 211 | } 212 | -------------------------------------------------------------------------------- /Classes/GPXTrackPoint.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXTrackPoint.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 9/12/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A track point is just like a waypoint or route point, but is suited to be part of a track segment. 12 | 13 | A bunch of track points can be used to form a track segement, while track segments form a track. 14 | (though a single track segment itself is enough to form a track.) 15 | */ 16 | public final class GPXTrackPoint: GPXWaypoint { 17 | 18 | // MARK:- Initializers 19 | 20 | /// Default Initializer. 21 | public required init() { 22 | super.init() 23 | } 24 | 25 | public override init(latitude: Double, longitude: Double) { 26 | super.init(latitude: latitude, longitude: longitude) 27 | } 28 | 29 | public override init(links: [GPXLink] = [], elevation: Double? = nil, time: Date? = nil, magneticVariation: Double? = nil, geoidHeight: Double? = nil, source: String? = nil, type: String? = nil, satellites: Int? = nil, horizontalDilution: Double? = nil, verticalDilution: Double? = nil, positionDilution: Double? = nil, extensions: GPXExtensions? = nil, latitude: Double? = nil, longitude: Double? = nil) { 30 | super.init(links: links, elevation: elevation, time: time, magneticVariation: magneticVariation, geoidHeight: geoidHeight, source: source, type: type, satellites: satellites, horizontalDilution: horizontalDilution, verticalDilution: verticalDilution, positionDilution: positionDilution, extensions: extensions, latitude: latitude, longitude: longitude) 31 | } 32 | 33 | override init(raw: GPXRawElement) { 34 | super.init(raw: raw) 35 | } 36 | 37 | /// For initializing with a `Decoder` 38 | /// 39 | /// Declared here for use of Codable functionalities. 40 | required public init(from decoder: Decoder) throws { 41 | try super.init(from: decoder) 42 | } 43 | 44 | 45 | // MARK:- Tag 46 | 47 | override func tagName() -> String { 48 | return "trkpt" 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Classes/GPXTrackSegment.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXTrackSegment.swift 3 | // GPXKit 4 | // 5 | // Created by Vincent on 9/12/18. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | A track segment that holds data on all track points in the particular segment. 12 | 13 | Does not hold additional information by default. 14 | */ 15 | public final class GPXTrackSegment: GPXElement, Codable { 16 | 17 | /// For Codable use 18 | private enum CodingKeys: String, CodingKey { 19 | case points = "trkpt" 20 | case extensions 21 | } 22 | 23 | /// Track points stored in current segment. 24 | @available(*, deprecated, renamed: "points") 25 | public var trackpoints: [GPXTrackPoint] { 26 | return points 27 | } 28 | 29 | /// Track points stored in current segment. 30 | public var points = [GPXTrackPoint]() 31 | 32 | /// Custom Extensions, if needed. 33 | public var extensions: GPXExtensions? 34 | 35 | 36 | // MARK:- Instance 37 | 38 | /// Default Initializer. 39 | public required init() { 40 | super.init() 41 | } 42 | 43 | /// Inits segment from 44 | /// 45 | /// - Parameters: 46 | /// - points: Track points of the segment. 47 | /// - extensions: Custom Extensions, if needed. 48 | public init(points: [GPXTrackPoint] = [], extensions: GPXExtensions? = nil) { 49 | self.points = points 50 | self.extensions = extensions 51 | } 52 | 53 | /// Inits native element from raw parser value 54 | /// 55 | /// - Parameters: 56 | /// - raw: Raw element expected from parser 57 | init(raw: GPXRawElement) { 58 | for child in raw.children { 59 | switch child.name { 60 | case "trkpt": self.points.append(GPXTrackPoint(raw: child)) 61 | case "extensions": self.extensions = GPXExtensions(raw: child) 62 | default: continue 63 | } 64 | } 65 | } 66 | 67 | // MARK:- Public Methods 68 | 69 | /// Initializes a new trackpoint to segment, and returns the trackpoint. 70 | public func newTrackpointWith(latitude: Double, longitude: Double) -> GPXTrackPoint { 71 | let trackpoint = GPXTrackPoint(latitude: latitude, longitude: longitude) 72 | 73 | self.add(trackpoint: trackpoint) 74 | 75 | return trackpoint 76 | } 77 | 78 | /// Adds a single track point to this track segment. 79 | public func add(trackpoint: GPXTrackPoint?) { 80 | if let validPoint = trackpoint { 81 | points.append(validPoint) 82 | } 83 | } 84 | 85 | /// Adds an array of track points to this track segment. 86 | public func add(trackpoints: [GPXTrackPoint]) { 87 | self.points.append(contentsOf: trackpoints) 88 | } 89 | 90 | /// Removes a track point from this track segment. 91 | public func remove(trackpoint: GPXTrackPoint) { 92 | if let index = points.firstIndex(of: trackpoint) { 93 | points.remove(at: index) 94 | } 95 | } 96 | 97 | // MARK:- Tag 98 | 99 | override func tagName() -> String { 100 | return "trkseg" 101 | } 102 | 103 | // MARK:- GPX 104 | 105 | override func addChildTag(toGPX gpx: NSMutableString, indentationLevel: Int) { 106 | super.addChildTag(toGPX: gpx, indentationLevel: indentationLevel) 107 | 108 | if self.extensions != nil { 109 | self.extensions?.gpx(gpx, indentationLevel: indentationLevel) 110 | } 111 | 112 | for trackpoint in points { 113 | trackpoint.gpx(gpx, indentationLevel: indentationLevel) 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /Classes/GPXWaypointProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GPXWaypointProtocol.swift 3 | // Pods 4 | // 5 | // Created by Vincent Neo on 13/6/20. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol GPXWaypointProtocol: GPXElement { 11 | // MARK:- Attributes of a waypoint 12 | 13 | /// Elevation of current point 14 | /// 15 | /// Should be in unit **meters** (m) 16 | var elevation: Double? { get set } 17 | 18 | /// Date and time of current point 19 | /// 20 | /// Should be in **Coordinated Universal Time (UTC)**, without offsets, not local time. 21 | var time: Date? { get set } 22 | 23 | /// Magnetic Variation of current point 24 | /// 25 | /// Should be in unit **degrees** (º) 26 | var magneticVariation: Double? { get set } 27 | 28 | /// Geoid Height of current point 29 | /// 30 | /// Should be in unit **meters** (m). Height of geoid, or mean sea level, above WGS84 earth ellipsoid 31 | var geoidHeight: Double? { get set } 32 | 33 | /// Name of current point 34 | /// 35 | /// - Warning: 36 | /// - This attribute may not be useful, in schema context. 37 | /// - This is carried over from GPX schema, to be compliant with the schema. 38 | var name: String? { get set } 39 | 40 | /// Comment of current point 41 | /// 42 | /// - Warning: 43 | /// - This attribute may not be useful, in schema context. 44 | /// - This is carried over from GPX schema, to be compliant with the schema. 45 | var comment: String? { get set } 46 | 47 | /// Description of current point 48 | /// 49 | /// - Warning: 50 | /// - This attribute may not be useful, in schema context. 51 | /// - This is carried over from GPX schema, to be compliant with the schema. 52 | var desc: String? { get set } 53 | 54 | /// Source of data of current point 55 | /// 56 | /// For assurance that current point is reliable 57 | var source: String? { get set } 58 | 59 | /// Text of GPS symbol name 60 | /// 61 | /// - Warning: 62 | /// - This attribute does not appear to be useful due to `CoreLocation` API. 63 | /// - This is carried over from GPX schema, to be compliant with the schema. 64 | var symbol: String? { get set } 65 | 66 | /// Type of current point 67 | var type: String? { get set } 68 | 69 | /// Type of GPS fix of current point, represented as a number 70 | /// 71 | /// - **Supported Types:** (written in order) 72 | /// - **None**: No fix 73 | /// - **2D**: Position only 74 | /// - **3D**: Position and Elevation 75 | /// - **DGPS**: Differential GPS 76 | /// - **PPS**: Military Signal 77 | /// 78 | /// Unknown fix should leave fix attribute as `nil` 79 | /// - Warning: 80 | /// - This attribute may have limited usefulness due to `CoreLocation` API. 81 | /// - This is carried over from GPX schema, to be compliant with the schema. 82 | var fix: GPXFix? { get set } 83 | 84 | /// Number of satellites used to calculate GPS fix of current point 85 | var satellites: Int? { get set } 86 | 87 | /// Horizontal dilution of precision of current point 88 | var horizontalDilution: Double? { get set } 89 | 90 | /// Vertical dilution of precision of current point 91 | var verticalDilution: Double? { get set } 92 | 93 | /// Position dilution of precision of current point 94 | var positionDilution: Double? { get set } 95 | 96 | /// Age of DGPS Data 97 | /// 98 | /// Number of seconds since last DGPS update 99 | /// 100 | /// - Warning: 101 | /// - This attribute may not be useful. 102 | /// - This is carried over from GPX schema, to be compliant with the schema. 103 | var ageofDGPSData: Double? { get set } 104 | 105 | /// DGPS' ID 106 | /// 107 | /// ID of DGPS station used in differential correction. 108 | /// 109 | /// - Warning: 110 | /// - This attribute may not be useful. 111 | /// - This is carried over from GPX schema, to be compliant with the schema. 112 | var DGPSid: Int? { get set } 113 | 114 | 115 | /// Latitude of current point 116 | /// 117 | /// - Latitude value should be within range of **-90 to 90** 118 | /// - Should be in unit **degrees** (º) 119 | /// - Should conform to WGS 84 datum. 120 | /// 121 | var latitude: Double? { get set } 122 | 123 | /// Longitude of current point 124 | /// 125 | /// - Longitude value should be within range of **-180 to 180** 126 | /// - Should be in unit **degrees** (º) 127 | /// - Should conform to WGS 84 datum. 128 | /// 129 | var longitude: Double? { get set } 130 | 131 | } 132 | 133 | extension GPXWaypointProtocol { 134 | func convert() -> T { 135 | let wpt = T() 136 | wpt.elevation = elevation 137 | wpt.time = time 138 | wpt.magneticVariation = magneticVariation 139 | wpt.geoidHeight = geoidHeight 140 | wpt.name = name 141 | wpt.comment = comment 142 | wpt.desc = desc 143 | wpt.source = source 144 | wpt.symbol = symbol 145 | wpt.type = type 146 | wpt.fix = fix 147 | wpt.satellites = satellites 148 | wpt.horizontalDilution = horizontalDilution 149 | wpt.verticalDilution = verticalDilution 150 | wpt.positionDilution = positionDilution 151 | wpt.ageofDGPSData = ageofDGPSData 152 | wpt.DGPSid = DGPSid 153 | wpt.latitude = latitude 154 | wpt.longitude = longitude 155 | return wpt 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /Classes/NSMutableString+XML.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableString+XML.swift 3 | // CoreGPX 4 | // 5 | // Created by Vincent Neo on 6/6/19. 6 | // 7 | 8 | import Foundation 9 | 10 | /** 11 | To ensure that all appended tags are appended with the right formats. 12 | 13 | For both open and close tags. 14 | */ 15 | extension NSMutableString { 16 | 17 | /// Appends an open tag 18 | /// 19 | /// This function will append an open tag with the right format. 20 | /// 21 | /// **Format it will append to:** 22 | /// 23 | /// "%@<%@%@>\r\n" 24 | /// //indentations \r\n 25 | func appendOpenTag(indentation: NSMutableString, tag: String, attribute: NSMutableString) { 26 | self.append("\(indentation)<\(tag)\(attribute)>\r\n") 27 | } 28 | 29 | /// Appends a close tag 30 | /// 31 | /// This function will append an close tag with the right format. 32 | /// Not currently used, but included, for ease of use when needed. 33 | /// 34 | /// **Format it will append to:** 35 | /// 36 | /// "%@\r\n" 37 | /// //indentations \r\n 38 | func appendCloseTag(indentation: NSMutableString, tag: String) { 39 | self.append("\(indentation)\r\n") 40 | } 41 | 42 | /// Appends attributes to a tag 43 | func appendAttributeTag(_ attribution: String, value: CVarArg) { 44 | self.append(" \(attribution)=\"\(value)\"") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /CoreGPX title.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/CoreGPX title.png -------------------------------------------------------------------------------- /CoreGPX.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint GPXKit.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see https://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'CoreGPX' 11 | s.version = '0.9.2' 12 | s.summary = 'A library for reading and creation of GPX location log files.' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | CoreGPX creates and parses GPX files with ease, written in Swift, supports iOS, macOS and watchOS. Replaces iOS-GPX-Framework. 22 | DESC 23 | 24 | s.homepage = 'https://github.com/vincentneo/CoreGPX' 25 | s.license = { :type => 'MIT', :file => 'LICENSE' } 26 | s.author = { 'vincentneo' => '23420208+vincentneo@users.noreply.github.com' } 27 | s.source = { :git => 'https://github.com/vincentneo/CoreGPX.git', :tag => s.version.to_s } 28 | s.social_media_url = 'https://twitter.com/ivincentneo' 29 | 30 | s.swift_versions = ['4.2', '5.0'] 31 | s.ios.deployment_target = '11.0' 32 | s.osx.deployment_target = '10.13' 33 | s.watchos.deployment_target = '4.0' 34 | 35 | s.source_files = 'Classes' 36 | 37 | end 38 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/xcshareddata/xcbaselines/607FACE41AFB9204008FA782.xcbaseline/8290985D-7441-4CE0-9256-ED960E0DA457.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | CoreGPX_Tests 8 | 9 | testPerformanceForParsingWithData() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.205 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | testPerformanceForParsingWithURL() 20 | 21 | com.apple.XCTPerformanceMetric_WallClockTime 22 | 23 | baselineAverage 24 | 0.232 25 | baselineIntegrationDisplayName 26 | Local Baseline 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/xcshareddata/xcbaselines/607FACE41AFB9204008FA782.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 8290985D-7441-4CE0-9256-ED960E0DA457 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 100 13 | cpuCount 14 | 1 15 | cpuKind 16 | Intel Core i5 17 | cpuSpeedInMHz 18 | 2700 19 | logicalCPUCoresPerPackage 20 | 4 21 | modelCode 22 | MacBookPro12,1 23 | physicalCPUCoresPerPackage 24 | 2 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | x86_64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone8,4 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/xcshareddata/xcschemes/CoreGPX Tests.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 14 | 15 | 17 | 23 | 24 | 25 | 26 | 27 | 37 | 39 | 45 | 46 | 47 | 48 | 54 | 55 | 61 | 62 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcodeproj/xcshareddata/xcschemes/CoreGPX-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 76 | 78 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/CoreGPX.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/GPXKit/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // GPXKit 4 | // 5 | // Created by vincentneo on 11/05/2018. 6 | // Copyright (c) 2018 vincentneo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 24 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Example/GPXKit/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/GPXKit/CoreGPX Playground.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import CoreGPX 3 | 4 | // Example: Generate GPX string 5 | 6 | let root = GPXRoot(creator: "CoreGPX Playground") // Init with a creator name. 7 | 8 | let track = GPXTrack() 9 | let firstSegment = GPXTrackSegment() 10 | 11 | let metadata = GPXMetadata() 12 | 13 | metadata.time = Date() 14 | metadata.copyright = GPXCopyright(author: "Vincent Neo") 15 | metadata.desc = "This GPX File is created to facilitate the understanding of CoreGPX library's usage!" 16 | 17 | root.metadata = metadata 18 | 19 | let firstTrkPt = GPXTrackPoint(latitude: 1.2345, longitude: 2.3456) 20 | let secondTrkPt = GPXTrackPoint(latitude: 0.294, longitude: 38.019) 21 | 22 | firstTrkPt.elevation = 31.92492 23 | firstTrkPt.extensions = GPXExtensions() 24 | firstTrkPt.extensions?.append(at: "customproperties", contents: ["Location" : "Sembawang, Singapore", "CompassDegree" : "120"]) 25 | 26 | 27 | firstSegment.add(trackpoints: [firstTrkPt, secondTrkPt]) 28 | track.add(trackSegment: firstSegment) 29 | 30 | root.add(track: track) 31 | 32 | 33 | // Dealing with extensions 34 | let rootExtensions = GPXExtensions() 35 | let child = GPXExtensionsElement(name: "trkExtensions") 36 | let speed = GPXExtensionsElement(name: "speed") 37 | speed.text = "50.49" 38 | let airQuality = GPXExtensionsElement(name: "AirQuality") 39 | airQuality.text = "82" 40 | child.attributes = ["purpose" : "test"] 41 | 42 | child.children.append(speed) 43 | child.children.append(airQuality) 44 | 45 | rootExtensions.children.append(child) 46 | 47 | root.extensions = rootExtensions 48 | 49 | print("Completed GPXRoot: \(root.gpx())") 50 | 51 | -------------------------------------------------------------------------------- /Example/GPXKit/CoreGPX Playground.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Example/GPXKit/CoreGPX Playground.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Example/GPXKit/CreationViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CreationViewController.swift 3 | // CoreGPX_Example 4 | // 5 | // Created by Vincent on 22/1/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | // This is quite a unreal and bad example; will replace this in near future. 10 | 11 | import UIKit 12 | import CoreGPX 13 | 14 | class CreationViewController: UIViewController, UITextFieldDelegate { 15 | 16 | @IBOutlet weak var typeSwitch: UISegmentedControl! 17 | @IBOutlet weak var elevationField: UITextField! 18 | @IBOutlet weak var latitudeField: UITextField! 19 | @IBOutlet weak var longitudeField: UITextField! 20 | @IBOutlet weak var dateField: UITextField! 21 | 22 | var typeSwitchIndex = Int() 23 | var waypoints = [GPXWaypoint]() 24 | var trackpoints = [GPXTrackPoint]() 25 | var gpxString = String() 26 | 27 | @IBAction func insertElementButton(_ sender: Any) { 28 | switch typeSwitchIndex { 29 | case 0: 30 | let latitude = Double(latitudeField.text ?? "") 31 | let longitude = Double(longitudeField.text ?? "") 32 | let elevation = value(from: elevationField.text) 33 | let waypoint = GPXWaypoint(latitude: latitude ?? 0, longitude: longitude ?? 0) 34 | waypoint.elevation = elevation ?? 0 35 | waypoint.time = Date() 36 | waypoints.append(waypoint) 37 | 38 | //clear fields 39 | elevationField.text = "" 40 | latitudeField.text = "" 41 | longitudeField.text = "" 42 | case 1: 43 | let latitude = Double(latitudeField.text ?? "") 44 | let longitude = Double(longitudeField.text ?? "") 45 | let elevation = value(from: elevationField.text) 46 | let trackpoint = GPXTrackPoint(latitude: latitude ?? 0, longitude: longitude ?? 0) 47 | trackpoint.elevation = elevation ?? 0 48 | trackpoint.time = Date() 49 | 50 | /* example for adding an extension in here. 51 | let dictionary = [["ext1" : "302", "ext1:speed" : "80"], ["ext2" : "03f"]] 52 | trackpoint.extensions = GPXExtensions(custom: dictionary) 53 | trackpoint.extensions?.insertParentTag(at: 0, tagName: "extension for 1") 54 | trackpoint.extensions?.insertParentTag(at: 1, tagName: "extension for 2") 55 | */ 56 | 57 | trackpoints.append(trackpoint) 58 | 59 | //clear fields 60 | elevationField.text = "" 61 | latitudeField.text = "" 62 | longitudeField.text = "" 63 | default: () 64 | 65 | } 66 | } 67 | 68 | @IBAction func outputFileButton(_ sender: Any) { 69 | let root = GPXRoot(creator: "example app, output as String") 70 | 71 | let track = GPXTrack() 72 | let trackseg = GPXTrackSegment() 73 | trackseg.add(trackpoints: trackpoints) 74 | track.add(trackSegment: trackseg) 75 | 76 | 77 | root.add(waypoints: waypoints) 78 | root.add(track: track) 79 | 80 | self.gpxString = root.gpx() 81 | print(gpxString) 82 | } 83 | 84 | @IBAction func outputAsFileButton(_ sender: Any) { 85 | let root = GPXRoot(creator: "example app, output as File") 86 | 87 | let track = GPXTrack() 88 | let trackseg = GPXTrackSegment() 89 | trackseg.add(trackpoints: trackpoints) 90 | track.add(trackSegment: trackseg) 91 | 92 | 93 | root.add(waypoints: waypoints) 94 | root.add(track: track) 95 | 96 | let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0] as URL 97 | do { 98 | try root.outputToFile(saveAt: url, fileName: "test") 99 | } 100 | catch { 101 | print(error) 102 | } 103 | let file = url.appendingPathComponent("test.gpx") 104 | 105 | let activityViewController = UIActivityViewController(activityItems: [file], applicationActivities: nil) 106 | activityViewController.popoverPresentationController?.sourceView = self.view 107 | 108 | // present the view controller 109 | self.present(activityViewController, animated: true, completion: nil) 110 | } 111 | 112 | @IBAction func segmentHasChanged(_ sender: UISegmentedControl) { 113 | typeSwitchIndex = sender.selectedSegmentIndex 114 | } 115 | 116 | override func viewDidLoad() { 117 | super.viewDidLoad() 118 | dateField.text = "current date" 119 | dateField.isEnabled = false 120 | 121 | latitudeField.keyboardType = .decimalPad 122 | longitudeField.keyboardType = .decimalPad 123 | elevationField.keyboardType = .decimalPad 124 | 125 | // Do any additional setup after loading the view. 126 | } 127 | 128 | func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { 129 | let invalidCharacters = CharacterSet(charactersIn: "0123456789.").inverted 130 | return string.rangeOfCharacter(from: invalidCharacters) == nil 131 | } 132 | 133 | func value(from string: String?) -> Double? { 134 | if let validStr = string { 135 | return Double(validStr) 136 | } 137 | return nil 138 | } 139 | 140 | 141 | 142 | /* 143 | // MARK: - Navigation 144 | 145 | // In a storyboard-based application, you will often want to do a little preparation before navigation 146 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 147 | // Get the new view controller using segue.destination. 148 | // Pass the selected object to the new view controller. 149 | } 150 | */ 151 | 152 | } 153 | -------------------------------------------------------------------------------- /Example/GPXKit/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "size" : "1024x1024", 46 | "scale" : "1x" 47 | } 48 | ], 49 | "info" : { 50 | "version" : 1, 51 | "author" : "xcode" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Example/GPXKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | UIInterfaceOrientationPortraitUpsideDown 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example/GPXKit/ParseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ParseViewController.swift 3 | // CoreGPX_Example 4 | // 5 | // Created by Vincent on 12/1/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreGPX 11 | 12 | class ParseViewController: UIViewController, UITableViewDelegate, UITableViewDataSource { 13 | 14 | @IBOutlet weak var inputTextField: UITextField! 15 | @IBOutlet weak var tableView: UITableView! 16 | 17 | var tracks = [GPXTrack]() 18 | var waypoints = [GPXWaypoint]() 19 | var waypoint = GPXWaypoint() 20 | var trackpoint = GPXTrackPoint() 21 | 22 | override func viewDidLoad() { 23 | super.viewDidLoad() 24 | inputTextField.autocorrectionType = .no 25 | trackpoint = GPXTrackPoint(latitude: 2, longitude: 0) 26 | // Do any additional setup after loading the view. 27 | } 28 | 29 | @IBAction func onPress(_ sender: Any) { 30 | 31 | let input = inputTextField.text 32 | 33 | if input != nil { 34 | if let inputURL = URL(string: input!) { 35 | //guard let gpx = GPXParser(withURL: inputURL)?.parsedData() else { return } 36 | guard let gpx = GPXParser(withURL: inputURL)?.parsedData() else { return } 37 | self.tracks = gpx.tracks 38 | self.waypoints = gpx.waypoints 39 | self.tableView.reloadData() 40 | //print("gpx creator: \(gpx.creator ?? "") ver: \(gpx.version ?? "")") 41 | } 42 | } 43 | } 44 | 45 | func trackpointsCount() -> Int { 46 | var totalCount: Int = 0 47 | for track in self.tracks { 48 | for trackSegment in track.segments { 49 | totalCount += trackSegment.points.count 50 | } 51 | } 52 | return totalCount 53 | } 54 | func waypointsCount() -> Int { 55 | var totalCount: Int = 0 56 | totalCount = self.waypoints.count 57 | return totalCount 58 | } 59 | 60 | func numberOfSections(in tableView: UITableView) -> Int { 61 | return 2 62 | } 63 | 64 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 65 | if section == 0 { 66 | return waypointsCount() 67 | } 68 | else { 69 | return trackpointsCount() 70 | } 71 | } 72 | 73 | func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { 74 | if section == 0 { 75 | return "Waypoints" 76 | } 77 | else { 78 | return "Trackpoints" 79 | } 80 | } 81 | 82 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 83 | let cell = UITableViewCell(style: .subtitle, reuseIdentifier: "cell") 84 | if indexPath.section == 0 { //waypoints 85 | 86 | var coordinates = [String]() 87 | var subtitles = [String]() 88 | 89 | for waypoint in self.waypoints { 90 | 91 | coordinates.append("Lat=\(waypoint.latitude ?? 0), Lon=\(waypoint.longitude ?? 0)") 92 | subtitles.append("Date:\(waypoint.time ?? Date()), Ele:\(waypoint.elevation ?? 0)") 93 | 94 | } 95 | 96 | cell.textLabel?.text = coordinates[indexPath.row] 97 | cell.detailTextLabel?.text = subtitles[indexPath.row] 98 | } 99 | 100 | else { 101 | 102 | var coordinates = [String]() 103 | var subtitles = [String]() 104 | 105 | for track in self.tracks { 106 | for tracksegment in track.segments { 107 | for trackpoint in tracksegment.points { 108 | coordinates.append("Lat=\(trackpoint.latitude!), Lon=\(trackpoint.longitude!)") 109 | subtitles.append("Date:\(trackpoint.time ?? Date()), Ele:\(trackpoint.elevation ?? 0), Ext:\(trackpoint.extensions?.get(from: nil) ?? [String:String]())") 110 | 111 | } 112 | } 113 | } 114 | cell.textLabel?.text = coordinates[indexPath.row] 115 | cell.detailTextLabel?.text = subtitles[indexPath.row] 116 | } 117 | return cell 118 | } 119 | 120 | /* 121 | // MARK: - Navigation 122 | 123 | // In a storyboard-based application, you will often want to do a little preparation before navigation 124 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 125 | // Get the new view controller using segue.destination. 126 | // Pass the selected object to the new view controller. 127 | } 128 | */ 129 | 130 | } 131 | -------------------------------------------------------------------------------- /Example/GPXKit/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // GPXKit 4 | // 5 | // Created by vincentneo on 11/05/2018. 6 | // Copyright (c) 2018 vincentneo. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import CoreGPX 11 | 12 | class ViewController: UIViewController { 13 | 14 | var tracks = [GPXTrack]() 15 | var waypoints = [GPXWaypoint]() 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | // Example of parsing a GPX file from a sample URL 21 | 22 | let urlString : String = "https://raw.githubusercontent.com/gps-touring/sample-gpx/master/BrittanyJura/Courgenay_Ballon-DAlsace.gpx" 23 | let url: URL = URL(string: urlString)! 24 | 25 | 26 | // GPXRoot object that contains all the data parsed from GPXParser. 27 | guard let gpx = GPXParser(withURL: url)?.parsedData() else { return } 28 | 29 | self.tracks = gpx.tracks 30 | self.waypoints = gpx.waypoints 31 | 32 | // This example prints all the waypoints's latitude, longitude and date from the GPX file. 33 | for waypoint in self.waypoints { 34 | print("waypoint-latitude: \(waypoint.latitude ?? 0)") 35 | print("waypoint-longitude: \(waypoint.longitude ?? 0)") 36 | print("waypoint-date: \(waypoint.time ?? Date())") 37 | } 38 | 39 | // This example prints all the trackpoint's latitude, longitude and date from the GPX file. 40 | for track in self.tracks { 41 | for tracksegment in track.segments { 42 | for trackpoint in tracksegment.points { 43 | print("trackpoint-latitude: \(trackpoint.latitude ?? 0)") 44 | print("trackpoint-longitude: \(trackpoint.longitude ?? 0)") 45 | print("trackpoint-date: \(trackpoint.time ?? Date())") 46 | } 47 | } 48 | } 49 | 50 | } 51 | 52 | override func didReceiveMemoryWarning() { 53 | super.didReceiveMemoryWarning() 54 | // Dispose of any resources that can be recreated. 55 | } 56 | 57 | } 58 | 59 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'CoreGPX_Example' do 4 | pod 'CoreGPX', :path => '../' 5 | 6 | target 'CoreGPX_Tests' do 7 | inherit! :search_paths 8 | 9 | 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - CoreGPX (0.3.5) 3 | 4 | DEPENDENCIES: 5 | - CoreGPX (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | CoreGPX: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | CoreGPX: b1071b6631f945da592e6117c8778c8076debe26 13 | 14 | PODFILE CHECKSUM: 9e9439f92bdc308d731e2bf95a97be91265a86df 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-macOS/CoreGPX_macOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // CoreGPX_macOS.h 3 | // CoreGPX-macOS 4 | // 5 | // Created by Vincent on 4/2/19. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for CoreGPX_macOS. 11 | //FOUNDATION_EXPORT double CoreGPX_macOSVersionNumber; 12 | FOUNDATION_EXPORT double CoreGPXVersionNumber; 13 | 14 | //! Project version string for CoreGPX_macOS. 15 | //FOUNDATION_EXPORT const unsigned char CoreGPX_macOSVersionString[]; 16 | FOUNDATION_EXPORT const unsigned char CoreGPXVersionString[]; 17 | 18 | // In this header, you should import all the public headers of your framework using statements like #import 19 | 20 | 21 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-macOSTests/CoreGPX_macOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreGPX_macOSTests.swift 3 | // CoreGPX-macOSTests 4 | // 5 | // Created by Vincent on 4/2/19. 6 | // 7 | 8 | import XCTest 9 | @testable import CoreGPX_macOS 10 | 11 | class CoreGPX_macOSTests: XCTestCase { 12 | 13 | override func setUp() { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDown() { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-macOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-watchOS/CoreGPX_watchOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // CoreGPX_watchOS.h 3 | // CoreGPX-watchOS 4 | // 5 | // Created by Vincent on 4/2/19. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for CoreGPX_watchOS. 11 | //FOUNDATION_EXPORT double CoreGPX_watchOSVersionNumber; 12 | FOUNDATION_EXPORT double CoreGPXVersionNumber; 13 | 14 | //! Project version string for CoreGPX_watchOS. 15 | //FOUNDATION_EXPORT const unsigned char CoreGPX_watchOSVersionString[]; 16 | FOUNDATION_EXPORT const unsigned char CoreGPXVersionString[]; 17 | 18 | // In this header, you should import all the public headers of your framework using statements like #import 19 | 20 | 21 | -------------------------------------------------------------------------------- /Example/Pods/Alternate Targets/CoreGPX-watchOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleVersion 20 | $(CURRENT_PROJECT_VERSION) 21 | 22 | 23 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/CoreGPX.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CoreGPX", 3 | "version": "0.3.5", 4 | "summary": "A library for reading and creation of GPX location log files.", 5 | "description": "GPXKit is designed to replace the aging, and abandoned project, iOS-GPX-Framework. This pod will be able to help developers parse and create GPX files more easily. More features will be added to the future.", 6 | "homepage": "https://github.com/vincentneo/CoreGPX", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "vincentneo": "23420208+vincentneo@users.noreply.github.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/vincentneo/CoreGPX.git", 16 | "tag": "0.3.5" 17 | }, 18 | "social_media_url": "https://twitter.com/ivincentneo", 19 | "platforms": { 20 | "ios": "8.0" 21 | }, 22 | "source_files": "Classes" 23 | } 24 | -------------------------------------------------------------------------------- /Example/Pods/Local Podspecs/GPXKit.podspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "GPXKit", 3 | "version": "0.3.0", 4 | "summary": "A library for reading and creation of GPX location log files.", 5 | "description": "TODO: Add long description of the pod here.", 6 | "homepage": "https://github.com/vincentneo/GPXKit", 7 | "license": { 8 | "type": "MIT", 9 | "file": "LICENSE" 10 | }, 11 | "authors": { 12 | "vincentneo": "23420208+vincentneo@users.noreply.github.com" 13 | }, 14 | "source": { 15 | "git": "https://github.com/vincentneo/GPXKit.git", 16 | "tag": "0.3.0" 17 | }, 18 | "platforms": { 19 | "ios": "8.0" 20 | }, 21 | "source_files": "Classes" 22 | } 23 | -------------------------------------------------------------------------------- /Example/Pods/Manifest.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - CoreGPX (0.3.5) 3 | 4 | DEPENDENCIES: 5 | - CoreGPX (from `../`) 6 | 7 | EXTERNAL SOURCES: 8 | CoreGPX: 9 | :path: "../" 10 | 11 | SPEC CHECKSUMS: 12 | CoreGPX: b1071b6631f945da592e6117c8778c8076debe26 13 | 14 | PODFILE CHECKSUM: 9e9439f92bdc308d731e2bf95a97be91265a86df 15 | 16 | COCOAPODS: 1.5.3 17 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/CoreGPX-macOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 42 | 48 | 49 | 50 | 51 | 52 | 62 | 63 | 69 | 70 | 71 | 72 | 78 | 79 | 85 | 86 | 87 | 88 | 90 | 91 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/CoreGPX-watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 66 | 67 | 68 | 69 | 71 | 72 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /Example/Pods/Pods.xcodeproj/xcshareddata/xcschemes/CoreGPX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 44 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 63 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/CoreGPX-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_CoreGPX : NSObject 3 | @end 4 | @implementation PodsDummy_CoreGPX 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/CoreGPX-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/CoreGPX-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double CoreGPXVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char CoreGPXVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/CoreGPX.modulemap: -------------------------------------------------------------------------------- 1 | framework module CoreGPX { 2 | umbrella header "CoreGPX-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/CoreGPX.xcconfig: -------------------------------------------------------------------------------- 1 | CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 4 | PODS_BUILD_DIR = ${BUILD_DIR} 5 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 6 | PODS_ROOT = ${SRCROOT} 7 | PODS_TARGET_SRCROOT = ${PODS_ROOT}/../.. 8 | PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} 9 | SKIP_INSTALL = YES 10 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/CoreGPX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | $(MARKETING_VERSION) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | 4 | ## CoreGPX 5 | 6 | MIT License 7 | 8 | Copyright (c) 2018 Vincent Neo 9 | 10 | Permission is hereby granted, free of charge, to any person obtaining a copy 11 | of this software and associated documentation files (the "Software"), to deal 12 | in the Software without restriction, including without limitation the rights 13 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the Software is 15 | furnished to do so, subject to the following conditions: 16 | 17 | The above copyright notice and this permission notice shall be included in all 18 | copies or substantial portions of the Software. 19 | 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | SOFTWARE. 27 | 28 | Generated by CocoaPods - https://cocoapods.org 29 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | MIT License 18 | 19 | Copyright (c) 2018 Vincent Neo 20 | 21 | Permission is hereby granted, free of charge, to any person obtaining a copy 22 | of this software and associated documentation files (the "Software"), to deal 23 | in the Software without restriction, including without limitation the rights 24 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 25 | copies of the Software, and to permit persons to whom the Software is 26 | furnished to do so, subject to the following conditions: 27 | 28 | The above copyright notice and this permission notice shall be included in all 29 | copies or substantial portions of the Software. 30 | 31 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 32 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 33 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 34 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 35 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 36 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | 39 | License 40 | MIT 41 | Title 42 | CoreGPX 43 | Type 44 | PSGroupSpecifier 45 | 46 | 47 | FooterText 48 | Generated by CocoaPods - https://cocoapods.org 49 | Title 50 | 51 | Type 52 | PSGroupSpecifier 53 | 54 | 55 | StringsTable 56 | Acknowledgements 57 | Title 58 | Acknowledgements 59 | 60 | 61 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_CoreGPX_Example : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_CoreGPX_Example 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | 145 | if [[ "$CONFIGURATION" == "Debug" ]]; then 146 | install_framework "${BUILT_PRODUCTS_DIR}/CoreGPX/CoreGPX.framework" 147 | fi 148 | if [[ "$CONFIGURATION" == "Release" ]]; then 149 | install_framework "${BUILT_PRODUCTS_DIR}/CoreGPX/CoreGPX.framework" 150 | fi 151 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 152 | wait 153 | fi 154 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_CoreGPX_ExampleVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_CoreGPX_ExampleVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example.debug.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX/CoreGPX.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGPX" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_CoreGPX_Example { 2 | umbrella header "Pods-CoreGPX_Example-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Example/Pods-CoreGPX_Example.release.xcconfig: -------------------------------------------------------------------------------- 1 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES 2 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX" 3 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 4 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 5 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX/CoreGPX.framework/Headers" 6 | OTHER_LDFLAGS = $(inherited) -framework "CoreGPX" 7 | OTHER_SWIFT_FLAGS = $(inherited) "-D" "COCOAPODS" 8 | PODS_BUILD_DIR = ${BUILD_DIR} 9 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 10 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 11 | PODS_ROOT = ${SRCROOT}/Pods 12 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIdentifier 10 | ${PRODUCT_BUNDLE_IDENTIFIER} 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | ${PRODUCT_NAME} 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | ${CURRENT_PROJECT_VERSION} 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-acknowledgements.markdown: -------------------------------------------------------------------------------- 1 | # Acknowledgements 2 | This application makes use of the following third party libraries: 3 | Generated by CocoaPods - https://cocoapods.org 4 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-acknowledgements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreferenceSpecifiers 6 | 7 | 8 | FooterText 9 | This application makes use of the following third party libraries: 10 | Title 11 | Acknowledgements 12 | Type 13 | PSGroupSpecifier 14 | 15 | 16 | FooterText 17 | Generated by CocoaPods - https://cocoapods.org 18 | Title 19 | 20 | Type 21 | PSGroupSpecifier 22 | 23 | 24 | StringsTable 25 | Acknowledgements 26 | Title 27 | Acknowledgements 28 | 29 | 30 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_CoreGPX_Tests : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_CoreGPX_Tests 5 | @end 6 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-frameworks.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then 7 | # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # frameworks to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 13 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 14 | 15 | COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" 16 | SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" 17 | 18 | # Used as a return value for each invocation of `strip_invalid_archs` function. 19 | STRIP_BINARY_RETVAL=0 20 | 21 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 22 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 23 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 24 | 25 | # Copies and strips a vendored framework 26 | install_framework() 27 | { 28 | if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then 29 | local source="${BUILT_PRODUCTS_DIR}/$1" 30 | elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then 31 | local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" 32 | elif [ -r "$1" ]; then 33 | local source="$1" 34 | fi 35 | 36 | local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 37 | 38 | if [ -L "${source}" ]; then 39 | echo "Symlinked..." 40 | source="$(readlink "${source}")" 41 | fi 42 | 43 | # Use filter instead of exclude so missing patterns don't throw errors. 44 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" 45 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" 46 | 47 | local basename 48 | basename="$(basename -s .framework "$1")" 49 | binary="${destination}/${basename}.framework/${basename}" 50 | if ! [ -r "$binary" ]; then 51 | binary="${destination}/${basename}" 52 | fi 53 | 54 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 55 | if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then 56 | strip_invalid_archs "$binary" 57 | fi 58 | 59 | # Resign the code if required by the build settings to avoid unstable apps 60 | code_sign_if_enabled "${destination}/$(basename "$1")" 61 | 62 | # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. 63 | if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then 64 | local swift_runtime_libs 65 | swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u && exit ${PIPESTATUS[0]}) 66 | for lib in $swift_runtime_libs; do 67 | echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" 68 | rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" 69 | code_sign_if_enabled "${destination}/${lib}" 70 | done 71 | fi 72 | } 73 | 74 | # Copies and strips a vendored dSYM 75 | install_dsym() { 76 | local source="$1" 77 | if [ -r "$source" ]; then 78 | # Copy the dSYM into a the targets temp dir. 79 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" 80 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" 81 | 82 | local basename 83 | basename="$(basename -s .framework.dSYM "$source")" 84 | binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" 85 | 86 | # Strip invalid architectures so "fat" simulator / device frameworks work on device 87 | if [[ "$(file "$binary")" == *"Mach-O dSYM companion"* ]]; then 88 | strip_invalid_archs "$binary" 89 | fi 90 | 91 | if [[ $STRIP_BINARY_RETVAL == 1 ]]; then 92 | # Move the stripped file into its final destination. 93 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" 94 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" 95 | else 96 | # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. 97 | touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" 98 | fi 99 | fi 100 | } 101 | 102 | # Signs a framework with the provided identity 103 | code_sign_if_enabled() { 104 | if [ -n "${EXPANDED_CODE_SIGN_IDENTITY}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then 105 | # Use the current code_sign_identitiy 106 | echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" 107 | local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" 108 | 109 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 110 | code_sign_cmd="$code_sign_cmd &" 111 | fi 112 | echo "$code_sign_cmd" 113 | eval "$code_sign_cmd" 114 | fi 115 | } 116 | 117 | # Strip invalid architectures 118 | strip_invalid_archs() { 119 | binary="$1" 120 | # Get architectures for current target binary 121 | binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" 122 | # Intersect them with the architectures we are building for 123 | intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" 124 | # If there are no archs supported by this binary then warn the user 125 | if [[ -z "$intersected_archs" ]]; then 126 | echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." 127 | STRIP_BINARY_RETVAL=0 128 | return 129 | fi 130 | stripped="" 131 | for arch in $binary_archs; do 132 | if ! [[ "${ARCHS}" == *"$arch"* ]]; then 133 | # Strip non-valid architectures in-place 134 | lipo -remove "$arch" -output "$binary" "$binary" || exit 1 135 | stripped="$stripped $arch" 136 | fi 137 | done 138 | if [[ "$stripped" ]]; then 139 | echo "Stripped $binary of architectures:$stripped" 140 | fi 141 | STRIP_BINARY_RETVAL=1 142 | } 143 | 144 | if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then 145 | wait 146 | fi 147 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | set -u 4 | set -o pipefail 5 | 6 | if [ -z ${UNLOCALIZED_RESOURCES_FOLDER_PATH+x} ]; then 7 | # If UNLOCALIZED_RESOURCES_FOLDER_PATH is not set, then there's nowhere for us to copy 8 | # resources to, so exit 0 (signalling the script phase was successful). 9 | exit 0 10 | fi 11 | 12 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 13 | 14 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 15 | > "$RESOURCES_TO_COPY" 16 | 17 | XCASSET_FILES=() 18 | 19 | # This protects against multiple targets copying the same framework dependency at the same time. The solution 20 | # was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html 21 | RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") 22 | 23 | case "${TARGETED_DEVICE_FAMILY:-}" in 24 | 1,2) 25 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 26 | ;; 27 | 1) 28 | TARGET_DEVICE_ARGS="--target-device iphone" 29 | ;; 30 | 2) 31 | TARGET_DEVICE_ARGS="--target-device ipad" 32 | ;; 33 | 3) 34 | TARGET_DEVICE_ARGS="--target-device tv" 35 | ;; 36 | 4) 37 | TARGET_DEVICE_ARGS="--target-device watch" 38 | ;; 39 | *) 40 | TARGET_DEVICE_ARGS="--target-device mac" 41 | ;; 42 | esac 43 | 44 | install_resource() 45 | { 46 | if [[ "$1" = /* ]] ; then 47 | RESOURCE_PATH="$1" 48 | else 49 | RESOURCE_PATH="${PODS_ROOT}/$1" 50 | fi 51 | if [[ ! -e "$RESOURCE_PATH" ]] ; then 52 | cat << EOM 53 | error: Resource "$RESOURCE_PATH" not found. Run 'pod install' to update the copy resources script. 54 | EOM 55 | exit 1 56 | fi 57 | case $RESOURCE_PATH in 58 | *.storyboard) 59 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 60 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .storyboard`.storyboardc" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 61 | ;; 62 | *.xib) 63 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile ${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib $RESOURCE_PATH --sdk ${SDKROOT} ${TARGET_DEVICE_ARGS}" || true 64 | ibtool --reference-external-strings-file --errors --warnings --notices --minimum-deployment-target ${!DEPLOYMENT_TARGET_SETTING_NAME} --output-format human-readable-text --compile "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$RESOURCE_PATH\" .xib`.nib" "$RESOURCE_PATH" --sdk "${SDKROOT}" ${TARGET_DEVICE_ARGS} 65 | ;; 66 | *.framework) 67 | echo "mkdir -p ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 68 | mkdir -p "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 69 | echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" $RESOURCE_PATH ${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" || true 70 | rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 71 | ;; 72 | *.xcdatamodel) 73 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH"`.mom\"" || true 74 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodel`.mom" 75 | ;; 76 | *.xcdatamodeld) 77 | echo "xcrun momc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd\"" || true 78 | xcrun momc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcdatamodeld`.momd" 79 | ;; 80 | *.xcmappingmodel) 81 | echo "xcrun mapc \"$RESOURCE_PATH\" \"${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm\"" || true 82 | xcrun mapc "$RESOURCE_PATH" "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$RESOURCE_PATH" .xcmappingmodel`.cdm" 83 | ;; 84 | *.xcassets) 85 | ABSOLUTE_XCASSET_FILE="$RESOURCE_PATH" 86 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 87 | ;; 88 | *) 89 | echo "$RESOURCE_PATH" || true 90 | echo "$RESOURCE_PATH" >> "$RESOURCES_TO_COPY" 91 | ;; 92 | esac 93 | } 94 | 95 | mkdir -p "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 96 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 97 | if [[ "${ACTION}" == "install" ]] && [[ "${SKIP_INSTALL}" == "NO" ]]; then 98 | mkdir -p "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 99 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 100 | fi 101 | rm -f "$RESOURCES_TO_COPY" 102 | 103 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "${XCASSET_FILES:-}" ] 104 | then 105 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 106 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 107 | while read line; do 108 | if [[ $line != "${PODS_ROOT}*" ]]; then 109 | XCASSET_FILES+=("$line") 110 | fi 111 | done <<<"$OTHER_XCASSETS" 112 | 113 | if [ -z ${ASSETCATALOG_COMPILER_APPICON_NAME+x} ]; then 114 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 115 | else 116 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${!DEPLOYMENT_TARGET_SETTING_NAME}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" --app-icon "${ASSETCATALOG_COMPILER_APPICON_NAME}" --output-partial-info-plist "${TARGET_TEMP_DIR}/assetcatalog_generated_info_cocoapods.plist" 117 | fi 118 | fi 119 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests-umbrella.h: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #else 4 | #ifndef FOUNDATION_EXPORT 5 | #if defined(__cplusplus) 6 | #define FOUNDATION_EXPORT extern "C" 7 | #else 8 | #define FOUNDATION_EXPORT extern 9 | #endif 10 | #endif 11 | #endif 12 | 13 | 14 | FOUNDATION_EXPORT double Pods_CoreGPX_TestsVersionNumber; 15 | FOUNDATION_EXPORT const unsigned char Pods_CoreGPX_TestsVersionString[]; 16 | 17 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests.debug.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX/CoreGPX.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests.modulemap: -------------------------------------------------------------------------------- 1 | framework module Pods_CoreGPX_Tests { 2 | umbrella header "Pods-CoreGPX_Tests-umbrella.h" 3 | 4 | export * 5 | module * { export * } 6 | } 7 | -------------------------------------------------------------------------------- /Example/Pods/Target Support Files/Pods-CoreGPX_Tests/Pods-CoreGPX_Tests.release.xcconfig: -------------------------------------------------------------------------------- 1 | FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' 4 | OTHER_CFLAGS = $(inherited) -iquote "${PODS_CONFIGURATION_BUILD_DIR}/CoreGPX/CoreGPX.framework/Headers" 5 | PODS_BUILD_DIR = ${BUILD_DIR} 6 | PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) 7 | PODS_PODFILE_DIR_PATH = ${SRCROOT}/. 8 | PODS_ROOT = ${SRCROOT}/Pods 9 | -------------------------------------------------------------------------------- /Example/Tests/GPXTest-DualLinks.gpx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 31.098352 7 | 8 | 9 | Vincent Site 10 | 11 | 12 | Get Sunlight 13 | 14 | 15 | 16 | 30 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Example/Tests/wptError.gpx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8:53:34 am 6 | 10 May 2019 at 8:53:34 am 7 | 8 | 9 | 10 | 11 | 27.882911682128906 12 | 13 | 14 | 15 | 27.79437255859375 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /Extras/GPX+CLLocation.swift: -------------------------------------------------------------------------------- 1 | // The following file is also licensed under the MIT license, as with the CoreGPX project. 2 | // You may read about the license here: https://raw.githubusercontent.com/vincentneo/CoreGPX/master/LICENSE 3 | // 4 | // GPX+CLLocation.swift 5 | // Extras 6 | // 7 | // Created by Vincent Neo on 19/2/21. 8 | // Provided under CoreGPX project. 9 | // 10 | 11 | import CoreGPX 12 | import CoreLocation 13 | 14 | public protocol GPXBridge { 15 | func convert(as: Point.Type) -> Point 16 | } 17 | 18 | extension GPXWaypointProtocol { 19 | public func convertToLocation() -> CLLocation? { 20 | guard let latitude = self.latitude, let longitude = self.longitude else { return nil } 21 | let coordinates = CLLocationCoordinate2D(latitude: latitude, longitude: longitude) 22 | let location = CLLocation(coordinate: coordinates, altitude: self.elevation ?? 0, horizontalAccuracy: 0, verticalAccuracy: self.elevation == nil ? -1 : 0, timestamp: self.time ?? Date()) 23 | return location 24 | } 25 | 26 | public func convertToCoordinates() -> CLLocationCoordinate2D? { 27 | guard let latitude = self.latitude, let longitude = self.longitude else { return nil } 28 | return CLLocationCoordinate2D(latitude: latitude, longitude: longitude) 29 | } 30 | } 31 | 32 | extension CLLocation: GPXBridge { 33 | 34 | public func convert(as: Point.Type) -> Point where Point : GPXWaypointProtocol { 35 | let point = Point() 36 | 37 | point.elevation = self.altitude 38 | point.time = self.timestamp 39 | point.latitude = self.coordinate.latitude 40 | point.longitude = self.coordinate.longitude 41 | 42 | return point 43 | } 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /GPXKit/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/GPXKit/Assets/.gitkeep -------------------------------------------------------------------------------- /GPXKit/Classes/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/GPXKit/Classes/.gitkeep -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Vincent Neo 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.9 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "CoreGPX", 8 | platforms: [ 9 | .iOS(.v12), .macOS(.v10_13), .watchOS(.v4), .visionOS(.v1), .tvOS(.v12) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "CoreGPX", 15 | targets: ["CoreGPX"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "CoreGPX", 26 | dependencies: [], 27 | path: "Classes"), 28 | .testTarget( 29 | name: "CoreGPXTests", 30 | dependencies: ["CoreGPX"], 31 | path: "Example/Tests"), 32 | ] 33 | ) 34 | 35 | -------------------------------------------------------------------------------- /Package@swift-5.2.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.2 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "CoreGPX", 8 | platforms: [ 9 | .iOS(.v11), .macOS(.v10_13), .watchOS(.v4) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries produced by a package, and make them visible to other packages. 13 | .library( 14 | name: "CoreGPX", 15 | targets: ["CoreGPX"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | ], 21 | targets: [ 22 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 23 | // Targets can depend on other targets in this package, and on products in packages which this package depends on. 24 | .target( 25 | name: "CoreGPX", 26 | dependencies: [], 27 | path: "Classes"), 28 | .testTarget( 29 | name: "CoreGPXTests", 30 | dependencies: ["CoreGPX"], 31 | path: "Example/Tests"), 32 | ], 33 | swiftLanguageVersions: [ .v4_2, .v5 ] 34 | ) 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 |

5 | 6 | Parse and generate GPX files easily on iOS, watchOS & macOS. 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 |

35 | 36 | ## What is CoreGPX? 37 | CoreGPX is a port of iOS-GPX-Framework to Swift language. 38 | 39 | CoreGPX currently supports all GPX tags listed in GPX v1.1 schema, along with the recent addition of GPX v1.0 support. It can generate and parse GPX compliant files on iOS, macOS and watchOS. 40 | 41 | As it makes use of `XMLParser` for parsing GPX files, CoreGPX is fully dependent on the `Foundation` API only. 42 | 43 | ## Features 44 | - [x] Successfully outputs string that can be packaged into a GPX file 45 | - [x] Parses GPX files using native XMLParser 46 | - [x] Support for iOS, macOS & watchOS 47 | - [x] Supports `Codable` in essential classes 48 | - [x] Enhanced full support for `GPXExtensions` for both parsing and creating. 49 | - [x] Lossy GPX compression. Check out [GPXCompressor](https://github.com/vincentneo/GPXCompressor) for an implementation of this new feature. 50 | - [x] **(new)** Legacy GPX support. (GPX 1.0 and below) 51 | 52 | ## Documentation 53 | 54 | CoreGPX is documented using [jazzy](https://github.com/realm/jazzy). 55 | 56 | [![Documentation Status](https://vincentneo.github.io/CoreGPX/badge.svg)](https://vincentneo.github.io/CoreGPX/index.html) 57 | 58 | You can read the documentation [here](https://vincentneo.github.io/CoreGPX/index.html), which documents most of the important features that will be used for parsing and creating of GPX files. 59 | 60 | ## Installation 61 | 62 | CoreGPX supports CocoaPods, Carthage, as well as Swift Package Manager, such that you can install it, any way you want. 63 | 64 | To install using [CocoaPods](https://cocoapods.org), simply add the following line to your Podfile: 65 | 66 | ```ruby 67 | pod 'CoreGPX' 68 | ``` 69 | 70 | CoreGPX works with [Carthage](https://github.com/Carthage/Carthage) as well, simply add the following line to your Cartfile: 71 | ```Swift 72 | github "vincentneo/CoreGPX" 73 | ``` 74 | 75 | ## How to use? 76 | Check out the [wiki page](https://github.com/vincentneo/CoreGPX/wiki) for some basic walkthroughs of how to use this library. 77 | 78 | Alternatively, you may check out the Example app, by cloning the repo, `pod install` and running the Example project. 79 | 80 | To know in-depth of how CoreGPX can be used in a true production setting, please refer to awesome projects like [iOS-Open-GPX-Tracker](https://github.com/merlos/iOS-Open-GPX-Tracker) or [Avenue GPX Viewer](https://github.com/vincentneo/Avenue-GPX-Viewer), both of which, uses CoreGPX. 81 | 82 | ## Extras 83 | Check out the Extras folder for some extra helper codes that may help you with using CoreGPX. 84 | Simply drag and drop it into your project to use. 85 | - `GPX+CLLocation.swift`: Converting `CLLocation` type to `GPXWaypoint`, `GPXTrackPoint` and more. 86 | 87 | ## Contributing 88 | Contributions to this project will be more than welcomed. Feel free to add a pull request or open an issue. 89 | If you require a feature that has yet to be available, do open an issue, describing why and what the feature could bring and how it would help you! 90 | 91 | ## Like the project? Check out these too! 92 | - [iOS-Open-GPX-Tracker](https://github.com/merlos/iOS-Open-GPX-Tracker), an awesome open-sourced GPS tracker for iOS and watchOS. 93 | - [Avenue GPX Viewer](https://github.com/vincentneo/Avenue-GPX-Viewer), a GPX file viewer, written for macOS 10.12 and above. 94 | - [LocaleComplete](https://github.com/vincentneo/LocaleComplete), a small library to make `Locale` identifier hunting more easy and straightforward. 95 | 96 | ## License 97 | CoreGPX is available under the MIT license. See the LICENSE file for more info. 98 | -------------------------------------------------------------------------------- /_Pods.xcodeproj: -------------------------------------------------------------------------------- 1 | Example/Pods/Pods.xcodeproj -------------------------------------------------------------------------------- /docs/badge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | documentation 17 | 18 | 19 | documentation 20 | 21 | 22 | 84% 23 | 24 | 25 | 84% 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleIdentifier 6 | com.jazzy.coregpx 7 | CFBundleName 8 | CoreGPX 9 | DocSetPlatformFamily 10 | coregpx 11 | isDashDocset 12 | 13 | dashIndexFilePath 14 | index.html 15 | isJavaScriptEnabled 16 | 17 | DashDocSetFamily 18 | dashtoc 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/Documents/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* Credit to https://gist.github.com/wataru420/2048287 */ 2 | .highlight { 3 | /* Comment */ 4 | /* Error */ 5 | /* Keyword */ 6 | /* Operator */ 7 | /* Comment.Multiline */ 8 | /* Comment.Preproc */ 9 | /* Comment.Single */ 10 | /* Comment.Special */ 11 | /* Generic.Deleted */ 12 | /* Generic.Deleted.Specific */ 13 | /* Generic.Emph */ 14 | /* Generic.Error */ 15 | /* Generic.Heading */ 16 | /* Generic.Inserted */ 17 | /* Generic.Inserted.Specific */ 18 | /* Generic.Output */ 19 | /* Generic.Prompt */ 20 | /* Generic.Strong */ 21 | /* Generic.Subheading */ 22 | /* Generic.Traceback */ 23 | /* Keyword.Constant */ 24 | /* Keyword.Declaration */ 25 | /* Keyword.Pseudo */ 26 | /* Keyword.Reserved */ 27 | /* Keyword.Type */ 28 | /* Literal.Number */ 29 | /* Literal.String */ 30 | /* Name.Attribute */ 31 | /* Name.Builtin */ 32 | /* Name.Class */ 33 | /* Name.Constant */ 34 | /* Name.Entity */ 35 | /* Name.Exception */ 36 | /* Name.Function */ 37 | /* Name.Namespace */ 38 | /* Name.Tag */ 39 | /* Name.Variable */ 40 | /* Operator.Word */ 41 | /* Text.Whitespace */ 42 | /* Literal.Number.Float */ 43 | /* Literal.Number.Hex */ 44 | /* Literal.Number.Integer */ 45 | /* Literal.Number.Oct */ 46 | /* Literal.String.Backtick */ 47 | /* Literal.String.Char */ 48 | /* Literal.String.Doc */ 49 | /* Literal.String.Double */ 50 | /* Literal.String.Escape */ 51 | /* Literal.String.Heredoc */ 52 | /* Literal.String.Interpol */ 53 | /* Literal.String.Other */ 54 | /* Literal.String.Regex */ 55 | /* Literal.String.Single */ 56 | /* Literal.String.Symbol */ 57 | /* Name.Builtin.Pseudo */ 58 | /* Name.Variable.Class */ 59 | /* Name.Variable.Global */ 60 | /* Name.Variable.Instance */ 61 | /* Literal.Number.Integer.Long */ } 62 | .highlight .c { 63 | color: #999988; 64 | font-style: italic; } 65 | .highlight .err { 66 | color: #a61717; 67 | background-color: #e3d2d2; } 68 | .highlight .k { 69 | color: #000000; 70 | font-weight: bold; } 71 | .highlight .o { 72 | color: #000000; 73 | font-weight: bold; } 74 | .highlight .cm { 75 | color: #999988; 76 | font-style: italic; } 77 | .highlight .cp { 78 | color: #999999; 79 | font-weight: bold; } 80 | .highlight .c1 { 81 | color: #999988; 82 | font-style: italic; } 83 | .highlight .cs { 84 | color: #999999; 85 | font-weight: bold; 86 | font-style: italic; } 87 | .highlight .gd { 88 | color: #000000; 89 | background-color: #ffdddd; } 90 | .highlight .gd .x { 91 | color: #000000; 92 | background-color: #ffaaaa; } 93 | .highlight .ge { 94 | color: #000000; 95 | font-style: italic; } 96 | .highlight .gr { 97 | color: #aa0000; } 98 | .highlight .gh { 99 | color: #999999; } 100 | .highlight .gi { 101 | color: #000000; 102 | background-color: #ddffdd; } 103 | .highlight .gi .x { 104 | color: #000000; 105 | background-color: #aaffaa; } 106 | .highlight .go { 107 | color: #888888; } 108 | .highlight .gp { 109 | color: #555555; } 110 | .highlight .gs { 111 | font-weight: bold; } 112 | .highlight .gu { 113 | color: #aaaaaa; } 114 | .highlight .gt { 115 | color: #aa0000; } 116 | .highlight .kc { 117 | color: #000000; 118 | font-weight: bold; } 119 | .highlight .kd { 120 | color: #000000; 121 | font-weight: bold; } 122 | .highlight .kp { 123 | color: #000000; 124 | font-weight: bold; } 125 | .highlight .kr { 126 | color: #000000; 127 | font-weight: bold; } 128 | .highlight .kt { 129 | color: #445588; } 130 | .highlight .m { 131 | color: #009999; } 132 | .highlight .s { 133 | color: #d14; } 134 | .highlight .na { 135 | color: #008080; } 136 | .highlight .nb { 137 | color: #0086B3; } 138 | .highlight .nc { 139 | color: #445588; 140 | font-weight: bold; } 141 | .highlight .no { 142 | color: #008080; } 143 | .highlight .ni { 144 | color: #800080; } 145 | .highlight .ne { 146 | color: #990000; 147 | font-weight: bold; } 148 | .highlight .nf { 149 | color: #990000; } 150 | .highlight .nn { 151 | color: #555555; } 152 | .highlight .nt { 153 | color: #000080; } 154 | .highlight .nv { 155 | color: #008080; } 156 | .highlight .ow { 157 | color: #000000; 158 | font-weight: bold; } 159 | .highlight .w { 160 | color: #bbbbbb; } 161 | .highlight .mf { 162 | color: #009999; } 163 | .highlight .mh { 164 | color: #009999; } 165 | .highlight .mi { 166 | color: #009999; } 167 | .highlight .mo { 168 | color: #009999; } 169 | .highlight .sb { 170 | color: #d14; } 171 | .highlight .sc { 172 | color: #d14; } 173 | .highlight .sd { 174 | color: #d14; } 175 | .highlight .s2 { 176 | color: #d14; } 177 | .highlight .se { 178 | color: #d14; } 179 | .highlight .sh { 180 | color: #d14; } 181 | .highlight .si { 182 | color: #d14; } 183 | .highlight .sx { 184 | color: #d14; } 185 | .highlight .sr { 186 | color: #009926; } 187 | .highlight .s1 { 188 | color: #d14; } 189 | .highlight .ss { 190 | color: #990073; } 191 | .highlight .bp { 192 | color: #999999; } 193 | .highlight .vc { 194 | color: #008080; } 195 | .highlight .vg { 196 | color: #008080; } 197 | .highlight .vi { 198 | color: #008080; } 199 | .highlight .il { 200 | color: #009999; } 201 | -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/carat.png -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/dash.png -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/docsets/CoreGPX.docset/Contents/Resources/Documents/img/gh.png -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/Documents/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.docset/Contents/Resources/docSet.dsidx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/docsets/CoreGPX.docset/Contents/Resources/docSet.dsidx -------------------------------------------------------------------------------- /docs/docsets/CoreGPX.tgz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/docsets/CoreGPX.tgz -------------------------------------------------------------------------------- /docs/img/carat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/img/carat.png -------------------------------------------------------------------------------- /docs/img/dash.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/img/dash.png -------------------------------------------------------------------------------- /docs/img/gh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vincentneo/CoreGPX/42662cc9235d385bdd0356c8f4cb7b581da24e39/docs/img/gh.png -------------------------------------------------------------------------------- /docs/js/jazzy.js: -------------------------------------------------------------------------------- 1 | window.jazzy = {'docset': false} 2 | if (typeof window.dash != 'undefined') { 3 | document.documentElement.className += ' dash' 4 | window.jazzy.docset = true 5 | } 6 | if (navigator.userAgent.match(/xcode/i)) { 7 | document.documentElement.className += ' xcode' 8 | window.jazzy.docset = true 9 | } 10 | 11 | function toggleItem($link, $content) { 12 | var animationDuration = 300; 13 | $link.toggleClass('token-open'); 14 | $content.slideToggle(animationDuration); 15 | } 16 | 17 | function itemLinkToContent($link) { 18 | return $link.parent().parent().next(); 19 | } 20 | 21 | // On doc load + hash-change, open any targetted item 22 | function openCurrentItemIfClosed() { 23 | if (window.jazzy.docset) { 24 | return; 25 | } 26 | var $link = $(`a[name="${location.hash.substring(1)}"]`).nextAll('.token'); 27 | $content = itemLinkToContent($link); 28 | if ($content.is(':hidden')) { 29 | toggleItem($link, $content); 30 | } 31 | } 32 | 33 | $(openCurrentItemIfClosed); 34 | $(window).on('hashchange', openCurrentItemIfClosed); 35 | 36 | // On item link ('token') click, toggle its discussion 37 | $('.token').on('click', function(event) { 38 | if (window.jazzy.docset) { 39 | return; 40 | } 41 | var $link = $(this); 42 | toggleItem($link, itemLinkToContent($link)); 43 | 44 | // Keeps the document from jumping to the hash. 45 | var href = $link.attr('href'); 46 | if (history.pushState) { 47 | history.pushState({}, '', href); 48 | } else { 49 | location.hash = href; 50 | } 51 | event.preventDefault(); 52 | }); 53 | 54 | // Clicks on links to the current, closed, item need to open the item 55 | $("a:not('.token')").on('click', function() { 56 | if (location == this.href) { 57 | openCurrentItemIfClosed(); 58 | } 59 | }); 60 | 61 | // KaTeX rendering 62 | if ("katex" in window) { 63 | $($('.math').each( (_, element) => { 64 | katex.render(element.textContent, element, { 65 | displayMode: $(element).hasClass('m-block'), 66 | throwOnError: false, 67 | trust: true 68 | }); 69 | })) 70 | } 71 | --------------------------------------------------------------------------------