Skip to content

Lokasyon Servisleri

Mobil uygulamalarda lokasyon servisleri, kullanıcı deneyimini zenginleştiren önemli bir bileşendir. Bu bölümde, lokasyon tabanlı servislerin güvenli, verimli ve kullanıcı dostu şekilde nasıl implement edileceği ele alınacaktır.

Temel Kavramlar

1. Lokasyon Hassasiyeti

  • Yüksek Hassasiyet: GPS + Ağ + Pasif
  • Dengeli Güç: Ağ + Pasif
  • Düşük Güç: Sadece Pasif
  • Güç Yok: Sadece Önbellek Konumları

2. Lokasyon Türleri

  • GPS: En hassas, en yüksek batarya tüketimi
  • Network: Orta hassasiyet, orta batarya tüketimi
  • Passive: Düşük hassasiyet, düşük batarya tüketimi

Platform-Spesifik Implementasyonlar

iOS Core Location

Temel Kurulum

swift
import CoreLocation

class LocationManager: NSObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()
    private var locationUpdateHandler: ((CLLocation) -> Void)?
    
    override init() {
        super.init()
        setupLocationManager()
    }
    
    private func setupLocationManager() {
        locationManager.delegate = self
        locationManager.desiredAccuracy = kCLLocationAccuracyBest
        locationManager.distanceFilter = 10.0 // 10 metre minimum hareket
    }
    
    func requestLocationPermission() {
        switch locationManager.authorizationStatus {
        case .notDetermined:
            locationManager.requestWhenInUseAuthorization()
        case .denied, .restricted:
            // Kullanıcıyı ayarlara yönlendir
            showLocationSettingsAlert()
        case .authorizedWhenInUse:
            // İzin var, lokasyon isteyebiliriz
            break
        case .authorizedAlways:
            // Tam izin var
            break
        @unknown default:
            break
        }
    }
    
    func startLocationUpdates(updateHandler: @escaping (CLLocation) -> Void) {
        self.locationUpdateHandler = updateHandler
        locationManager.startUpdatingLocation()
    }
    
    func stopLocationUpdates() {
        locationManager.stopUpdatingLocation()
        locationUpdateHandler = nil
    }
    
    // MARK: - CLLocationManagerDelegate
    
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        guard let location = locations.last else { return }
        locationUpdateHandler?(location)
    }
    
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("Location error: \\(error.localizedDescription)")
    }
    
    func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
        switch status {
        case .authorizedWhenInUse, .authorizedAlways:
            startLocationUpdates { location in
                // Handle location update
            }
        case .denied, .restricted:
            showLocationSettingsAlert()
        default:
            break
        }
    }
}

Background Location

swift
// Background location için gerekli konfigürasyon
class BackgroundLocationManager: NSObject {
    private let locationManager = CLLocationManager()
    
    func enableBackgroundLocationUpdates() {
        // Info.plist'e eklenmiş olmalı:
        // <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
        // <key>UIBackgroundModes</key>
        // <array><string>location</string></array>
        
        locationManager.requestAlwaysAuthorization()
        locationManager.allowsBackgroundLocationUpdates = true
        locationManager.pausesLocationUpdatesAutomatically = false
    }
    
    func optimizeForBattery() {
        // Batarya optimizasyonu için
        locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters
        locationManager.distanceFilter = 100.0
        locationManager.pausesLocationUpdatesAutomatically = true
    }
}

Android Location Services

Modern Location API

kotlin
import com.google.android.gms.location.*
import com.google.android.gms.tasks.Task

class LocationService {
    private lateinit var fusedLocationClient: FusedLocationProviderClient
    private lateinit var locationRequest: LocationRequest
    private lateinit var locationCallback: LocationCallback
    
    fun initializeLocationServices(context: Context) {
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
        createLocationRequest()
        createLocationCallback()
    }
    
    private fun createLocationRequest() {
        locationRequest = LocationRequest.create().apply {
            interval = 10000 // 10 saniye
            fastestInterval = 5000 // 5 saniye
            priority = LocationRequest.PRIORITY_HIGH_ACCURACY
            smallestDisplacement = 10f // 10 metre
        }
    }
    
    private fun createLocationCallback() {
        locationCallback = object : LocationCallback() {
            override fun onLocationResult(locationResult: LocationResult) {
                locationResult.lastLocation?.let { location ->
                    handleLocationUpdate(location)
                }
            }
            
            override fun onLocationAvailability(locationAvailability: LocationAvailability) {
                if (!locationAvailability.isLocationAvailable) {
                    // Lokasyon servisi mevcut değil
                    handleLocationUnavailable()
                }
            }
        }
    }
    
    @SuppressLint("MissingPermission")
    fun startLocationUpdates() {
        fusedLocationClient.requestLocationUpdates(
            locationRequest,
            locationCallback,
            Looper.getMainLooper()
        )
    }
    
    fun stopLocationUpdates() {
        fusedLocationClient.removeLocationUpdates(locationCallback)
    }
    
    @SuppressLint("MissingPermission")
    fun getLastKnownLocation(): Task<Location> {
        return fusedLocationClient.lastLocation
    }
    
    private fun handleLocationUpdate(location: Location) {
        // Lokasyon güncellemesini işle
        val latitude = location.latitude
        val longitude = location.longitude
        val accuracy = location.accuracy
        
        // Lokasyonu kaydet veya kullan
        saveLocationToDatabase(latitude, longitude, accuracy)
    }
}

Permission Handling

kotlin
class LocationPermissionManager {
    companion object {
        const val LOCATION_PERMISSION_REQUEST_CODE = 1001
        
        fun hasLocationPermission(context: Context): Boolean {
            return ContextCompat.checkSelfPermission(
                context,
                Manifest.permission.ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED
        }
        
        fun requestLocationPermission(activity: Activity) {
            ActivityCompat.requestPermissions(
                activity,
                arrayOf(
                    Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION
                ),
                LOCATION_PERMISSION_REQUEST_CODE
            )
        }
        
        fun shouldShowRationale(activity: Activity): Boolean {
            return ActivityCompat.shouldShowRequestPermissionRationale(
                activity,
                Manifest.permission.ACCESS_FINE_LOCATION
            )
        }
    }
}

// Activity'de kullanım
class MainActivity : AppCompatActivity() {
    
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        
        when (requestCode) {
            LocationPermissionManager.LOCATION_PERMISSION_REQUEST_CODE -> {
                if (grantResults.isNotEmpty() && 
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // İzin verildi
                    startLocationServices()
                } else {
                    // İzin reddedildi
                    showPermissionDeniedMessage()
                }
            }
        }
    }
}

React Native Lokasyon Servisleri

Geolocation API

javascript
import Geolocation from '@react-native-community/geolocation';
import {PermissionsAndroid, Platform} from 'react-native';

class LocationService {
  constructor() {
    this.watchId = null;
    this.currentLocation = null;
    this.isTracking = false;
  }
  
  async requestLocationPermission() {
    if (Platform.OS === 'android') {
      try {
        const granted = await PermissionsAndroid.request(
          PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
          {
            title: 'Lokasyon İzni',
            message: 'Uygulamanın lokasyonunuza erişmesi gerekiyor',
            buttonNeutral: 'Daha Sonra Sor',
            buttonNegative: 'İptal',
            buttonPositive: 'Tamam',
          }
        );
        
        return granted === PermissionsAndroid.RESULTS.GRANTED;
      } catch (err) {
        console.warn(err);
        return false;
      }
    }
    return true; // iOS için permission Info.plist'te tanımlı
  }
  
  getCurrentLocation() {
    return new Promise((resolve, reject) => {
      Geolocation.getCurrentPosition(
        position => {
          this.currentLocation = position;
          resolve(position);
        },
        error => reject(error),
        {
          enableHighAccuracy: true,
          timeout: 15000,
          maximumAge: 10000,
        }
      );
    });
  }
  
  startTracking(onLocationUpdate) {
    this.watchId = Geolocation.watchPosition(
      position => {
        this.currentLocation = position;
        onLocationUpdate(position);
      },
      error => {
        console.error('Location tracking error:', error);
      },
      {
        enableHighAccuracy: true,
        distanceFilter: 10, // 10 metre minimum hareket
        interval: 10000, // 10 saniye interval (Android)
        fastestInterval: 5000, // 5 saniye minimum interval (Android)
      }
    );
    
    this.isTracking = true;
  }
  
  stopTracking() {
    if (this.watchId !== null) {
      Geolocation.clearWatch(this.watchId);
      this.watchId = null;
      this.isTracking = false;
    }
  }
  
  // Batarya optimizasyonu için düşük hassasiyet modu
  startLowPowerTracking(onLocationUpdate) {
    this.watchId = Geolocation.watchPosition(
      position => {
        this.currentLocation = position;
        onLocationUpdate(position);
      },
      error => {
        console.error('Location tracking error:', error);
      },
      {
        enableHighAccuracy: false, // Düşük hassasiyet
        distanceFilter: 100, // 100 metre minimum hareket
        interval: 60000, // 1 dakika interval
        fastestInterval: 30000, // 30 saniye minimum interval
      }
    );
  }
}

// Hook kullanımı
import {useState, useEffect} from 'react';

export const useLocation = () => {
  const [location, setLocation] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  
  const locationService = new LocationService();
  
  const getCurrentLocation = async () => {
    setLoading(true);
    setError(null);
    
    try {
      const hasPermission = await locationService.requestLocationPermission();
      if (!hasPermission) {
        throw new Error('Lokasyon izni reddedildi');
      }
      
      const position = await locationService.getCurrentLocation();
      setLocation(position);
    } catch (err) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  };
  
  const startTracking = async (callback) => {
    const hasPermission = await locationService.requestLocationPermission();
    if (hasPermission) {
      locationService.startTracking(callback);
    }
  };
  
  const stopTracking = () => {
    locationService.stopTracking();
  };
  
  return {
    location,
    loading,
    error,
    getCurrentLocation,
    startTracking,
    stopTracking,
  };
};

Flutter Location Services

Geolocator Package

dart
import 'package:geolocator/geolocator.dart';
import 'package:permission_handler/permission_handler.dart';

class LocationService {
  static const LocationSettings _locationSettings = LocationSettings(
    accuracy: LocationAccuracy.high,
    distanceFilter: 10,
  );
  
  Future<bool> requestLocationPermission() async {
    PermissionStatus permission = await Permission.locationWhenInUse.status;
    
    if (permission.isDenied) {
      permission = await Permission.locationWhenInUse.request();
    }
    
    if (permission.isPermanentlyDenied) {
      // Kullanıcıyı ayarlara yönlendir
      openAppSettings();
      return false;
    }
    
    return permission.isGranted;
  }
  
  Future<Position?> getCurrentLocation() async {
    try {
      bool hasPermission = await requestLocationPermission();
      if (!hasPermission) return null;
      
      bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        throw Exception('Lokasyon servisi kapalı');
      }
      
      Position position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high,
      );
      
      return position;
    } catch (e) {
      print('Lokasyon alınamadı: $e');
      return null;
    }
  }
  
  Stream<Position> getLocationStream() {
    return Geolocator.getPositionStream(
      locationSettings: _locationSettings,
    );
  }
  
  // Mesafe hesaplama
  double calculateDistance(Position start, Position end) {
    return Geolocator.distanceBetween(
      start.latitude,
      start.longitude,
      end.latitude,
      end.longitude,
    );
  }
  
  // Batarya dostu tracking
  Stream<Position> getBatteryOptimizedLocationStream() {
    const LocationSettings batteryOptimizedSettings = LocationSettings(
      accuracy: LocationAccuracy.low,
      distanceFilter: 100, // 100 metre
    );
    
    return Geolocator.getPositionStream(
      locationSettings: batteryOptimizedSettings,
    );
  }
}

// Provider kullanımı
class LocationProvider extends ChangeNotifier {
  Position? _currentPosition;
  bool _isTracking = false;
  StreamSubscription<Position>? _positionSubscription;
  
  Position? get currentPosition => _currentPosition;
  bool get isTracking => _isTracking;
  
  final LocationService _locationService = LocationService();
  
  Future<void> getCurrentLocation() async {
    Position? position = await _locationService.getCurrentLocation();
    if (position != null) {
      _currentPosition = position;
      notifyListeners();
    }
  }
  
  void startTracking() {
    if (_isTracking) return;
    
    _positionSubscription = _locationService.getLocationStream().listen(
      (Position position) {
        _currentPosition = position;
        notifyListeners();
      },
      onError: (error) {
        print('Location tracking error: $error');
      },
    );
    
    _isTracking = true;
    notifyListeners();
  }
  
  void stopTracking() {
    _positionSubscription?.cancel();
    _positionSubscription = null;
    _isTracking = false;
    notifyListeners();
  }
  
  @override
  void dispose() {
    stopTracking();
    super.dispose();
  }
}

Geofencing

iOS Geofencing

swift
import CoreLocation

class GeofenceManager: NSObject, CLLocationManagerDelegate {
    private let locationManager = CLLocationManager()
    private var geofences: [CLCircularRegion] = []
    
    override init() {
        super.init()
        locationManager.delegate = self
    }
    
    func addGeofence(identifier: String, center: CLLocationCoordinate2D, radius: CLLocationDistance) {
        let geofence = CLCircularRegion(
            center: center,
            radius: radius,
            identifier: identifier
        )
        
        geofence.notifyOnEntry = true
        geofence.notifyOnExit = true
        
        locationManager.startMonitoring(for: geofence)
        geofences.append(geofence)
    }
    
    func removeGeofence(identifier: String) {
        if let region = locationManager.monitoredRegions.first(where: { $0.identifier == identifier }) {
            locationManager.stopMonitoring(for: region)
        }
        
        geofences.removeAll { $0.identifier == identifier }
    }
    
    // MARK: - CLLocationManagerDelegate
    
    func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
        print("Geofence'e girildi: \\(region.identifier)")
        handleGeofenceEntry(region.identifier)
    }
    
    func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
        print("Geofence'den çıkıldı: \\(region.identifier)")
        handleGeofenceExit(region.identifier)
    }
    
    private func handleGeofenceEntry(_ identifier: String) {
        // Geofence'e girildiğinde yapılacak işlemler
        sendLocalNotification(title: "Bölgeye Girildi", body: "\\(identifier) bölgesine girdiniz")
    }
    
    private func handleGeofenceExit(_ identifier: String) {
        // Geofence'den çıkıldığında yapılacak işlemler
        sendLocalNotification(title: "Bölgeden Çıkıldı", body: "\\(identifier) bölgesinden çıktınız")
    }
}

Android Geofencing

kotlin
import com.google.android.gms.location.*

class GeofenceManager(private val context: Context) {
    private lateinit var geofencingClient: GeofencingClient
    private val geofenceList = mutableListOf<Geofence>()
    
    init {
        geofencingClient = LocationServices.getGeofencingClient(context)
    }
    
    fun addGeofence(id: String, latitude: Double, longitude: Double, radius: Float) {
        val geofence = Geofence.Builder()
            .setRequestId(id)
            .setCircularRegion(latitude, longitude, radius)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)
            .build()
            
        geofenceList.add(geofence)
        addGeofences()
    }
    
    @SuppressLint("MissingPermission")
    private fun addGeofences() {
        val geofencingRequest = GeofencingRequest.Builder().apply {
            setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
            addGeofences(geofenceList)
        }.build()
        
        geofencingClient.addGeofences(geofencingRequest, geofencePendingIntent)
    }
    
    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(context, GeofenceBroadcastReceiver::class.java)
        PendingIntent.getBroadcast(
            context, 
            0, 
            intent, 
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
    }
}

class GeofenceBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        
        if (geofencingEvent?.hasError() == true) {
            return
        }
        
        val geofenceTransition = geofencingEvent?.geofenceTransition
        
        when (geofenceTransition) {
            Geofence.GEOFENCE_TRANSITION_ENTER -> {
                // Geofence'e girildi
                handleGeofenceEntry(geofencingEvent.triggeringGeofences)
            }
            Geofence.GEOFENCE_TRANSITION_EXIT -> {
                // Geofence'den çıkıldı
                handleGeofenceExit(geofencingEvent.triggeringGeofences)
            }
        }
    }
    
    private fun handleGeofenceEntry(geofences: List<Geofence>?) {
        geofences?.forEach { geofence ->
            // Notification gönder
            sendNotification("Bölgeye Girildi", "${geofence.requestId} bölgesine girdiniz")
        }
    }
    
    private fun handleGeofenceExit(geofences: List<Geofence>?) {
        geofences?.forEach { geofence ->
            // Notification gönder
            sendNotification("Bölgeden Çıkıldı", "${geofence.requestId} bölgesinden çıktınız")
        }
    }
}

En İyi Uygulamalar

1. Batarya Optimizasyonu

  • Gerekli olmadığında lokasyon servisini durdurun
  • Düşük hassasiyet modunu kullanın
  • Minimum hareket mesafesi belirleyin

2. Güvenlik ve Gizlilik

  • Kullanıcıdan açık izin alın
  • Lokasyon verilerini güvenli şekilde saklayın
  • Gereksiz lokasyon verisi toplamayın

3. Kullanıcı Deneyimi

  • İzin isteme nedenini açık şekilde belirtin
  • Lokasyon servisi kapalıysa alternatif sunun
  • Loading state'leri gösterin

4. Error Handling

  • Lokasyon bulunamadığında fallback plan hazırlayın
  • Timeout durumlarını handle edin
  • Kullanıcıya anlaşılır hata mesajları verin

Bu implementasyonlar ile güçlü, verimli ve kullanıcı dostu lokasyon servisleri geliştirebilirsiniz.

Eren Demir tarafından oluşturulmuştur.