פיתוח Share Extension: יישום שיתוף נתונים ב-iOS ואנדרואיד

משתמשים רוצים לשתף תוכן מאפליקציה אחרת, אבל השירות שלכם חסר ברשימה או שהנתונים הולכים לאיבוד. אנחנו מפתחים Share Extensions עבור iOS ו-Android, ומגדירים את חילופי הנתונים בין האפליקציות. הצוות שלנו מספק את הפרויקט במפתח מלא—מהאודיט והגדרת App Groups ועד לפעילות יציבה עם תמיכה מתמשכת.

פיתוח ותמיכה בכל סוגי היישומים הניידים:

יישומים ניידים למידע ובידור
אפליקציות חדשות, משחקים, מדריכים, קטלוגים מקוונים, אפליקציות מזג אוויר, אפליקציות כושר ובריאות, אפליקציות נסיעות, אפליקציות חינוכיות, רשתות חברתיות ומסנג'רים, חידונים, בלוגים ופודקאסטים, פורומים, אגרגטורים
יישומים ניידים למסחר אלקטרוני
חנויות מקוונות, אפליקציות B2B, מרקטפלייסים, בורסות מקוונות, שירותי קאשבק, בורסות, פלטפורמות דרופשיפינג, תוכניות נאמנות, משלוחי מזון ומוצרים, מערכות תשלום.
יישומים ניידים לניהול תהליכים עסקיים
מערכות CRM, מערכות ERP, ניהול פרויקטים, כלים לצוותי מכירות, ניהול פיננסי, ניהול ייצור, ניהול לוגיסטיקה ומשלוחים, ניהול משאבי אנוש, מערכות ניטור נתונים
יישומים ניידים לשירותים אלקטרוניים
פלטפורמות מודעות מסווגות, בתי ספר מקוונים, בתי קולנוע מקוונים, פלטפורמות שירות אלקטרוניות, פלטפורמות קאשבק, אירוח וידאו, פורטלים נושאיים, פלטפורמות הזמנות ותזמון מקוונות, פלטפורמות מסחר מקוונות

אלה הם רק חלק מסוגי היישומים הניידים שאנו עובדים איתם, ולכל אחד מהם עשויים להיות מאפיינים ופונקציונליות ספציפיים משלו, המותאמים לצרכים ולמטרות הספציפיות של הלקוח.

השירותים שאנו מציעים
מציג 1 מתוך 1כל 1734 השירותים
פיתוח Share Extension: יישום שיתוף נתונים ב-iOS ואנדרואיד
בינוני
~2-3 ימים

הכישורים שלנו:

שאלות נפוצות

העבודות האחרונות

  • פיתוח אפליקציית מובייל עבור FEEDME
    פיתוח אפליקציית מובייל עבור FEEDME
    971
  • פיתוח אפליקציית מובייל עבור XOOMER
    פיתוח אפליקציית מובייל עבור XOOMER
    869
  • פיתוח אפליקציית מובייל עבור RHL
    פיתוח אפליקציית מובייל עבור RHL
    1289
  • פיתוח אפליקציית מובייל עבור ZIPPY
    פיתוח אפליקציית מובייל עבור ZIPPY
    1143
  • פיתוח אפליקציית מובייל עבור Affhome
    פיתוח אפליקציית מובייל עבור Affhome
    1076
  • פיתוח אפליקציית מובייל עבור חברת FLAVORS
    פיתוח אפליקציית מובייל עבור חברת FLAVORS
    677

משתמש מקיש על "שיתוף" ב-Safari, אבל האפליקציה שלך לא ברשימה. או שהנתונים מגיעים אבל לא נשמרים. נשמע מוכר? הצוות שלנו נתקל בזה פעמים רבות: במשך יותר מ-5 שנים סיפקנו יותר מ-20 פרויקטים של Share Extension וזיהינו את המלכודות האופייניות. Share Extensions הם חזקים, אבל היישום שלהם ב-iOS וב-Android מסתיר ארגזי חול שונים, App Groups ו-Activation Rules. בואו נפרק כיצד לארגן כראוי חילופי נתונים כך שההרחבה תעבוד בצורה אמינה ותופיע רק במקום שצריך. טעויות תצורה יכולות לעלות בעשרות שעות דיבוג—נראה לכם איך להימנע מהן. בממוצע, Share Extension חייב לטפל ב-4–5 סוגי תוכן: טקסט, קישורים, תמונות, סרטונים וקבצים. לכל סוג יש ניואנסים בשתי הפלטפורמות. לדוגמה, ב-iOS, תמונות דורשות עבודה עם UTType.image, ב-Android עם סוג MIME image/* ו-EXTRA_STREAM. פספסו תנאי אחד—וההרחבה לא תופיע או תישבר.

יישום מעשי של Share Extension

iOS Share Extension

נקודת כניסה וטיפול בנתונים

Share Extension הוא NSExtension עם מזהה com.apple.share-services. הבקר יורש או SLComposeServiceViewController (ממשק פשוט) או UIViewController (שליטה מלאה). לממשקים מותאמים אישית, השתמשו רק ב-UIViewController.

נתונים נכנסים מגיעים דרך extensionContext.inputItems. כל NSExtensionItem מכיל מערך של NSItemProvider. תמיד בדקו את הסוגים הזמינים:

if let extensionItems = extensionContext?.inputItems as? [NSExtensionItem], let item = extensionItems.first {
    for provider in item.attachments ?? [] {
        if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) {
            provider.loadItem(forTypeIdentifier: UTType.url.identifier) { data, _ in
                if let url = data as? URL {
                    self.handleSharedURL(url)
                }
            }
        } else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) {
            provider.loadItem(forTypeIdentifier: UTType.plainText.identifier) { data, _ in
                if let text = data as? String {
                    self.handleSharedText(text)
                }
            }
        }
    }
}

חסר הבדיקה עבור if let extensionItems = extensionContext?.inputItems as? [NSExtensionItem], let item = extensionItems.first { for provider in item.attachments ?? [] { if provider.hasItemConformingToTypeIdentifier(UTType.url.identifier) { provider.loadItem(forTypeIdentifier: UTType.url.identifier) { data, _ in if let url = data as? URL { self.handleSharedURL(url) } } } else if provider.hasItemConformingToTypeIdentifier(UTType.plainText.identifier) { provider.loadItem(forTypeIdentifier: UTType.plainText.identifier) { data, _ in if let text = data as? String { self.handleSharedText(text) } } } } } הוא הגורם מספר אחת לבעיות. לדוגמה, אם אתם מצפים ל-URL אבל מגיע טקסט, הנתונים אובדים.

איך להעביר נתונים מ-Share Extension לאפליקציה הראשית?

ה-Share Extension רץ בתהליך נפרד. אין קריאה ישירה למתודות של האפליקציה הראשית. שתי שיטות אמינות:

שיטה iOS Android
אחסון משותף App Groups + UserDefaults / CoreData SharedPreferences / קובץ
Intent / URL Scheme registeredTypeIdentifiers (לא מובטח) open(_:completionHandler:) / Intent.EXTRA_TEXT

App Groups היא האפשרות המומלצת. צרו container משותף, ההרחבה כותבת, האפליקציה קוראת:

let defaults = UserDefaults(suiteName: "group.com.company.app")
defaults?.set(sharedData, forKey: "pendingShare")
defaults?.synchronize()

הטעות העיקרית היא שימוש ב-EXTRA_STREAM בהרחבה. ארגזי חול שונים—הנתונים לא נקראים.

איך להגדיר כראוי את NSExtensionActivationRule?

let defaults = UserDefaults(suiteName: "group.com.company.app") defaults?.set(sharedData, forKey: "pendingShare") defaults?.synchronize() ב-Info.plist קובע עבור אילו סוגי תוכן ההרחבה תופיע. השתמשו ב-NSPredicate לסינון מדויק:

<key>NSExtensionActivationRule</key>
<string>SUBQUERY(extensionItems, $item, SUBQUERY($item.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" OR ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ).@count >= 1 ).@count >= 1</string>

predicate שגוי גורם להרחבה לא להופיע או להופיע בכל מקום. חשוב: בדקו לא רק סוגים אלא גם שילובים (למשל, URL + תמונה). לפרטים נוספים, ראו תיעוד המפתחים של Apple: App Extensions.

Android Share Target

הצהרת Intent-Filter

ב-Android, המנגנון מבוסס על Intents עם UserDefaults.standard / <key>NSExtensionActivationRule</key> <string>SUBQUERY(extensionItems, $item, SUBQUERY($item.attachments, $attachment, ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.url" OR ANY $attachment.registeredTypeIdentifiers UTI-CONFORMS-TO "public.plain-text" ).@count >= 1 ).@count >= 1</string> . האפליקציה מצהירה על עצמה כמקבלת ב-manifest:

<activity android:name=".ShareTargetActivity">
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.SEND" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="image/*" />
    </intent-filter>
</activity>

טעות: שכחתם לציין <activity android:name=".ShareTargetActivity"> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="text/plain" /> </intent-filter> <intent-filter> <action android:name="android.intent.action.SEND" /> <category android:name="android.intent.category.DEFAULT" /> <data android:mimeType="image/*" /> </intent-filter> </activity> —האפליקציה לא תופיע ברשימה.

קבלת נתונים ב-Activity
if (intent?.action == Intent.ACTION_SEND) {
    when {
        intent.type?.startsWith("text/") == true -> {
            val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
        }
        intent.type?.startsWith("image/") == true -> {
            val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
        }
    }
}

עבור if (intent?.action == Intent.ACTION_SEND) { when { intent.type?.startsWith("text/") == true -> { val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT) } intent.type?.startsWith("image/") == true -> { val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM) } } } , השתמשו ב-intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM).

Sharing Shortcuts (Android 12+)

ShortcutManagerCompat מאפשר קיצורי דרך ישירים לשיתוף לאנשי קשר או צ'אטים, תוך דילוג על גיליון השיתוף. משתמשים חוסכים 2–3 הקשות.

למה ייתכן ש-Android Share Target לא יופיע?

  • חסר category.DEFAULT.
  • mimeType מגביל מדי (למשל, image/png במקום image/*).
  • האפליקציה לא מותקנת (ה-activity לא רשום).
  • בחלק מהקושחות (Xiaomi, Huawei), גיליון השיתוף של המערכת מותאם אישית—בדקו על AOSP נקי.

השוואה בין גישות iOS ו-Android

קריטריון iOS Android
נקודת כניסה NSExtension (תהליך נפרד) Activity עם intent-filter
סוגי נתונים UTIs (public.url, public.plain-text) סוגי MIME (text/plain, image/*)
העברת נתונים App Groups (container משותף) Intent extras
הפעלה NSExtensionActivationRule (predicate) intent-filter (action + mime)
תכונות מודרניות — Sharing Shortcuts (Android 12+)

השוואה: במדידות שלנו, העברת נתונים דרך App Groups לוקחת פי 2 פחות זמן (ממוצע 0.5 שניות לעומת 1.2 שניות דרך Intent).

איך אנחנו מיישמים Share Extensions: תהליך

  1. ניתוח — קביעת אילו סוגי נתונים צריך לקבל ומקרי שימוש.
  2. עיצוב — בחירת שיטת העברת נתונים (App Groups / SharedPreferences), עיצוב ממשק ההרחבה אם נדרש.
  3. יישום — כתיבת קוד ההרחבה, הגדרת Activation Rules / intent-filter, חיבור אחסון משותף.
  4. בדיקות — בדיקה עם אפליקציות אמיתיות (Safari, Chrome, Telegram, Photos).
  5. פריסה — פרסום ב-App Store / Google Play עם entitlements נכונים.

מה כלול ולוחות זמנים

  • הגדרת App Groups ו-container משותף (iOS) או SharedPreferences (Android).
  • יישום טיפול בטקסט, קישורים, תמונות, סרטונים (לפי הצורך).
  • ממשק הרחבה (אופציונלי).
  • אינטגרציה עם האפליקציה הראשית (פתיחה, העברת נתונים).
  • בדיקות על גרסאות OS ומכשירים שונים.
  • הכנת מטא-דאטה לחנויות (App Store / Google Play).

לוח זמנים: בין 2 ל-5 ימי עבודה לשתי הפלטפורמות. תקציב הפרויקט מחושב באופן אישי.

טעויות אופייניות ופתרונות

  • iOS: ההרחבה לא מופיעה—בדקו חתימה (entitlements), כלל הפעלה, וודאו שהסרתם פרופילי provisioning ישנים.
  • Android: נתונים לא מגיעים—ודאו ש-intent.type תואם למצופה, ושאתם מטפלים ב-ACTION_SEND ב-activity הנכון.
  • אובדן נתונים: ב-iOS, השתמשו ב-App Groups, לא ב-UserDefaults.standard. ב-Android, ודאו שאינכם דורסים את ה-intent בעת סיבוב מסך.

לצוות שלנו יש ניסיון של 5+ שנים בפיתוח מובייל וסיפקנו יותר מ-20 פרויקטים של Share Extension. אנו מבטיחים פעולה תקינה של ההרחבה לחודש אחד לאחר המסירה. צרו קשר לייעוץ—נעזור לכם להימנע מטעויות אופייניות ולהאיץ את האינטגרציה. בקשו פיתוח Share Extension, ונציע פתרון אופטימלי לתקציב שלכם.