Mobile App

Mobil Marketplace Uygulaması

React Native ve Flutter ile iOS/Android e-ticaret uygulaması geliştirme. Push notification, ödeme, offline mode ve performance optimization.

📱Mobil Geliştirme Teknolojileri

Mobil marketplace kullanıcı trafiğinin %70-80'ini oluşturur. React Native veya Flutter ile hem iOS hem Android için tek kod tabanı.

React Native

Dil: JavaScript/TypeScript
Performans: İyi (native bridge)
Ekosistem: Çok geniş
Örnekler: Facebook, Instagram, Shopify
Maliyet: 150-300K TL

Flutter

Dil: Dart
Performans: Çok iyi (direkt compile)
Ekosistem: Büyüyor
Örnekler: Alibaba, eBay, Google Pay
Maliyet: 120-250K TL

React Native Marketplace

// React Native E-Ticaret Ana Ekran
import React, { useState, useEffect } from 'react';
import { View, FlatList, StyleSheet, RefreshControl } from 'react-native';
import { SearchBar, Card, Button } from 'react-native-elements';

const HomeScreen = ({ navigation }) => {
  const [products, setProducts] = useState([]);
  const [search, setSearch] = useState('');
  const [refreshing, setRefreshing] = useState(false);

  useEffect(() => {
    fetchProducts();
  }, []);

  const fetchProducts = async () => {
    try {
      const response = await fetch('https://api.example.com/products');
      const data = await response.json();
      setProducts(data.products);
    } catch (error) {
      console.error('Error fetching products:', error);
    }
  };

  const onRefresh = async () => {
    setRefreshing(true);
    await fetchProducts();
    setRefreshing(false);
  };

  const renderProduct = ({ item }) => (
    <Card containerStyle={styles.card}>
      <Card.Image
        source={{ uri: item.image }}
        style={styles.productImage}
        resizeMode="cover"
      />
      <Card.Title>{item.name}</Card.Title>
      <View style={styles.priceContainer}>
        <Text style={styles.price}>{item.price} TL</Text>
        {item.discount && (
          <Text style={styles.oldPrice}>{item.oldPrice} TL</Text>
        )}
      </View>
      <Button
        title="Sepete Ekle"
        onPress={() => addToCart(item)}
        buttonStyle={styles.addButton}
      />
    </Card>
  );

  return (
    <View style={styles.container}>
      <SearchBar
        placeholder="Ürün ara..."
        onChangeText={setSearch}
        value={search}
        platform="ios"
        containerStyle={styles.searchBar}
      />
      <FlatList
        data={products.filter(p =>
          p.name.toLowerCase().includes(search.toLowerCase())
        )}
        renderItem={renderProduct}
        keyExtractor={item => item.id}
        numColumns={2}
        refreshControl={
          <RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
        }
      />
    </View>
  );
};

const styles = StyleSheet.create({
  container: { flex: 1, backgroundColor: '#f5f5f5' },
  searchBar: { backgroundColor: 'transparent' },
  card: { borderRadius: 8, margin: 5, padding: 10 },
  productImage: { height: 150, borderRadius: 8 },
  priceContainer: { flexDirection: 'row', marginTop: 10 },
  price: { fontSize: 18, fontWeight: 'bold', color: '#10B981' },
  oldPrice: { fontSize: 14, textDecorationLine: 'line-through', marginLeft: 8 },
  addButton: { backgroundColor: '#10B981', marginTop: 10 }
});

export default HomeScreen;

Push Notification

// Firebase Cloud Messaging (FCM) entegrasyonu
import messaging from '@react-native-firebase/messaging';
import notifee from '@notifee/react-native';

// Notification permission
async function requestUserPermission() {
  const authStatus = await messaging().requestPermission();
  const enabled =
    authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
    authStatus === messaging.AuthorizationStatus.PROVISIONAL;

  if (enabled) {
    console.log('Authorization status:', authStatus);
    getFCMToken();
  }
}

// FCM Token al
async function getFCMToken() {
  const fcmToken = await messaging().getToken();
  console.log('FCM Token:', fcmToken);

  // Token'ı backend'e gönder
  await fetch('https://api.example.com/users/fcm-token', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ token: fcmToken })
  });
}

// Foreground notification (app açıkken)
messaging().onMessage(async remoteMessage => {
  console.log('Notification received:', remoteMessage);

  // Notifee ile local notification göster
  await notifee.displayNotification({
    title: remoteMessage.notification.title,
    body: remoteMessage.notification.body,
    android: {
      channelId: 'default',
      smallIcon: 'ic_launcher',
      pressAction: {
        id: 'default',
      },
    },
    ios: {
      sound: 'default',
    },
  });
});

// Background notification (app kapalıyken)
messaging().setBackgroundMessageHandler(async remoteMessage => {
  console.log('Background notification:', remoteMessage);
});

// Notification tıklama
messaging().onNotificationOpenedApp(remoteMessage => {
  console.log('Notification opened:', remoteMessage);

  // Deep linking
  if (remoteMessage.data.productId) {
    navigation.navigate('ProductDetail', {
      productId: remoteMessage.data.productId
    });
  }
});

// App Service Worker (backend - Node.js)
const admin = require('firebase-admin');
admin.initializeApp({
  credential: admin.credential.cert('serviceAccountKey.json')
});

async function sendPushNotification(userId, title, body, data) {
  // User'ın FCM token'ını database'den al
  const user = await getUserById(userId);

  if (user.fcmToken) {
    const message = {
      notification: { title, body },
      data: data || {},
      token: user.fcmToken
    };

    try {
      const response = await admin.messaging().send(message);
      console.log('Notification sent:', response);
    } catch (error) {
      console.error('Error sending notification:', error);
    }
  }
}

// Toplu gönderim (batch)
async function sendBulkNotifications(userIds, title, body) {
  const tokens = await getUserTokens(userIds);

  const message = {
    notification: { title, body },
    tokens: tokens  // Max 500 token
  };

  const response = await admin.messaging().sendMulticast(message);
  console.log(`${response.successCount} notifications sent`);
}

Ödeme Entegrasyonu (iyzico)

// React Native iyzico WebView ödeme
import { WebView } from 'react-native-webview';

const PaymentScreen = ({ route }) => {
  const { orderId, amount } = route.params;
  const [paymentUrl, setPaymentUrl] = useState(null);

  useEffect(() => {
    initiatePayment();
  }, []);

  const initiatePayment = async () => {
    // Backend'den ödeme URL'i al
    const response = await fetch('https://api.example.com/payments/initiate', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        orderId,
        amount,
        returnUrl: 'myapp://payment-success',
        cancelUrl: 'myapp://payment-cancel'
      })
    });

    const data = await response.json();
    setPaymentUrl(data.paymentPageUrl);  // iyzico 3D Secure URL
  };

  const onNavigationStateChange = (navState) => {
    // Ödeme sonucu kontrolü
    if (navState.url.includes('payment-success')) {
      navigation.navigate('OrderSuccess', { orderId });
    } else if (navState.url.includes('payment-cancel')) {
      navigation.navigate('OrderFailed');
    }
  };

  return (
    <View style={{ flex: 1 }}>
      {paymentUrl ? (
        <WebView
          source={{ uri: paymentUrl }}
          onNavigationStateChange={onNavigationStateChange}
          startInLoadingState
        />
      ) : (
        <ActivityIndicator size="large" color="#10B981" />
      )}
    </View>
  );
};

// Alternatif: Native ödeme (Apple Pay, Google Pay)
import { PaymentRequest } from 'react-native-payments';

const payWithApplePay = async (amount) => {
  const METHOD_DATA = [{
    supportedMethods: ['apple-pay'],
    data: {
      merchantIdentifier: 'merchant.com.yourapp',
      supportedNetworks: ['visa', 'mastercard'],
      countryCode: 'TR',
      currencyCode: 'TRY'
    }
  }];

  const DETAILS = {
    total: {
      label: 'Toplam',
      amount: { currency: 'TRY', value: amount.toString() }
    }
  };

  const paymentRequest = new PaymentRequest(METHOD_DATA, DETAILS);

  try {
    const paymentResponse = await paymentRequest.show();
    const { paymentToken } = paymentResponse.details;

    // Token'ı backend'e gönder
    await processPayment(paymentToken);

    paymentResponse.complete('success');
  } catch (error) {
    console.error('Payment error:', error);
  }
};

Offline Mode ve Caching

// AsyncStorage ile offline data
import AsyncStorage from '@react-native-async-storage/async-storage';
import NetInfo from '@react-native-community/netinfo';

class OfflineManager {
  static async saveProductsCache(products) {
    await AsyncStorage.setItem('cached_products', JSON.stringify(products));
  }

  static async getCachedProducts() {
    const cached = await AsyncStorage.getItem('cached_products');
    return cached ? JSON.parse(cached) : [];
  }

  static async fetchProductsWithOffline() {
    const netInfo = await NetInfo.fetch();

    if (netInfo.isConnected) {
      // Online: API'den çek
      try {
        const response = await fetch('https://api.example.com/products');
        const products = await response.json();

        // Cache'e kaydet
        await this.saveProductsCache(products);
        return { products, source: 'api' };
      } catch (error) {
        // API başarısız, cache'den oku
        const cached = await this.getCachedProducts();
        return { products: cached, source: 'cache' };
      }
    } else {
      // Offline: Cache'den oku
      const cached = await this.getCachedProducts();
      return { products: cached, source: 'offline' };
    }
  }

  // Pending actions (sepete ekleme vb.)
  static async addPendingAction(action) {
    const pending = await this.getPendingActions();
    pending.push(action);
    await AsyncStorage.setItem('pending_actions', JSON.stringify(pending));
  }

  static async syncPendingActions() {
    const pending = await this.getPendingActions();

    for (const action of pending) {
      try {
        await this.executeAction(action);
        // Başarılı, listeden çıkar
        pending.shift();
      } catch (error) {
        // Hata, daha sonra tekrar dene
        break;
      }
    }

    await AsyncStorage.setItem('pending_actions', JSON.stringify(pending));
  }
}

// Network durumunu dinle
NetInfo.addEventListener(state => {
  if (state.isConnected) {
    // İnternet geri geldi, pending actions'ları senkronize et
    OfflineManager.syncPendingActions();
  }
});

Performance Optimization

Optimizasyon Teknikleri:

Image caching: react-native-fast-image kullan
Lazy loading: FlatList için initialNumToRender=10
Memoization: React.memo(), useMemo(), useCallback()
Bundle size: Hermes engine kullan (Android)
Network: GraphQL + caching (Apollo Client)
Analytics: Crash reporting (Sentry, Firebase Crashlytics)
// Performance best practices
import React, { memo, useMemo, useCallback } from 'react';
import FastImage from 'react-native-fast-image';

// 1. Memo ile gereksiz render'ları önle
const ProductCard = memo(({ product, onPress }) => {
  return (
    <TouchableOpacity onPress={() => onPress(product.id)}>
      <FastImage
        source={{ uri: product.image, priority: FastImage.priority.normal }}
        style={styles.image}
        resizeMode={FastImage.resizeMode.cover}
      />
      <Text>{product.name}</Text>
      <Text>{product.price} TL</Text>
    </TouchableOpacity>
  );
});

// 2. useCallback ile fonksiyon referansını sabit tut
const ProductList = ({ products }) => {
  const handlePress = useCallback((productId) => {
    navigation.navigate('ProductDetail', { productId });
  }, []);

  // 3. useMemo ile pahalı hesaplamaları cache'le
  const sortedProducts = useMemo(() => {
    return products.sort((a, b) => b.rating - a.rating);
  }, [products]);

  return (
    <FlatList
      data={sortedProducts}
      renderItem={({ item }) => (
        <ProductCard product={item} onPress={handlePress} />
      )}
      keyExtractor={item => item.id}
      initialNumToRender={10}
      maxToRenderPerBatch={10}
      windowSize={5}
      removeClippedSubviews={true}
      getItemLayout={(data, index) => ({
        length: ITEM_HEIGHT,
        offset: ITEM_HEIGHT * index,
        index
      })}
    />
  );
};

Geliştirme Süreci ve Maliyet

ÖzellikSüreMaliyetÖncelik
Temel UI/UX (ürün, sepet, profil)4 hafta60K TLMust-have
Ödeme entegrasyonu2 hafta25K TLMust-have
Push notification1 hafta15K TLShould-have
Offline mode2 hafta20K TLNice-to-have
Test + yayınlama (App Store, Play Store)2 hafta30K TLMust-have

Toplam Maliyet (MVP):

Geliştirme: 150K TL (React Native, 11 hafta)
Tasarım: 30K TL (UI/UX designer)
Backend: 50K TL (API geliştirme, varsa)
App Store/Play Store: 2.5K TL/yıl
Toplam: 230K TL

Mobil Marketplace Uygulamanızı Geliştirelim

React Native veya Flutter ile iOS/Android e-ticaret uygulaması. Push notification, ödeme, offline mode ile tam özellikli çözüm.

Demo İste