Flutter uygulamasında mesaj alın

Gelen mesajlar, cihazın durumuna bağlı olarak farklı şekilde işlenir. Bu senaryoları ve FCM'yi kendi uygulamanıza nasıl entegre edeceğinizi anlamak için öncelikle bir cihazın bulunabileceği çeşitli durumları belirlemek önemlidir:

Eyalet Açıklama
Ön plan Uygulama açık, görünür ve kullanımdayken
Arka plan Uygulama açıkken ancak arka planda (küçültülmüş) olduğunda Bu durum genellikle kullanıcı cihazdaki "Ana Sayfa" düğmesine bastığında, uygulama değiştiriciyi kullanarak başka bir uygulamaya geçtiğinde veya uygulamayı farklı bir sekmede (web) açtığında meydana gelir.
Sona erdirildi Cihaz kilitliyken veya uygulama çalışmıyorken

Uygulamanın FCM üzerinden mesaj yükleri alabilmesi için karşılanması gereken birkaç ön koşul vardır:

  • Uygulama, FCM'ye kayda izin vermek için en az bir kez açılmış olmalıdır.
  • iOS'te kullanıcı, uygulama değiştiriciden uygulamayı kaydırarak kapatırsa arka plan mesajlarının tekrar çalışmaya başlaması için uygulamanın manuel olarak yeniden açılması gerekir.
  • Android'de kullanıcı, cihaz ayarlarından uygulamadan çıkmaya zorlarsa mesajların çalışmaya başlaması için uygulamanın manuel olarak yeniden açılması gerekir.
  • Web'de, web push sertifikanızla birlikte bir jeton istemiş olmanız gerekir (getToken() kullanılarak).

Mesaj alma izni isteme

iOS, macOS, web ve Android 13 (veya daha yeni) sürümlerde, FCM yüklerinin cihazınızda alınabilmesi için önce kullanıcının iznini almanız gerekir.

firebase_messaging paketi, requestPermission yöntemiyle izin istemek için basit bir API sağlar. Bu API, bildirim yükleri içeren mesajların ses çalmasını veya Siri aracılığıyla okunmasını sağlama gibi, talep etmek istediğiniz izin türünü tanımlayan bir dizi adlandırılmış bağımsız değişkeni kabul eder. Varsayılan olarak, yöntem, makul varsayılan izinler ister. Referans API, her iznin ne için kullanıldığıyla ilgili tam belgeler sağlar.

Başlamak için uygulamanızdan yöntemi çağırın (iOS'te yerel bir modal görüntülenir, web'de ise tarayıcının yerel API akışı tetiklenir):

FirebaseMessaging messaging = FirebaseMessaging.instance;

NotificationSettings settings = await messaging.requestPermission(
  alert: true,
  announcement: false,
  badge: true,
  carPlay: false,
  criticalAlert: false,
  provisional: false,
  sound: true,
);

print('User granted permission: ${settings.authorizationStatus}');

İstekten döndürülen NotificationSettings nesnesinin authorizationStatus özelliği, kullanıcının genel kararını belirlemek için kullanılabilir:

  • authorized: Kullanıcı izin verdi.
  • denied: Kullanıcı izni reddetti.
  • notDetermined: Kullanıcı henüz izin verip vermeyeceğine karar vermemiştir.
  • provisional: Kullanıcı, geçici izin verdi

NotificationSettings üzerindeki diğer özellikler, belirli bir iznin etkinleştirilip etkinleştirilmediğini veya mevcut cihazda desteklenip desteklenmediğini döndürür.

İzin verildikten ve farklı cihaz durumu türleri anlaşıldıktan sonra uygulamanız artık gelen FCM yüklerini işlemeye başlayabilir.

İleti işleme

Uygulamanızın mevcut durumuna bağlı olarak, farklı mesaj türlerinin gelen yüklerinin işlenmesi için farklı uygulamalar gerekir:

Ön plan mesajları

Uygulamanız ön plandayken mesajları işlemek için onMessage akışını dinleyin.

FirebaseMessaging.onMessage.listen((RemoteMessage message) {
  print('Got a message whilst in the foreground!');
  print('Message data: ${message.data}');

  if (message.notification != null) {
    print('Message also contained a notification: ${message.notification}');
  }
});

Akışta, yükle ilgili çeşitli bilgilerin (ör. nereden geldiği, benzersiz kimliği, gönderilme zamanı, bildirim içerip içermediği) ayrıntılı olarak verildiği bir RemoteMessage bulunur. Uygulamanız ön planda çalışırken mesaj alındığından Flutter uygulamanızın durumuna ve bağlamına doğrudan erişebilirsiniz.

Ön plan ve bildirim mesajları

Uygulama ön plandayken gelen bildirim iletileri, hem Android hem de iOS'te varsayılan olarak görünür bir bildirim göstermez. Ancak bu davranışı geçersiz kılmak mümkündür:

  • Android'de "Yüksek Öncelikli" bir bildirim kanalı oluşturmanız gerekir.
  • iOS'te uygulamanın sunum seçeneklerini güncelleyebilirsiniz.

Arka plan mesajları

Arka plan mesajlarını işleme süreci, yerel (Android ve Apple) ve web tabanlı platformlarda farklıdır.

Apple platformları ve Android

onBackgroundMessage işleyici kaydederek arka plan mesajlarını işleyin. İletiler alındığında, uygulamanız çalışmıyorken bile iletileri işlemenize olanak tanıyan bir yalıtılmış alan oluşturulur (yalnızca Android'de geçerlidir, iOS/macOS'te ayrı bir yalıtılmış alan gerekmez).

Arka plan mesajı işleyicinizle ilgili olarak unutmamanız gereken birkaç nokta vardır:

  1. Anonim işlev olmamalıdır.
  2. Üst düzey bir işlev olmalıdır (ör. başlatma gerektiren bir sınıf yöntemi olmamalıdır).
  3. Flutter 3.3.0 veya sonraki sürümler kullanılırken mesaj işleyici, işlev bildiriminin hemen üzerinde @pragma('vm:entry-point') ile açıklama eklenmelidir (aksi takdirde yayın modu için ağaç temizleme sırasında kaldırılabilir).
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
  // If you're going to use other Firebase services in the background, such as Firestore,
  // make sure you call `initializeApp` before using other Firebase services.
  await Firebase.initializeApp();

  print("Handling a background message: ${message.messageId}");
}

void main() {
  FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
  runApp(MyApp());
}

İşleyici, uygulama bağlamınızın dışında kendi izole ortamında çalıştığından uygulama durumunu güncellemek veya kullanıcı arayüzünü etkileyen herhangi bir mantığı yürütmek mümkün değildir. Ancak HTTP istekleri gibi mantıklar yürütebilir, G/Ç işlemleri (ör. yerel depolamayı güncelleme) gerçekleştirebilir, diğer eklentilerle iletişim kurabilirsiniz.

Mantığınızı en kısa sürede tamamlamanız da önerilir. Uzun ve yoğun görevler çalıştırmak cihaz performansını etkiler ve işletim sisteminin işlemi sonlandırmasına neden olabilir. Görevler 30 saniyeden uzun sürerse cihaz işlemi otomatik olarak sonlandırabilir.

Web

Web'de arka planda çalışan bir JavaScript Service Worker yazın. Arka plan mesajlarını işlemek için servis çalışanını kullanın.

Başlamak için web dizininizde yeni bir dosya oluşturun ve bu dosyayı firebase-messaging-sw.js olarak adlandırın:

// Please see this file for the latest firebase-js-sdk version:
// https://github.com/firebase/flutterfire/blob/main/packages/firebase_core/firebase_core_web/lib/src/firebase_sdk_version.dart
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-app-compat.js");
importScripts("https://www.gstatic.com/firebasejs/10.7.0/firebase-messaging-compat.js");

firebase.initializeApp({
  apiKey: "...",
  authDomain: "...",
  databaseURL: "...",
  projectId: "...",
  storageBucket: "...",
  messagingSenderId: "...",
  appId: "...",
});

const messaging = firebase.messaging();

// Optional:
messaging.onBackgroundMessage((message) => {
  console.log("onBackgroundMessage", message);
});

Dosya hem uygulama hem de mesajlaşma SDK'larını içe aktarmalı, Firebase'i başlatmalı ve messaging değişkenini kullanıma sunmalıdır.

Ardından, çalışanın kaydı yapılmalıdır. index.html dosyasında, Flutter'ı başlatan <script> etiketini değiştirerek çalışanı kaydedin:

<script src="flutter_bootstrap.js" async>
  if ('serviceWorker' in navigator) {
    window.addEventListener('load', function () {
      navigator.serviceWorker.register('firebase-messaging-sw.js', {
        scope: '/firebase-cloud-messaging-push-scope',
      });
    });
  }
</script>

Hâlâ eski şablon sistemini kullanıyorsanız Flutter'ı başlatmak için kullanılan <script> etiketini aşağıdaki şekilde değiştirerek çalışanı kaydedebilirsiniz:

<html>
<body>
  <script>
      var serviceWorkerVersion = null;
      var scriptLoaded = false;
      function loadMainDartJs() {
        if (scriptLoaded) {
          return;
        }
        scriptLoaded = true;
        var scriptTag = document.createElement('script');
        scriptTag.src = 'main.dart.js';
        scriptTag.type = 'application/javascript';
        document.body.append(scriptTag);
      }

      if ('serviceWorker' in navigator) {
        // Service workers are supported. Use them.
        window.addEventListener('load', function () {
          // Register Firebase Messaging service worker.
          navigator.serviceWorker.register('firebase-messaging-sw.js', {
            scope: '/firebase-cloud-messaging-push-scope',
          });

          // Wait for registration to finish before dropping the <script> tag.
          // Otherwise, the browser will load the script multiple times,
          // potentially different versions.
          var serviceWorkerUrl =
            'flutter_service_worker.js?v=' + serviceWorkerVersion;

          navigator.serviceWorker.register(serviceWorkerUrl).then((reg) => {
            function waitForActivation(serviceWorker) {
              serviceWorker.addEventListener('statechange', () => {
                if (serviceWorker.state == 'activated') {
                  console.log('Installed new service worker.');
                  loadMainDartJs();
                }
              });
            }
            if (!reg.active && (reg.installing || reg.waiting)) {
              // No active web worker and we have installed or are installing
              // one for the first time. Simply wait for it to activate.
              waitForActivation(reg.installing ?? reg.waiting);
            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
              // When the app updates the serviceWorkerVersion changes, so we
              // need to ask the service worker to update.
              console.log('New service worker available.');
              reg.update();
              waitForActivation(reg.installing);
            } else {
              // Existing service worker is still good.
              console.log('Loading app from service worker.');
              loadMainDartJs();
            }
          });

          // If service worker doesn't succeed in a reasonable amount of time,
          // fallback to plaint <script> tag.
          setTimeout(() => {
            if (!scriptLoaded) {
              console.warn(
                'Failed to load app from service worker. Falling back to plain <script> tag.'
              );
              loadMainDartJs();
            }
          }, 4000);
        });
      } else {
        // Service workers not supported. Just drop the <script> tag.
        loadMainDartJs();
      }
  </script>
</body>

Ardından, Flutter uygulamanızı yeniden başlatın. Çalışan kaydedilir ve arka plandaki tüm mesajlar bu dosya üzerinden işlenir.

Etkileşimi İşleme

Bildirimler görünür bir ipucu olduğundan kullanıcılar genellikle bildirimlerle etkileşim kurar (basarak). Hem Android hem de iOS'te varsayılan davranış, uygulamanın açılmasıdır. Uygulama sonlandırılmışsa başlatılır, arka plandaysa ön plana getirilir.

Bildirimin içeriğine bağlı olarak, uygulama açıldığında kullanıcının etkileşimini yönetmek isteyebilirsiniz. Örneğin, bir bildirim aracılığıyla yeni bir sohbet mesajı gönderilirse ve kullanıcı bu bildirime basarsa uygulama açıldığında ilgili görüşmeyi açmak isteyebilirsiniz.

firebase-messaging paketi, bu etkileşimi ele almanın iki yolunu sunar:

  • getInitialMessage(): Uygulama sonlandırılmış durumdan açılırsa RemoteMessage içeren bir Future döndürülür. RemoteMessage kullanıldıktan sonra kaldırılır.
  • onMessageOpenedApp: Uygulama arka plan durumundan açıldığında RemoteMessage yayınlayan bir Stream.

Kullanıcılarınızın sorunsuz bir kullanıcı deneyimi yaşamasını sağlamak için her iki senaryonun da ele alınması önerilir. Aşağıdaki kod örneğinde bunun nasıl yapılabileceği açıklanmaktadır:

class Application extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _Application();
}

class _Application extends State<Application> {
  // It is assumed that all messages contain a data field with the key 'type'
  Future<void> setupInteractedMessage() async {
    // Get any messages which caused the application to open from
    // a terminated state.
    RemoteMessage? initialMessage =
        await FirebaseMessaging.instance.getInitialMessage();

    // If the message also contains a data property with a "type" of "chat",
    // navigate to a chat screen
    if (initialMessage != null) {
      _handleMessage(initialMessage);
    }

    // Also handle any interaction when the app is in the background via a
    // Stream listener
    FirebaseMessaging.onMessageOpenedApp.listen(_handleMessage);
  }

  void _handleMessage(RemoteMessage message) {
    if (message.data['type'] == 'chat') {
      Navigator.pushNamed(context, '/chat',
        arguments: ChatArguments(message),
      );
    }
  }

  @override
  void initState() {
    super.initState();

    // Run code required to handle interacted messages in an async function
    // as initState() must not be async
    setupInteractedMessage();
  }

  @override
  Widget build(BuildContext context) {
    return Text("...");
  }
}

Etkileşimi nasıl ele alacağınız, uygulama kurulumunuza bağlıdır. Yukarıdaki örnekte, StatefulWidget kullanılarak oluşturulmuş temel bir görsel gösterilmektedir.

Mesajları Yerelleştirme

Yerelleştirilmiş dizeleri iki farklı şekilde gönderebilirsiniz:

  • Her kullanıcınızın tercih ettiği dili sunucunuzda saklayın ve her dil için özelleştirilmiş bildirimler gönderin.
  • Yerelleştirilmiş dizeleri uygulamanıza yerleştirin ve işletim sisteminin yerel ayarlarından yararlanın

İkinci yöntemi kullanmak için:

Android

  1. resources/values/strings.xml içinde varsayılan dil mesajlarınızı belirtin:

    <string name="notification_title">Hello world</string>
    <string name="notification_message">This is a message</string>
    
  2. values-language dizininde çevrilmiş mesajları belirtin. Örneğin, resources/values-fr/strings.xml dilinde Fransızca mesajlar belirtin:

    <string name="notification_title">Bonjour le monde</string>
    <string name="notification_message">C'est un message</string>
    
  3. Sunucu yükünde, title, message ve body anahtarlarını kullanmak yerine yerelleştirilmiş mesajınız için title_loc_key ve body_loc_key anahtarlarını kullanın ve bunları göstermek istediğiniz mesajın name özelliğine ayarlayın.

    Mesaj yükü şöyle görünür:

    {
      "android": {
         "notification": {
           "title_loc_key": "notification_title",
           "body_loc_key": "notification_message"
         }
      }
    }
    

iOS

  1. Base.lproj/Localizable.strings içinde varsayılan dil mesajlarınızı belirtin:

    "NOTIFICATION_TITLE" = "Hello World";
    "NOTIFICATION_MESSAGE" = "This is a message";
    
  2. language.lproj dizininde çevrilmiş mesajları belirtin. Örneğin, fr.lproj/Localizable.strings dilinde Fransızca mesajlar belirtin:

    "NOTIFICATION_TITLE" = "Bonjour le monde";
    "NOTIFICATION_MESSAGE" = "C'est un message";
    

    Mesaj yükü şöyle görünür:

    {
      "apns": {
         "payload": {
           "alert": {
             "title-loc-key": "NOTIFICATION_TITLE",
             "loc-key": "NOTIFICATION_MESSAGE"
           }
         }
      }
    }
    

İleti teslimi verilerini dışa aktarmayı etkinleştirme

Daha ayrıntılı analiz için ileti verilerinizi BigQuery'ye aktarabilirsiniz. BigQuery, verileri BigQuery SQL kullanarak analiz etmenize, başka bir bulut sağlayıcıya aktarmanıza veya özel makine öğrenimi modelleriniz için kullanmanıza olanak tanır. BigQuery'ye yapılan dışa aktarma işlemine, mesaj türü veya mesajın API ya da Bildirim Oluşturucu aracılığıyla gönderilip gönderilmediğine bakılmaksızın mesajlarla ilgili tüm veriler dahil edilir.

Dışa aktarma özelliğini etkinleştirmek için önce burada açıklanan adımları, ardından aşağıdaki talimatları uygulayın:

Android

Aşağıdaki kodu kullanabilirsiniz:

await FirebaseMessaging.instance.setDeliveryMetricsExportToBigQuery(true);

iOS

iOS için AppDelegate.m simgesini aşağıdaki içerikle değiştirmeniz gerekir.

#import "AppDelegate.h"
#import "GeneratedPluginRegistrant.h"
#import <Firebase/Firebase.h>

@implementation AppDelegate

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];
  // Override point for customization after application launch.
  return [super application:application didFinishLaunchingWithOptions:launchOptions];
}

- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)userInfo
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  [[FIRMessaging extensionHelper] exportDeliveryMetricsToBigQueryWithMessageInfo:userInfo];
}

@end

Web

Web için SDK'nın v9 sürümünü kullanmak üzere hizmet çalışanınızı değiştirmeniz gerekir. v9 sürümünün paketlenmesi gerektiğinden, hizmet çalışanının çalışması için örneğin esbuild gibi bir paketleyici kullanmanız gerekir. Bunu nasıl yapacağınızı öğrenmek için örnek uygulamaya bakın.

v9 SDK'ya geçiş yaptıktan sonra aşağıdaki kodu kullanabilirsiniz:

import {
  experimentalSetDeliveryMetricsExportedToBigQueryEnabled,
  getMessaging,
} from 'firebase/messaging/sw';
...

const messaging = getMessaging(app);
experimentalSetDeliveryMetricsExportedToBigQueryEnabled(messaging, true);

Yeni hizmet çalışanı sürümünüzü web klasörüne aktarmak için yarn build komutunu çalıştırmayı unutmayın.

iOS'te bildirimlerde resimleri gösterme

Apple cihazlarda, gelen FCM bildirimlerinin FCM yükündeki resimleri göstermesi için ek bir bildirim hizmeti uzantısı eklemeniz ve uygulamanızı bu uzantıyı kullanacak şekilde yapılandırmanız gerekir.

Firebase telefon kimlik doğrulaması kullanıyorsanız Firebase Auth pod'unu Podfile'ınıza eklemeniz gerekir.

1. adım: Bildirim hizmeti uzantısı ekleyin

  1. Xcode'da File > New > Target... (Dosya > Yeni > Hedef...) seçeneğini tıklayın.
  2. Bir modal pencerede olası hedeflerin listesi gösterilir. Bildirim Hizmeti Uzantısı'nı seçmek için aşağı kaydırın veya filtreyi kullanın. Next'i (Sonraki) tıklayın.
  3. Bir ürün adı ekleyin (bu eğitimde ilerlemek için "ImageNotification"ı kullanın), Swift veya Objective-C seçeneğini belirleyin ve Bitir'i tıklayın.
  4. Etkinleştir'i tıklayarak planı etkinleştirin.

2. adım: Hedefi Podfile'a ekleyin

Swift

Yeni uzantınızın, FirebaseMessaging swift paketine erişebildiğinden emin olmak için paketi Runner hedefinize ekleyin:

  1. Gezgin'den Firebase Apple platformları SDK'sını ekleyin: File > Add Package Dependencies... (Dosya > Paket Bağımlılıkları Ekle...)

  2. Paket URL'sini arayın veya girin: https://github.com/firebase/firebase-ios-sdk

  3. Projeye ekle Runner: Paket Ekle

  4. FirebaseMessaging'i seçin ve hedef ImageNotification'a ekleyin: Paket Ekle

Objective-C

Podfile'a ekleyerek yeni uzantınızın Firebase/Messaging pod'una erişebildiğinden emin olun:

  1. Gezgin'den Podfile'ı açın: Pods > Podfile

  2. Dosyanın en altına ilerleyip şunları ekleyin:

    target 'ImageNotification' do
      use_frameworks!
      pod 'Firebase/Auth' # Add this line if you are using FirebaseAuth phone authentication
      pod 'Firebase/Messaging'
    end
    
  3. pod install kullanarak ios veya macos dizininden pod'lerinizi yükleyin veya güncelleyin.

3. adım: Uzantı yardımcısını kullanın

Bu noktada her şey normal şekilde çalışmaya devam etmelidir. Son adım, uzantı yardımcısını çağırmaktır.

Swift

  1. Gezginden ImageNotification uzantınızı seçin.

  2. NotificationService.swift dosyasını açın.

  3. NotificationService.swift içeriğini şununla değiştirin:

    import UserNotifications
    import FirebaseMessaging
    
    class NotificationService: UNNotificationServiceExtension {
    
        var contentHandler: ((UNNotificationContent) -> Void)?
        var bestAttemptContent: UNMutableNotificationContent?
    
        override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
            self.contentHandler = contentHandler
            bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
    
            Messaging.serviceExtension().populateNotificationContent(bestAttemptContent!, withContentHandler: contentHandler)
        }
    
        override func serviceExtensionTimeWillExpire() {
            if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent {
                contentHandler(bestAttemptContent)
            }
        }
    }
    

Objective-C

  1. Gezginden ImageNotification uzantınızı seçin.

  2. NotificationService.m dosyasını açın.

  3. Dosyanın en üstünde, aşağıda gösterildiği gibi FirebaseMessaging.h simgesini NotificationService.h simgesinden hemen sonra içe aktarın.

    NotificationService.m içeriğini şununla değiştirin:

    #import "NotificationService.h"
    #import "FirebaseMessaging.h"
    #import <FirebaseAuth/FirebaseAuth-Swift.h> // Add this line if you are using FirebaseAuth phone authentication
    #import <UIKit/UIKit.h> // Add this line if you are using FirebaseAuth phone authentication
    
    @interface NotificationService () <NSURLSessionDelegate>
    
    @property(nonatomic) void (^contentHandler)(UNNotificationContent *contentToDeliver);
    @property(nonatomic) UNMutableNotificationContent *bestAttemptContent;
    
    @end
    
    @implementation NotificationService
    
    /* Uncomment this if you are using Firebase Auth
    - (BOOL)application:(UIApplication *)app
                openURL:(NSURL *)url
                options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
      if ([[FIRAuth auth] canHandleURL:url]) {
        return YES;
      }
      return NO;
    }
    
    - (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts {
      for (UIOpenURLContext *urlContext in URLContexts) {
        [FIRAuth.auth canHandleURL:urlContext.URL];
      }
    }
    */
    
    - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
        self.contentHandler = contentHandler;
        self.bestAttemptContent = [request.content mutableCopy];
    
        // Modify the notification content here...
        [[FIRMessaging extensionHelper] populateNotificationContent:self.bestAttemptContent withContentHandler:contentHandler];
    }
    
    - (void)serviceExtensionTimeWillExpire {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        self.contentHandler(self.bestAttemptContent);
    }
    
    @end
    

4. adım: Görüntüyü yükleme işlemine ekleyin

Artık bildirim yükünüze resim ekleyebilirsiniz. Gönderme isteği oluşturma ile ilgili iOS belgelerine bakın. Cihazın maksimum 300 KB resim boyutu uyguladığını unutmayın.