<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nuh Mehmet Demirkol</title>
    <description>The latest articles on DEV Community by Nuh Mehmet Demirkol (@nmdemirkol).</description>
    <link>https://dev.to/nmdemirkol</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4170535%2F4ed79ab9-c933-4922-bae3-7c4f28f87233.png</url>
      <title>DEV Community: Nuh Mehmet Demirkol</title>
      <link>https://dev.to/nmdemirkol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nmdemirkol"/>
    <language>en</language>
    <item>
      <title>Offline-First Mobil Mimari: Isar, SQLite ve REST API ile Kusursuz Senkronizasyon</title>
      <dc:creator>Nuh Mehmet Demirkol</dc:creator>
      <pubDate>Fri, 09 Oct 2026 06:25:25 +0000</pubDate>
      <link>https://dev.to/nmdemirkol/offline-first-mobil-mimari-isar-sqlite-ve-rest-api-ile-kusursuz-senkronizasyon-35hj</link>
      <guid>https://dev.to/nmdemirkol/offline-first-mobil-mimari-isar-sqlite-ve-rest-api-ile-kusursuz-senkronizasyon-35hj</guid>
      <description>&lt;h1&gt;
  
  
  Offline-First Mobil Mimari: Isar, SQLite ve REST API ile Kusursuz Senkronizasyon
&lt;/h1&gt;

&lt;p&gt;Günümüz mobil uygulama ekosisteminde kullanıcı deneyimini (UX) belirleyen en kritik unsurlardan biri, uygulamanın ağ bağlantısından bağımsız olarak ne kadar kararlı çalıştığıdır. Tünelde giden bir metroda, sinyalin zayıf olduğu kırsal bir bölgede ya da uçak modunda bile uygulamanızın çökmeden, takılmadan ve veri kaybetmeden çalışması gerekir.&lt;/p&gt;

&lt;p&gt;Bu makalede, modern mobil uygulamalarda &lt;strong&gt;Offline-First (Önce Çevrimdışı)&lt;/strong&gt; mimariyi kurmanın teorik temellerini, yerel veri tabanı (Local DB) seçim kriterlerini ve yerel verileri bir &lt;strong&gt;REST API&lt;/strong&gt; ile kusursuz bir şekilde senkronize etmenin mühendislik pratiklerini ele alacağız.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Offline-First Mimari Nedir ve Neden Önemlidir?
&lt;/h2&gt;

&lt;p&gt;Offline-First, uygulamanın birincil veri kaynağı (Single Source of Truth) olarak yerel veri tabanını (Local Database) kabul ettiği bir tasarım örüntüsüdür. Uygulama ağ isteklerinin sonucunu beklemek yerine, doğrudan cihaz üzerindeki yerel veriyi okur ve yazar. Arka planda çalışan bir &lt;strong&gt;Senkronizasyon Motoru (Sync Engine)&lt;/strong&gt;, bu yerel verileri uzak sunucuyla (Cloud/REST API) asenkron olarak eşitler.&lt;/p&gt;

&lt;h3&gt;
  
  
  Avantajları:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Ultra Düşük Gecikme (Low Latency):&lt;/strong&gt; UI, ağ gecikmesine (network latency) takılmadan yerel veri tabanından anında beslenir.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Kesintisiz Çalışabilirlik:&lt;/strong&gt; İnternet bağlantısı koptuğunda dahi tam fonksiyonellik sunulur.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Daha Az Pil ve Veri Tüketimi:&lt;/strong&gt; Ağ istekleri optimize edilerek paketler halinde (batching) gönderilir.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Mimari Tasarım Şeması
&lt;/h2&gt;

&lt;p&gt;Kusursuz bir Offline-First mimari, katmanlı bir yapıda tasarlanmalıdır. İş mantığı (Business Logic), veri tabanı motorundan ve ağ katmanından tamamen izole edilmelidir.&lt;/p&gt;

&lt;p&gt;+-------------------------------------------------------------+&lt;br&gt;
|                        UI Katmanı                           |&lt;br&gt;
+-------------------------------------------------------------+&lt;br&gt;
                              │ (Reactive Streams / Streams)&lt;br&gt;
                              ▼&lt;br&gt;
+-------------------------------------------------------------+&lt;br&gt;
|                     Repository Katmanı                      |&lt;br&gt;
|             (Verinin nereden geleceğini yönetir)            |&lt;br&gt;
+-------------------------------------------------------------+&lt;br&gt;
          │                                           │&lt;br&gt;
          ▼ (Okuma / Yazma)                           ▼ (Tetikleme)&lt;br&gt;
+-------------------+                       +-----------------+&lt;br&gt;
|  Local Data Source|                       |   Sync Engine   |&lt;br&gt;
|  (Isar / SQLite)  |                       | (Senkronizasyon)|&lt;br&gt;
+-------------------+                       +-----------------+&lt;br&gt;
                                                     │&lt;br&gt;
                                                     ▼ (REST API - JSON)&lt;br&gt;
                                            +-----------------+&lt;br&gt;
                                            |Remote Data Source|&lt;br&gt;
                                            +-----------------+&lt;/p&gt;




&lt;h2&gt;
  
  
  3. Yerel Veri Tabanı Seçimi: SQLite mı, Isar mı?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Özellik&lt;/th&gt;
&lt;th&gt;SQLite (Drift / Moor)&lt;/th&gt;
&lt;th&gt;Isar (NoSQL)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Tür&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;İlişkisel (SQL)&lt;/td&gt;
&lt;td&gt;Doküman / Nesne Tabanlı (NoSQL)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hız&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Orta / İyi&lt;/td&gt;
&lt;td&gt;Çok Hızlı (Asenkron &amp;amp; Çoklu İzlek)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Reaktivite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stream desteği var (Üçüncü parti kütüphanelerle)&lt;/td&gt;
&lt;td&gt;Doğal reaktif (Query Watchers)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Şema Göçü (Migration)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Manuel SQL sorguları ile zorlayıcı&lt;/td&gt;
&lt;td&gt;Otomatik şema yönetimi ile kolay&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Öneri:&lt;/strong&gt; Eğer uygulamanız karmaşık çoklu tablolara ve ilişkisel ACID işlemlerine yoğun şekilde ihtiyaç duyuyorsa &lt;strong&gt;SQLite (Drift)&lt;/strong&gt;; performans, reaktivite, kolay geliştirme ve Flutter ile mükemmel uyum arıyorsanız modern &lt;strong&gt;Isar Database&lt;/strong&gt; tercih edilmelidir.&lt;/p&gt;




&lt;h2&gt;
  
  
  4. Senkronizasyon Stratejileri ve Veri Modeli Tasarımı
&lt;/h2&gt;

&lt;p&gt;Verilerin senkronize edilebilmesi için veri modellerimizde bazı meta-verilere (meta-fields) ihtiyacımız vardır. Her tablo/doküman şu alanları barındırmalıdır:&lt;/p&gt;

&lt;p&gt;dart&lt;br&gt;
abstract class SyncableEntity {&lt;br&gt;
  String get id; // Evrensel tekil kimlik (UUID v4)&lt;br&gt;
  DateTime get lastUpdatedAt; // Son güncellenme zamanı&lt;br&gt;
  bool get isDirty; // Sunucuya gönderilmeyi bekleyen yerel değişiklik var mı?&lt;br&gt;
  bool get isDeleted; // Soft-delete (Yumuşak silme) bayrağı&lt;br&gt;
}&lt;/p&gt;

&lt;h3&gt;
  
  
  Kritik Tasarım Örüntüsü: Outbox (Giden Kutusu) Tasarımı
&lt;/h3&gt;

&lt;p&gt;Kullanıcı çevrimdışıyken bir işlem gerçekleştirdiğinde, bu işlem doğrudan sunucuya gönderilemediği için bir &lt;strong&gt;Outbox&lt;/strong&gt; tablosuna kuyruğa alınır.&lt;/p&gt;

&lt;p&gt;dart&lt;br&gt;
import 'package:isar/isar.dart';&lt;/p&gt;

&lt;p&gt;part 'outbox_action.g.dart';&lt;/p&gt;

&lt;p&gt;@collection&lt;br&gt;
class OutboxAction {&lt;br&gt;
  Id? id = Isar.autoIncrement;&lt;/p&gt;

&lt;p&gt;@Index(unique: true)&lt;br&gt;
  late String entityId; // Değişen kaydın ID'si&lt;/p&gt;

&lt;p&gt;late String entityName; // 'Task', 'User', 'Order' vb.&lt;/p&gt;

&lt;p&gt;@enumerated&lt;br&gt;
  late ActionType actionType; // CREATE, UPDATE, DELETE&lt;/p&gt;

&lt;p&gt;late String payload; // Değişikliğin JSON hali&lt;/p&gt;

&lt;p&gt;late DateTime createdAt;&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;enum ActionType { create, update, delete }&lt;/p&gt;




&lt;h2&gt;
  
  
  5. Senkronizasyon Algoritması (Sync Engine)
&lt;/h2&gt;

&lt;p&gt;Senkronizasyon motoru iki yönlü çalışır: &lt;strong&gt;Upstream (Yerelden Sunucuya)&lt;/strong&gt; ve &lt;strong&gt;Downstream (Sunucudan Yerelle)&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adım 1: Upstream (Yerel Değişikliklerin Sunucuya İletilmesi)
&lt;/h3&gt;

&lt;p&gt;dart&lt;br&gt;
class SyncEngine {&lt;br&gt;
  final Isar _localDb;&lt;br&gt;
  final ApiClient _apiClient;&lt;/p&gt;

&lt;p&gt;SyncEngine(this._localDb, this._apiClient);&lt;/p&gt;

&lt;p&gt;Future syncUpstream() async {&lt;br&gt;
    // 1. Bekleyen yerel işlemleri oku&lt;br&gt;
    final pendingActions = await _localDb.outboxActions&lt;br&gt;
        .where()&lt;br&gt;
        .sortByCreatedAt()&lt;br&gt;
        .findAll();&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;for (var action in pendingActions) {
  try {
    bool isSuccess = false;

    switch (action.actionType) {
      case ActionType.create:
        isSuccess = await _apiClient.post(
          '/api/${action.entityName.toLowerCase()}',
          action.payload,
        );
        break;
      case ActionType.update:
        isSuccess = await _apiClient.put(
          '/api/${action.entityName.toLowerCase()}/${action.entityId}',
          action.payload,
        );
        break;
      case ActionType.delete:
        isSuccess = await _apiClient.delete(
          '/api/${action.entityName.toLowerCase()}/${action.entityId}',
        );
        break;
    }

    if (isSuccess) {
      // İşlem başarılı, kuyruktan sil
      await _localDb.writeTxn(() async {
        await _localDb.outboxActions.delete(action.id!);
      });
    }
  } catch (e) {
    // Hata durumunda loglama yap ve bir sonraki senkronizasyon döngüsünü bekle
    print("Senkronizasyon hatası: $e");
    break;
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;

&lt;h3&gt;
  
  
  Adım 2: Downstream (Sunucudaki Değişikliklerin Yerelle Eşitlenmesi)
&lt;/h3&gt;

&lt;p&gt;Çakışmaları önlemek için sunucu tarafındaki güncel veriler çekilirken en son başarılı senkronizasyon zaman damgası (&lt;code&gt;lastSyncTimestamp&lt;/code&gt;) parametre olarak gönderilir.&lt;/p&gt;

&lt;p&gt;dart&lt;br&gt;
Future syncDownstream() async {&lt;br&gt;
  final lastSync = await _getLastSyncTimestamp();&lt;/p&gt;

&lt;p&gt;// Sadece son senkronizasyondan sonra değişen verileri iste&lt;br&gt;
  final response = await _apiClient.get('/api/sync?since=${lastSync.toIso8601String()}');&lt;/p&gt;

&lt;p&gt;if (response.statusCode == 200) {&lt;br&gt;
    final List remoteChanges = response.data['changes'];&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;await _localDb.writeTxn(() async {
  for (var change in remoteChanges) {
    // Çakışma Kontrolü: Eğer yerelde isDirty true ise yerel veriyi ezme (Conflict Resolution)
    final localEntity = await _localDb.tasks.filter().idEqualTo(change['id']).findFirst();

    if (localEntity == null || !localEntity.isDirty) {
      // Yerelde değişiklik yoksa veya kayıt yeniyse güncelle
      await _localDb.tasks.put(Task.fromJson(change));
    }
  }
});

await _saveLastSyncTimestamp(DateTime.now());
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
}&lt;/p&gt;




&lt;h2&gt;
  
  
  6. Çakışma Çözümleme (Conflict Resolution) Stratejileri
&lt;/h2&gt;

&lt;p&gt;Çevrimdışı dünyada aynı veri hem sunucuda hem de yerelde güncellenmiş olabilir. Bu durumlar için şu stratejilerden biri seçilmelidir:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Last-Write-Wins (LWW):&lt;/strong&gt; Son güncelleyen kazanır. Zaman damgası (Timestamp) en büyük olan veri kabul edilir.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Server-Wins:&lt;/strong&gt; Sunucudaki veri her zaman doğru kabul edilir, yerel değişiklikler ezilir.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Client-Wins:&lt;/strong&gt; Kullanıcının cihazındaki değişiklik önceliklidir.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Merge (Birleştirme):&lt;/strong&gt; Çakışan alanlar analiz edilerek birleştirilir (Örn: Farklı alanlar güncellendiyse ikisi de korunur).&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Sonuç
&lt;/h2&gt;

&lt;p&gt;Offline-First mimari kurmak, ilk etapta karmaşık görünse de doğru veri tabanı seçimi (Isar/SQLite) ve sağlam bir &lt;strong&gt;Outbox Tasarımı&lt;/strong&gt; ile uygulamanızın kalitesini bambaşka bir seviyeye taşır. Ağ hatalarını birer istisna olmaktan çıkarıp uygulamanızın doğal akışının bir parçası haline getirmek, kullanıcı sadakatini artırmanın en kesin yoludur.&lt;/p&gt;




&lt;h3&gt;
  
  
  Profesyonel Destek ve İletişim
&lt;/h3&gt;

&lt;p&gt;Kurumsal mobil uygulamalarınızda &lt;strong&gt;yüksek performanslı veri tabanı mimarileri&lt;/strong&gt;, &lt;strong&gt;senkronizasyon algoritmaları&lt;/strong&gt; ve &lt;strong&gt;Offline-First stratejileri&lt;/strong&gt; geliştirmek için bir uzmana mı ihtiyacınız var?&lt;/p&gt;

&lt;p&gt;Ankara başta olmak üzere tüm dünya çapındaki projelerinize vizyon katmak ve mühendislik kalitesini artırmak için benimle iletişime geçebilirsiniz.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Unvan:&lt;/strong&gt; Nuh Mehmet Demirkol - Lisanslı Bilgisayar Mühendisi&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Lokasyon:&lt;/strong&gt; Ankara, Türkiye&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Portfolyo ve Hizmetler:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com" rel="noopener noreferrer"&gt;nmdemirkol.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Doğrudan İletişim (WhatsApp):&lt;/strong&gt; &lt;a href="https://wa.me/905078236811" rel="noopener noreferrer"&gt;+90 507 823 68 11&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;Geleceğin mobil mimarilerini bugünden inşa edelim.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>mobile</category>
      <category>flutter</category>
      <category>architecture</category>
    </item>
    <item>
      <title>App Store Retlerinden Kaçınma: Guideline 2.1 (Performance) ve 4.8 (Sign in with Apple) Engellerini Aşmak İçin Mimari Çözümler</title>
      <dc:creator>Nuh Mehmet Demirkol</dc:creator>
      <pubDate>Thu, 08 Oct 2026 07:39:40 +0000</pubDate>
      <link>https://dev.to/nmdemirkol/app-store-retlerinden-kacinma-guideline-21-performance-ve-48-sign-in-with-apple-engellerini-3l30</link>
      <guid>https://dev.to/nmdemirkol/app-store-retlerinden-kacinma-guideline-21-performance-ve-48-sign-in-with-apple-engellerini-3l30</guid>
      <description>&lt;h1&gt;
  
  
  App Store Retlerinden Kaçınma: Guideline 2.1 ve 4.8 Engellerini Aşmak İçin Mimari Çözümler
&lt;/h1&gt;

&lt;p&gt;Mobil uygulama geliştirme süreçlerinde en büyük darboğazlardan biri, haftalarca süren emeğin ardından karşılaşılan App Store retleridir (App Store Rejections). Apple'ın katı denetim mekanizması, özellikle &lt;strong&gt;Guideline 2.1 (Performance - App Completeness)&lt;/strong&gt; ve &lt;strong&gt;Guideline 4.8 (Sign in with Apple)&lt;/strong&gt; maddelerinde geliştiricileri ciddi şekilde zorlamaktadır.&lt;/p&gt;

&lt;p&gt;Bu teknik makalede, bir Mobil Yazılım Mimarı gözünden, bu iki kritik ret sebebini derinlemesine inceleyecek, mimari seviyede nasıl önlemler alabileceğinizi ve Apple denetçilerinin (Reviewers) onayından tek seferde geçecek sistemleri nasıl kuracağınızı kod örnekleriyle ele alacağız.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Guideline 2.1 - App Completeness (Uygulama Bütünlüğü)
&lt;/h2&gt;

&lt;p&gt;Guideline 2.1, uygulamanızın kararlı, test edilmiş ve tüm fonksiyonlarıyla çalışır durumda olmasını şart koşar. "Under construction" ibareleri, boş ekranlar, mock veriler, ağ bağlantısı koptuğunda çöken veya tepkisiz kalan arayüzler doğrudan bu maddeden ret alır.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mimari Çözüm: State-Driven UI ve Güçlü Hata Yönetimi
&lt;/h3&gt;

&lt;p&gt;2.1 retlerini önlemenin en kesin yolu, uygulamanın her ekranını bir durum makinesi (State Machine) olarak tasarlamaktır. UI katmanı hiçbir zaman doğrudan ham veriye erişmemeli; her zaman &lt;code&gt;Idle&lt;/code&gt;, &lt;code&gt;Loading&lt;/code&gt;, &lt;code&gt;Success(Data)&lt;/code&gt; ve &lt;code&gt;Failure(Error)&lt;/code&gt; durumlarından birini render etmelidir.&lt;/p&gt;

&lt;p&gt;Aşağıdaki Swift/Combine örneğinde, ağ hatası durumunda uygulamayı kilitlemek yerine kullanıcıya anlamlı bir kurtarma senaryosu sunan ve denetçinin uygulamadan çıkmadan süreci tekrar denemesini sağlayan (Retry Mechanism) mimariyi görebilirsiniz:&lt;/p&gt;

&lt;p&gt;swift&lt;br&gt;
import Combine&lt;br&gt;
import Foundation&lt;/p&gt;

&lt;p&gt;// 1. Ekran Durum Tanımları&lt;br&gt;
enum ViewState {&lt;br&gt;
    case idle&lt;br&gt;
    case loading&lt;br&gt;
    case success(T)&lt;br&gt;
    case failure(Error)&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// 2. Ağ Bağlantısı Takip Servisi&lt;br&gt;
class NetworkMonitor: ObservableObject {&lt;br&gt;
    static let shared = NetworkMonitor()&lt;br&gt;
    @Published var isConnected: Bool = true&lt;br&gt;
    // Gerçek senaryoda NWPathMonitor entegre edilmelidir.&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;// 3. ViewModel Mimarisi&lt;br&gt;
class ProductViewModel: ObservableObject {&lt;br&gt;
    @Published var state: ViewState&amp;lt;[String]&amp;gt; = .idle&lt;br&gt;
    private var cancellables = Set()&lt;br&gt;
    private let networkMonitor = NetworkMonitor.shared&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;func fetchProducts() {
    // Ağ kontrolü - Denetçinin çevrimdışı senaryoda kilitlenmesini önler
    guard networkMonitor.isConnected else {
        self.state = .failure(NSError(domain: "Network", code: -1009, userInfo: [NSLocalizedDescriptionKey: "İnternet bağlantısı bulunamadı. Lütfen kontrol edip tekrar deneyiniz."]))
        return
    }

    self.state = .loading

    // Mock API Call simulating network
    Just(["Premium Üyelik", "Sınırsız Depolama", "Yapay Zeka Asistanı"])
        .delay(for: .seconds(1), scheduler: RunLoop.main)
        .map { ViewState.success($0) }
        .catch { Just(ViewState.failure($0)) }
        .assign(to: \.state, on: self)
        .store(in: &amp;amp;cancellables)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;h3&gt;
  
  
  Apple Denetçilerine Özel İpuçları (Guideline 2.1 İçin):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Demo Hesapları:&lt;/strong&gt; Uygulamanız iki adımlı doğrulama (2FA) veya SMS doğrulaması kullanıyorsa, Apple denetçileri için çalışan static bir test telefon numarası ve OTP kodu (örn: +90 555 555 55 55 / Code: 123456) tanımlayın.&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Mock Veri Kontrolü:&lt;/strong&gt; "Lorem Ipsum" içeren metinleri ve geçici görsel asset'lerini prodüksiyon build'inden kesinlikle temizleyin.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  2. Guideline 4.8 - Sign in with Apple (Apple ile Giriş)
&lt;/h2&gt;

&lt;p&gt;Eğer uygulamanızda Google, Facebook veya X (Twitter) gibi üçüncü taraf sosyal giriş (Social Sign-In) yöntemleri sunuyorsanız, &lt;strong&gt;Sign in with Apple&lt;/strong&gt; seçeneğini de eşdeğer bir görsel öncelikle sunmak zorundasınız.&lt;/p&gt;

&lt;h3&gt;
  
  
  İstisnalar Nelerdir?
&lt;/h3&gt;

&lt;p&gt;Eğer uygulamanız;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  Sadece şirkete özel kurumsal hesap altyapısı kullanıyorsa,&lt;/li&gt;
&lt;li&gt;  E-posta/Şifre ile kendi veritabanınız üzerinden üyelik sunuyorsa (sosyal giriş olmadan),&lt;/li&gt;
&lt;li&gt;  Doğrudan bir devlet/kamu kimlik doğrulama sistemi kullanıyorsa Apple ile Giriş zorunlu değildir.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Mimari Çözüm: Dynamic Identity Provider Factory
&lt;/h3&gt;

&lt;p&gt;Platforma göre dinamik giriş butonları üreten ve Apple'ın tasarım kılavuzlarına (Human Interface Guidelines) tam uyumlu bir buton hiyerarşisi oluşturan Swift/SwiftUI mimarisi:&lt;/p&gt;

&lt;p&gt;swift&lt;br&gt;
import SwiftUI&lt;br&gt;
import AuthenticationServices&lt;/p&gt;

&lt;p&gt;struct LoginView: View {&lt;br&gt;
    @StateObject private var viewModel = LoginViewModel()&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;var body: some View {
    VStack(spacing: 16) {
        // Standart Giriş Alanları
        TextField("E-posta", text: $viewModel.email)
            .textFieldStyle(RoundedBorderTextFieldStyle())
            .padding()

        Button("E-posta ile Giriş Yap") {
            viewModel.loginWithEmail()
        }
        .buttonStyle(.borderedProminent)

        Divider().padding(.vertical)

        // Sosyal Giriş Bölümü
        VStack(spacing: 12) {
            // 1. Google Giriş Butonu
            Button(action: { viewModel.loginWithGoogle() }) {
                HStack {
                    Image("google_icon") // Custom asset
                    Text("Google ile Devam Et")
                }
                .frame(maxWidth: .infinity)
                .padding()
                .background(Color.white)
                .cornerRadius(8)
                .overlay(RoundedRectangle(cornerRadius: 8).stroke(Color.gray, lineWidth: 1))
            }

            // 2. Apple ile Giriş - Guideline 4.8 Gereksinimi
            SignInWithAppleButton(
                onRequest: { request in
                    request.requestedScopes = [.fullName, .email]
                },
                onCompletion: { result in
                    switch result {
                    case .success(let authorization):
                        viewModel.handleAppleSignIn(authorization: authorization)
                    case .failure(let error):
                        print("Apple Sign-In Hatası: \(error.localizedDescription)")
                    }
                }
            )
            .signInWithAppleButtonStyle(.black)
            .frame(height: 50)
            .cornerRadius(8)
        }
        .padding(.horizontal)
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;/p&gt;

&lt;h3&gt;
  
  
  Sunucu Tarafı (Backend) Doğrulaması
&lt;/h3&gt;

&lt;p&gt;Apple Sign-In entegrasyonunda yapılan en büyük hata, client tarafında alınan &lt;code&gt;identityToken&lt;/code&gt; değerini backend tarafında doğrulamadan doğrudan kullanıcıyı içeri almaktır. JWT (JSON Web Token) formatındaki bu token, Apple'ın public anahtarları (&lt;code&gt;https://appleid.apple.com/auth/keys&lt;/code&gt;) kullanılarak arka planda doğrulanmalıdır.&lt;/p&gt;




&lt;h2&gt;
  
  
  3. App Store Gönderim Öncesi Kontrol Listesi (Checklist)
&lt;/h2&gt;

&lt;p&gt;Yayına çıkmadan önce bu kontrol listesini tamamladığınızdan emin olun:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Madde&lt;/th&gt;
&lt;th&gt;Açıklama&lt;/th&gt;
&lt;th&gt;İlgili Kılavuz&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Giriş Bilgileri&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Reviewer için çalışan, kilitlenmeyen test hesabı hazır mı?&lt;/td&gt;
&lt;td&gt;Guideline 2.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IPv6 Desteği&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Uygulama sadece IPv6 kullanan ağlarda düzgün çalışıyor mu?&lt;/td&gt;
&lt;td&gt;Guideline 2.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Giriş Koşulu&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sosyal girişlerin yanında Apple Sign-In butonu aynı boyutta var mı?&lt;/td&gt;
&lt;td&gt;Guideline 4.8&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Hesap Silme&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Kullanıcı hesap silme (Delete Account) butonu uygulama içinde kolayca erişilebilir mi?&lt;/td&gt;
&lt;td&gt;Guideline 5.1.1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Sonuç
&lt;/h2&gt;

&lt;p&gt;App Store onay süreçleri, teknik kalitenin yanında Apple'ın tasarım ve iş etiği kurallarına sıkı sıkıya bağlı kalmayı gerektirir. Uygulama mimarinizi en baştan esnek, hata toleranslı ve platform standartlarına uyumlu kurgulamak, projelerinizin zamanında canlıya çıkmasını garanti eder.&lt;/p&gt;




&lt;h3&gt;
  
  
  Profesyonel Mobil Mimari ve Süreç Yönetimi Desteği
&lt;/h3&gt;

&lt;p&gt;Uygulamanızın App Store onay süreçlerinde takıldınız mı, yoksa sıfırdan ölçeklenebilir ve Apple standartlarına tam uyumlu bir mobil mimari mi inşa etmek istiyorsunuz? Ankara merkezli mühendislik ofisimizde, küresel standartlarda mobil yazılım mimarisi, kod kalitesi denetimi (Code Review) ve App Store/Google Play Store yayın süreçleri yönetimi danışmanlığı sunuyoruz.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;  &lt;strong&gt;Yazan:&lt;/strong&gt; Nuh Mehmet Demirkol (Lisanslı Bilgisayar Mühendisi)&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Web Sitesi:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com" rel="noopener noreferrer"&gt;nmdemirkol.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Ofis Lokasyonu:&lt;/strong&gt; Ankara, Türkiye&lt;/li&gt;
&lt;li&gt;  &lt;strong&gt;Doğrudan İletişim (WhatsApp):&lt;/strong&gt; &lt;a href="https://wa.me/905078236811" rel="noopener noreferrer"&gt;+90 507 823 68 11&lt;/a&gt; &lt;em&gt;Teknik mimari, danışmanlık ve kurumsal çözümler için dilediğiniz an iletişime geçebilirsiniz.&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ios</category>
      <category>mobile</category>
      <category>apple</category>
      <category>appstore</category>
    </item>
    <item>
      <title>2026 Mobil Uygulama Geliştirme Yol Haritası: Flutter, Mimari ve Sıfır Hata Prensipleri</title>
      <dc:creator>Nuh Mehmet Demirkol</dc:creator>
      <pubDate>Thu, 08 Oct 2026 07:37:29 +0000</pubDate>
      <link>https://dev.to/nmdemirkol/2026-mobil-uygulama-gelistirme-yol-haritasi-flutter-mimari-ve-sifir-hata-prensipleri-22hg</link>
      <guid>https://dev.to/nmdemirkol/2026-mobil-uygulama-gelistirme-yol-haritasi-flutter-mimari-ve-sifir-hata-prensipleri-22hg</guid>
      <description>&lt;h1&gt;
  
  
  2026 Mobil Uygulama Geliştirme Yol Haritası: Flutter, Mimari ve Sıfır Hata Prensipleri
&lt;/h1&gt;

&lt;p&gt;Mobil ekosistemi 2026 yılında artık sadece çalışan bir uygulama sunmayı değil; sıfır çökme (0% crash rate), 120 FPS akıcılık, pil ve bellek dostu mimarileri şart koşmaktadır.&lt;/p&gt;

&lt;p&gt;Bir &lt;strong&gt;Bilgisayar Mühendisi&lt;/strong&gt; olarak hem kurumsal firmalara hem de yenilikçi girişimlere geliştirdiğim anahtar teslim mobil projelerde uyguladığım temel mühendislik ilkelerini bu makalede derledim.&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Neden Flutter ve Clean Architecture?
&lt;/h2&gt;

&lt;p&gt;Çapraz platform (Cross-platform) geliştirme süreçlerinde &lt;strong&gt;Flutter (Impeller Render Engine)&lt;/strong&gt; günümüzde yerel (native) performansı yakalamış durumdadır. Ancak yanlış durum yönetimi (State Management) projenin ilerleyen aşamalarında teknik borca (Technical Debt) yol açar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Katmanlı Mimari (Clean Architecture)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Domain Katmanı:&lt;/strong&gt; İş mantığı (Use Cases) ve arayüz bağımsız kurallar.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data Katmanı:&lt;/strong&gt; Repository pattern, API istemcileri (Dio, Retrofit) ve yerel önbellekleme (Isar, Hive, SQLite).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Presentation Katmanı:&lt;/strong&gt; BLoC / Cubit veya Riverpod ile reaktif, test edilebilir UI durumu.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight dart"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Temiz Repository Kullanım Örneği&lt;/span&gt;
&lt;span class="kd"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Either&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Failure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;UserProfile&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getUserProfile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;String&lt;/span&gt; &lt;span class="n"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  2. App Store ve Google Play Kabul Güvencesi
&lt;/h2&gt;

&lt;p&gt;Uygulama mağazalarında ret almamak için şu 3 kurala sıkı sıkıya bağlıyız:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Apple Human Interface &amp;amp; Guideline 2.1:&lt;/strong&gt; Başlatma esnasında gereksiz izin istememek ve tam ekran yükleme kilitlerini engellemek.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kişisel Verilerin Korunması (KVKK &amp;amp; GDPR):&lt;/strong&gt; Şifreli yerel depolama (Flutter Secure Storage / Keychain).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pil ve Ağ Verimliliği:&lt;/strong&gt; Arka plan görevlerinde akıllı batching ve WebSocket optimizasyonu.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  3. Girişimciler ve Şirketler İçin Kritik Tavsiye: Kod Mülkiyeti
&lt;/h2&gt;

&lt;p&gt;Mobil uygulama yaptırırken en sık yapılan hata, yazılım ajanslarının kaynak kodları teslim etmemesi veya fahiş aylık bakım ücretleri talep etmesidir. Güvenilir bir yazılım işbirliğinde:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;%100 Açık Kaynak Kod ve Git Deposu&lt;/strong&gt; müşteriye eksiksiz teslim edilmelidir.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;En az 1 Yıllık Teknik Bakım ve Hata Garantisi&lt;/strong&gt; sözleşmeye bağlanmalıdır.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canlı Mağaza Hesapları (App Store &amp;amp; Google Play)&lt;/strong&gt; müşterinin kendi kurumsal hesabında açılmalıdır.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  🚀 Projenizi Birlikte Hayata Geçirelim
&lt;/h3&gt;

&lt;p&gt;Fikir aşamasındaki veya mevcut mobil projeniz için ücretsiz mimari değerlendirme ve teklif almak için bana ulaşabilirsiniz:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mühendis:&lt;/strong&gt; Nuh Mehmet Demirkol (Bilgisayar Mühendisi)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resmi Web Sitesi:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com" rel="noopener noreferrer"&gt;nmdemirkol.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;WhatsApp / Telefon:&lt;/strong&gt; &lt;a href="https://wa.me/905078236811" rel="noopener noreferrer"&gt;+90 507 823 68 11&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Maliyet Hesaplayıcı:&lt;/strong&gt; &lt;a href="https://nmdemirkol.com/mobil-uygulama-maliyet-hesaplama" rel="noopener noreferrer"&gt;Mobil Uygulama Maliyet Hesaplama Aracı&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ofis / Konum:&lt;/strong&gt; Ankara, Türkiye&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>flutter</category>
      <category>ios</category>
      <category>android</category>
      <category>mobile</category>
    </item>
  </channel>
</rss>
