foreground-location 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,75 @@
1
+ import Foundation
2
+ import CoreLocation
3
+
4
+ @objc public class ForeGroundLocation: NSObject, CLLocationManagerDelegate {
5
+ private var locationManager: CLLocationManager?
6
+ private var locationCompletion: ((Result<CLLocation, Error>) -> Void)?
7
+
8
+ public func getCurrentLocation(completion: @escaping (Result<CLLocation, Error>) -> Void) {
9
+ locationManager = CLLocationManager()
10
+ locationManager?.delegate = self
11
+ self.locationCompletion = completion
12
+
13
+ guard CLLocationManager.locationServicesEnabled() else {
14
+ completion(.failure(LocationError.locationServicesDisabled))
15
+ return
16
+ }
17
+
18
+ let authStatus = CLLocationManager.authorizationStatus()
19
+ guard authStatus == .authorizedWhenInUse || authStatus == .authorizedAlways else {
20
+ completion(.failure(LocationError.permissionDenied))
21
+ return
22
+ }
23
+
24
+ locationManager?.desiredAccuracy = kCLLocationAccuracyBest
25
+ locationManager?.requestLocation()
26
+ }
27
+
28
+ // MARK: - CLLocationManagerDelegate
29
+ public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
30
+ guard let location = locations.first else { return }
31
+ locationCompletion?(.success(location))
32
+ locationCompletion = nil
33
+
34
+ // Clean up
35
+ locationManager?.delegate = nil
36
+ locationManager = nil
37
+ }
38
+
39
+ public func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
40
+ locationCompletion?(.failure(error))
41
+ locationCompletion = nil
42
+
43
+ // Clean up
44
+ locationManager?.delegate = nil
45
+ locationManager = nil
46
+ }
47
+
48
+ public func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
49
+ // Handle authorization changes if needed
50
+ switch status {
51
+ case .denied, .restricted:
52
+ locationCompletion?(.failure(LocationError.permissionDenied))
53
+ locationCompletion = nil
54
+ default:
55
+ break
56
+ }
57
+ }
58
+ }
59
+
60
+ enum LocationError: Error {
61
+ case locationServicesDisabled
62
+ case permissionDenied
63
+ case locationUnavailable
64
+
65
+ var localizedDescription: String {
66
+ switch self {
67
+ case .locationServicesDisabled:
68
+ return "Location services are disabled"
69
+ case .permissionDenied:
70
+ return "Location permission denied"
71
+ case .locationUnavailable:
72
+ return "Location unavailable"
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,125 @@
1
+ import Foundation
2
+ import Capacitor
3
+ import CoreLocation
4
+
5
+ /**
6
+ * Foreground Location Plugin for iOS
7
+ * Limited iOS implementation - Foreground service pattern not available
8
+ */
9
+ @objc(ForeGroundLocationPlugin)
10
+ public class ForeGroundLocationPlugin: CAPPlugin, CAPBridgedPlugin {
11
+ public let identifier = "ForeGroundLocationPlugin"
12
+ public let jsName = "ForeGroundLocation"
13
+ public let pluginMethods: [CAPPluginMethod] = [
14
+ CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
15
+ CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
16
+ CAPPluginMethod(name: "startForegroundLocationService", returnType: CAPPluginReturnPromise),
17
+ CAPPluginMethod(name: "stopForegroundLocationService", returnType: CAPPluginReturnPromise),
18
+ CAPPluginMethod(name: "isServiceRunning", returnType: CAPPluginReturnPromise),
19
+ CAPPluginMethod(name: "getCurrentLocation", returnType: CAPPluginReturnPromise),
20
+ CAPPluginMethod(name: "updateLocationSettings", returnType: CAPPluginReturnPromise)
21
+ ]
22
+
23
+ private let implementation = ForeGroundLocation()
24
+
25
+ @objc override public func checkPermissions(_ call: CAPPluginCall) {
26
+ let locationManager = CLLocationManager()
27
+ let authStatus = locationManager.authorizationStatus
28
+
29
+ let permissionState: String
30
+ switch authStatus {
31
+ case .notDetermined:
32
+ permissionState = "prompt"
33
+ case .denied, .restricted:
34
+ permissionState = "denied"
35
+ case .authorizedWhenInUse, .authorizedAlways:
36
+ permissionState = "granted"
37
+ @unknown default:
38
+ permissionState = "prompt"
39
+ }
40
+
41
+ call.resolve([
42
+ "location": permissionState,
43
+ "backgroundLocation": permissionState == "granted" && authStatus == .authorizedAlways ? "granted" : "denied",
44
+ "notifications": "denied"
45
+ ])
46
+ }
47
+
48
+ @objc override public func requestPermissions(_ call: CAPPluginCall) {
49
+ let locationManager = CLLocationManager()
50
+
51
+ switch locationManager.authorizationStatus {
52
+ case .notDetermined:
53
+ // For iOS, we can only request when-in-use permission
54
+ // Background location requires additional setup in Info.plist
55
+ locationManager.requestWhenInUseAuthorization()
56
+
57
+ // Since we can't easily wait for the delegate callback in this context,
58
+ // we'll return the current state and recommend checking again
59
+ call.resolve([
60
+ "location": "prompt",
61
+ "backgroundLocation": "denied",
62
+ "notifications": "denied"
63
+ ])
64
+ case .denied, .restricted:
65
+ call.resolve([
66
+ "location": "denied",
67
+ "backgroundLocation": "denied",
68
+ "notifications": "denied"
69
+ ])
70
+ case .authorizedWhenInUse:
71
+ call.resolve([
72
+ "location": "granted",
73
+ "backgroundLocation": "denied", // Would need always authorization
74
+ "notifications": "denied"
75
+ ])
76
+ case .authorizedAlways:
77
+ call.resolve([
78
+ "location": "granted",
79
+ "backgroundLocation": "granted",
80
+ "notifications": "denied"
81
+ ])
82
+ @unknown default:
83
+ call.resolve([
84
+ "location": "prompt",
85
+ "backgroundLocation": "denied",
86
+ "notifications": "denied"
87
+ ])
88
+ }
89
+ }
90
+
91
+ @objc func startForegroundLocationService(_ call: CAPPluginCall) {
92
+ call.unimplemented("Foreground location service pattern not available on iOS. Use iOS background location modes instead.")
93
+ }
94
+
95
+ @objc func stopForegroundLocationService(_ call: CAPPluginCall) {
96
+ call.unimplemented("Foreground location service pattern not available on iOS.")
97
+ }
98
+
99
+ @objc func isServiceRunning(_ call: CAPPluginCall) {
100
+ call.resolve(["isRunning": false])
101
+ }
102
+
103
+ @objc func getCurrentLocation(_ call: CAPPluginCall) {
104
+ implementation.getCurrentLocation { result in
105
+ switch result {
106
+ case .success(let location):
107
+ call.resolve([
108
+ "latitude": location.coordinate.latitude,
109
+ "longitude": location.coordinate.longitude,
110
+ "accuracy": location.horizontalAccuracy,
111
+ "altitude": location.altitude,
112
+ "bearing": location.course >= 0 ? location.course : nil,
113
+ "speed": location.speed >= 0 ? location.speed : nil,
114
+ "timestamp": ISO8601DateFormatter().string(from: location.timestamp)
115
+ ])
116
+ case .failure(let error):
117
+ call.reject("Location error: \(error.localizedDescription)")
118
+ }
119
+ }
120
+ }
121
+
122
+ @objc func updateLocationSettings(_ call: CAPPluginCall) {
123
+ call.unimplemented("Location service settings not supported on iOS.")
124
+ }
125
+ }
@@ -0,0 +1,36 @@
1
+ import XCTest
2
+ @testable import ForeGroundLocationPlugin
3
+
4
+ class ForeGroundLocationTests: XCTestCase {
5
+ func testLocationErrorCases() {
6
+ // Test that LocationError enum has proper descriptions
7
+ let locationServicesDisabledError = LocationError.locationServicesDisabled
8
+ XCTAssertEqual(locationServicesDisabledError.localizedDescription, "Location services are disabled")
9
+
10
+ let permissionDeniedError = LocationError.permissionDenied
11
+ XCTAssertEqual(permissionDeniedError.localizedDescription, "Location permission denied")
12
+
13
+ let locationUnavailableError = LocationError.locationUnavailable
14
+ XCTAssertEqual(locationUnavailableError.localizedDescription, "Location unavailable")
15
+ }
16
+
17
+ func testPluginMethodsRegistration() {
18
+ // Test that plugin has all required methods registered
19
+ let plugin = ForeGroundLocationPlugin()
20
+ let methodNames = plugin.pluginMethods.map { $0.name }
21
+
22
+ let expectedMethods = [
23
+ "checkPermissions",
24
+ "requestPermissions",
25
+ "startForegroundLocationService",
26
+ "stopForegroundLocationService",
27
+ "isServiceRunning",
28
+ "getCurrentLocation",
29
+ "updateLocationSettings"
30
+ ]
31
+
32
+ for method in expectedMethods {
33
+ XCTAssertTrue(methodNames.contains(method), "Missing method: \(method)")
34
+ }
35
+ }
36
+ }
package/package.json ADDED
@@ -0,0 +1,82 @@
1
+ {
2
+ "name": "foreground-location",
3
+ "version": "0.0.1",
4
+ "description": "Location Tracking Using Foreground Service and Fused Location API",
5
+ "main": "dist/plugin.cjs.js",
6
+ "module": "dist/esm/index.js",
7
+ "types": "dist/esm/index.d.ts",
8
+ "unpkg": "dist/plugin.js",
9
+ "files": [
10
+ "android/src/main/",
11
+ "android/build.gradle",
12
+ "dist/",
13
+ "ios/Sources",
14
+ "ios/Tests",
15
+ "Package.swift",
16
+ "ForegroundLocation.podspec",
17
+ "docs/"
18
+ ],
19
+ "author": "xConcepts",
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/xconcepts17/foreground-location.git"
24
+ },
25
+ "bugs": {
26
+ "url": "https://github.com/xconcepts17/foreground-location/issues"
27
+ },
28
+ "keywords": [
29
+ "capacitor",
30
+ "plugin",
31
+ "native"
32
+ ],
33
+ "scripts": {
34
+ "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
35
+ "verify:ios": "xcodebuild -scheme ForegroundLocation -destination generic/platform=iOS",
36
+ "verify:android": "cd android && ./gradlew clean build test && cd ..",
37
+ "verify:web": "npm run build",
38
+ "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
39
+ "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
40
+ "eslint": "eslint . --ext ts",
41
+ "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
42
+ "swiftlint": "node-swiftlint",
43
+ "docgen": "docgen --api ForeGroundLocationPlugin --output-readme README.md --output-json dist/docs.json",
44
+ "build": "npm run clean && tsc && rollup -c rollup.config.mjs",
45
+ "build-with-docs": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
46
+ "clean": "rimraf ./dist",
47
+ "watch": "tsc --watch",
48
+ "prepublishOnly": "npm run build"
49
+ },
50
+ "devDependencies": {
51
+ "@capacitor/android": "^7.0.0",
52
+ "@capacitor/core": "^7.0.0",
53
+ "@capacitor/docgen": "^0.3.0",
54
+ "@capacitor/ios": "^7.0.0",
55
+ "@ionic/eslint-config": "^0.4.0",
56
+ "@ionic/prettier-config": "^4.0.0",
57
+ "@ionic/swiftlint-config": "^2.0.0",
58
+ "eslint": "^8.57.0",
59
+ "prettier": "^3.4.2",
60
+ "prettier-plugin-java": "^2.6.6",
61
+ "rimraf": "^6.0.1",
62
+ "rollup": "^4.30.1",
63
+ "swiftlint": "^2.0.0",
64
+ "typescript": "~4.1.5"
65
+ },
66
+ "peerDependencies": {
67
+ "@capacitor/core": ">=7.0.0"
68
+ },
69
+ "prettier": "@ionic/prettier-config",
70
+ "swiftlint": "@ionic/swiftlint-config",
71
+ "eslintConfig": {
72
+ "extends": "@ionic/eslint-config/recommended"
73
+ },
74
+ "capacitor": {
75
+ "ios": {
76
+ "src": "ios"
77
+ },
78
+ "android": {
79
+ "src": "android"
80
+ }
81
+ }
82
+ }