# ITMS-90338: תיקון Non-public API usage ב-Expo

> ITMS-90338: ה-build שלך מפנה למתודה, פונקציה או מחלקה ש-Apple מחשיבה כפרטית. מצא את התלות האחראית, עדכן אותה ובנה build חדש.

עמוד מקורי: [ITMS-90338: תיקון Non-public API usage ב-Expo](https://devoria.fr/he/blog/itms-90338-non-public-api-usage-expo)
כותב: [Mehdi Azizi](https://devoria.fr/he/mehdi-azizi)
פרסום: 2026-11-04
עדכון תוכן אחרון: 2026-11-04


ITMS-90338 היא בדיקה אוטומטית ש-Apple מריצה על ה-build שלך. היא אומרת שהבינארי שלך מפנה ל-API ש-Apple מחשיבה כפרטיים: מתודות Objective-C, פונקציות או מחלקות. **התיקון: לאתר את הקובץ שמוזכר במייל, להגיע לתלות שמכילה את השם, לעדכן אותה או לשנות את שם המתודה שלך, ואז לבנות build חדש.**

עם Expo, השם מגיע כמעט תמיד מספרייה נייטיב, ורק לעיתים רחוקות מה-JavaScript שלך. ב-15 באוקטובר 2026 הרצתי את פקודות הניתוח מהמדריך הזה על build סימולטור של Solys, השעון המעורר שלי עם מיני-משחקים (Expo SDK 57), ועל ה-SDK של iOS ב-Xcode 26.2. הנה איך לקרוא את המייל, למצוא את האשם ולזהות false positive.

## מה המשמעות של השגיאה ITMS-90338?

ההודעה נקראת "ITMS-90338: Non-public API usage". יש לה שלוש גרסאות, לפי מה ש-Apple מצאה בבינארי שלך:

- **"references non-public selectors"**: שמות של מתודות Objective-C, כמו `boldFontOfSize:`;
- **"references non-public symbols"**: פונקציות, שנכתבות עם קו תחתון בהתחלה, כמו `__dyld_get_shared_cache_range`;
- **"contains or inherits from non-public classes"**: שם של מחלקה.

כל גרסה מציינת נתיב, ואחריו את רשימת השמות. הטקסט מוסיף שתי עצות: לשנות את שמות המתודות שלך אם הם זהים ל-API פרטי, ולהסיר את הספרייה הסטטית שמכילה אותם. מייל מספטמבר 2026 מציע גם את `xcrun swift-demangle` כדי להפוך שמות Swift מקודדים לקריאים.

הכלל שמאחורי הבדיקה הוא [guideline 2.5.1](https://developer.apple.com/app-store/review/guidelines/#2.5.1): אפליקציה יכולה להשתמש רק ב-API ציבוריים. [הסכם הרישיון של Apple Developer Program](https://developer.apple.com/support/terms/apple-developer-program-license-agreement/) חוזר על זה בסעיף 3.3.1: אסור לקרוא ל-API פרטי. הבדיקה האנושית של הכלל הזה מפורטת במדריך על [guideline 2.5.1](https://devoria.fr/he/blog/guideline-2-5-1-public-api-sdk-expo). כאן אנחנו מטפלים בשגיאת ההעלאה.

כמו בשאר שגיאות ה-ITMS, אף אחד לא פתח את האפליקציה שלך: Apple ניתחה את הבינארי בזמן עיבוד ה-build. **Apple לא מפרסמת שום רשימה של API פרטיים: השמות במייל שלך הם הרשימה האמינה היחידה.**

## לקרוא את הנתיב: SDK או קובץ ההרצה שלך?

הנתיב שכתוב לפני הרשימה אומר לך באיזה קובץ לחפש.

- **`Frameworks/Nom.framework/Nom`**: השם נמצא ב-framework דינמי מוטמע, כלומר ב-SDK מסוים. ב-build של Solys התיקייה הזו מכילה 14 frameworks: Hermes, React Native, מודולים של Expo שמגיעים כבר מקומפלים, וספריית התמונות SDWebImage.
- **רק שם האפליקציה שלך**: השם נמצא בקובץ ההרצה הראשי. ב-Expo הוא מאחד את הקוד הנייטיב שלך ואת ה-pods שקומפלו מהמקור, שמקושרים סטטית כברירת מחדל. כל ספרייה נייטיב יכולה להיות האשמה.

## העץ: מהשם המוזכר לתיקון

עבור על השורות לפי הסדר. השורה הראשונה שמתאימה למקרה שלך נותנת את הכיוון.

| מה שאתה רואה                 | איפה לחפש              | תיקון                  |
| ---------------------------- | ---------------------- | ---------------------- |
| שם שמתחיל ב-`$s` או `_$s`    | `xcrun swift-demangle` | להתחיל את העץ מחדש     |
| לא נמצא ב-IPA שלך            | `grep` על ה-bundle     | לשלוח build מחדש       |
| שם ציבורי ב-iOS              | ה-headers של SDK iOS   | לשלוח build מחדש       |
| `API_UNAVAILABLE(ios)`       | ה-headers של SDK iOS   | לעדכן את החבילה        |
| בתוך `Frameworks/X`          | ה-SDK שמספק את X       | לעדכן את ה-SDK         |
| selector, האפליקציה שלך      | pods ומודולים נייטיב   | להוסיף prefix או לעדכן |
| סימבול `_nom`, האפליקציה שלך | `nm -m`, ואז ה-pods    | להחליף את הקריאה       |
| מחלקה, האפליקציה שלך         | המחלקות וה-pods שלך    | להוסיף prefix למחלקה   |

השורות "לשלוח build מחדש" מסמנות false positive, שמטופל בהמשך.

## שלב 1: לפתוח את ה-build שנשלח

תעבוד על הקובץ שבאמת נשלח ל-Apple, לא על הפרויקט שלך. הורד את ה-IPA מעמוד ה-build ב-expo.dev, ואז הרץ ב-Mac שלך:

```bash
unzip -q MyApp.ipa -d ipa
ls ipa/Payload/MyApp.app/Frameworks
otool -L ipa/Payload/MyApp.app/MyApp | grep PrivateFrameworks
```

השורה השנייה מציגה את ה-frameworks המוטמעים. השלישית מחפשת קישור ישיר ל-framework פרטי של Apple: היא לא אמורה להציג כלום. ב-Solys, `otool -L` לא מוצא שום framework פרטי, לא בקובץ ההרצה ולא ב-14 ה-frameworks.

## שלב 2: למצוא את הקובץ שמכיל את השם

תתחיל רחב. חפש את השם המוזכר, בלי הקווים התחתונים שבהתחלה, בכל ה-bundle:

```bash
grep -rl "dyld_get_shared_cache_range" ipa/Payload/MyApp.app
```

הפקודה מציגה את הקבצים שמכילים את הטקסט הזה. אחר כך תדייק לפי סוג השם (selector, סימבול, מחלקה), על הקובץ שנמצא, שמסומן כאן `Binary`:

```bash
strings -a Binary | grep -x "boldFontOfSize:"
nm -m Binary | grep "_dyld_get_shared_cache_range"
nm -m Binary | grep '_OBJC_CLASS_$_ClassName'
```

**בשביל selector, תשתמש ב-`strings`, לא ב-`nm`.** על ה-framework של React ב-Solys, `nm` מציג מתודות יחד עם המחלקה שלהן, כמו `-[RCTRootView initWithBridge:moduleName:initialProperties:]`, ואף פעם לא את ה-selector `initWithBridge:` לבד. `strings` כן מוצא אותו.

בשביל סימבול, תקרא את סוף השורה. ב-Solys, `nm -m` מציג למשל `(undefined) external __dyld_get_image_name (from libSystem)`: הבינארי קורא לפונקציה בלי להכיל אותה, ו-libSystem מספקת אותה. הפונקציה הזו דווקא ציבורית, כמו שהשלב הבא מראה.

## שלב 3: לבדוק אם השם באמת פרטי

ה-headers של SDK iOS מתארים את ה-API הציבוריים. חיפוש של כמה שניות אומר אם השם המוזכר מופיע שם, ובאילו פלטפורמות. בשביל סימבול, קודם תוריד את הקו התחתון שנוסף בקומפילציה: `_SCDynamicStoreCreate` הופך ל-`SCDynamicStoreCreate`.

```bash
SDK=$(xcrun --sdk iphoneos --show-sdk-path)
grep -rn "SCDynamicStoreCreate" "$SDK/usr/include" "$SDK"/System/Library/Frameworks/*/Headers
```

ב-15 באוקטובר 2026, עם SDK iOS 26.2, זה מה שיוצא עבור ארבעה שמות מתוך מיילים אמיתיים של ITMS-90338:

| השם המוזכר                    | ב-SDK של iOS         | פירוש                |
| ----------------------------- | -------------------- | -------------------- |
| \_dyld_get_shared_cache_range | לא קיים              | API פרטי             |
| boldFontOfSize:               | לא קיים              | פרטי או התנגשות שמות |
| SCDynamicStoreCreate          | API_UNAVAILABLE(ios) | macOS בלבד           |
| isMainFrame                   | property של WebKit   | ציבורי               |

**שם שקיים וזמין ב-iOS מצביע על false positive. שם שלא קיים מצביע על API פרטי או על התנגשות שמות.** שתי מגבלות: API ציבורי שקיים רק ב-Swift לא מופיע ב-headers האלה, ובשביל accessor כמו `setDelegate:`, חפש את ה-property `delegate`.

## שלב 4: להגיע עד החבילה

אם הנתיב מזכיר framework, השם שלו מוביל ל-pod, ומשם לחבילת ה-npm:

```bash
grep -n "FrameworkName" ios/Podfile.lock
npm explain package-name
```

`npm explain` אומר איזו תלות התקינה אותה, אם אף פעם לא הוספת אותה בעצמך.

אם הנתיב מזכיר את קובץ ההרצה, חפש את השם בקוד המקור הנייטיב, ואז ב-pods:

```bash
grep -rl --include='*.m' --include='*.mm' --include='*.swift' --include='*.h' \
  "boldFontOfSize" node_modules modules
grep -rl "boldFontOfSize" ios/Pods
```

תתאים את רשימת התיקיות: `modules` היא התיקייה של מודולי Expo המקומיים. התיקייה `ios/Pods` מופיעה אחרי `npx expo prebuild -p ios`, שגם מתקינה את ה-pods ב-Mac. שים לב: הפקודה עלולה ליצור מחדש את תיקיית `ios`. אם שינית אותה ידנית, תעבוד על עותק.

**החיפוש ב-`ios/Pods` חשוב בדיוק כמו החיפוש בקוד המקור.** ב-Solys התיקייה הזו מכילה 15 frameworks שמגיעים כבר מקומפלים, בפורמט `.xcframework`: מודולים של Expo, Hermes, React Native ו-SDKs של צד שלישי. חיפוש שמוגבל לקבצי מקור יפספס את הקוד שלהם.

## הסיבות הנפוצות

**SDK של צד שלישי, ישן מדי או חדש מדי.** בספטמבר 2026 מפתח Flutter [דיווח על דחייה בגלל SDK אבטחה](https://github.com/talsec/Free-RASP-Flutter/issues/236): הגרסה 8.2.3 שלו קראה ל-`__dyld_get_shared_cache_range`. גרסה 8.2.2 עברה, והמפתח של ה-SDK המליץ לחזור אליה עד שיהיה תיקון.

**מודול נייטיב שקורא ל-API לא מתועד.** המודול שלך, plugin או ספרייה קטנה יכולים להשתמש במתודה פנימית של UIKit שמישהו מצא בפורום. הבדיקה מזהה אותה לפי השם.

**התנגשות שמות.** במקרה, למתודה או למחלקה שלך יש שם זהה ל-API פרטי של Apple. ההודעה צופה את זה ומבקשת ממך לשנות את השם. Apple ממליצה [להוסיף prefix למתודות שאתה מוסיף למחלקות שלה](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html), למשל שלוש אותיות קטנות ואחריהן קו תחתון.

**API ציבורי ב-macOS, לא ב-iOS.** ב-2024 אפליקציית .NET MAUI [נדחתה בגלל `_SCDynamicStoreCreate`](https://github.com/dotnet/runtime/issues/103949). Apple אכן מתעדת את הפונקציה הזו, אבל [רק ל-macOS](https://developer.apple.com/documentation/systemconfiguration/1437828-scdynamicstorecreate), וה-header של SDK iOS מסמן אותה `API_UNAVAILABLE(ios, tvos, watchos)`.

## לתקן בלי לעקוף

1. **עדכן את התלות.** עבור חבילות ש-Expo עוקבת אחריהן, `npx expo install --check` מסמן את אלה שלא תואמות ל-SDK שלך, לפי [התיעוד של Expo CLI](https://docs.expo.dev/more/expo-cli/). לשאר, תקרא את ה-changelog.
2. **חזור לגרסה הקודמת** אם הגרסה החדשה היא זו שהכניסה את השם, ותעדכן את המפתח שלה.
3. **החלף או הסר** ספרייה נטושה.
4. **שנה את השם** של המתודה או המחלקה שלך, עם prefix.

**אל תסתיר את השם.** לפצל מחרוזת או לקרוא למתודה בדרך עוקפת מסתיר את השימוש בלי לתקן אותו. זה עדיין אסור לפי כלל 2.5.1 ולפי ההסכם, ואז החשבון שלך הוא זה שבסיכון.

אחר כך, תבנה build חדש עם [מספר build](https://devoria.fr/he/blog/app-store-connect-build-number-version) חדש. עדכון מרחוק לא מספיק: השם נמצא בקוד הנייטיב המקומפל. לפני ההעלאה, תריץ שוב את שלב 2 על ה-IPA החדש. השם לא אמור להופיע יותר.

## כשמדובר ב-false positive

הבדיקה של Apple טועה לפעמים. ב-6 ביוני 2022 אפליקציות React Native ו-Flutter קיבלו ITMS-90338 על שמות ציבוריים של WebKit, כמו `setNavigationDelegate:` או `isMainFrame`, גם בעדכונים כמעט ללא שינוי. למחרת, מהנדס של Apple כתב שהבעיה נפתרה בצד הוולידציה והזמין לשלוח את ה-builds מחדש. השרשור שמור [ב-repo של Expo ב-GitHub](https://github.com/expo/expo/issues/17768).

שלושה סימנים ביחד מצביעים על false positive:

- ה-build דומה ל-build האחרון שהתקבל;
- שלב 2 לא מוצא את השם, או ששלב 3 מראה שהוא ציבורי ב-iOS;
- מפתחים אחרים מדווחים על אותה שגיאה באותו יום בפורומים של Apple.

במקרה כזה, תבדוק את [סטטוס השירותים של Apple](https://developer.apple.com/system-status/), ואז תשלח build מחדש עם מספר חדש. אם השגיאה ממשיכה, תכתוב ל[תמיכת המפתחים של Apple](https://developer.apple.com/contact/) עם המייל המלא. לעומת זאת, התנגשות שמות היא לא false positive מבחינת Apple: תשנה את השם.

## לפני ההעלאה הבאה שלך

לשגיאות ITMS האחרות יש גם מדריך: [ITMS-90725](https://devoria.fr/he/blog/itms-90725-sdk-version-issue-expo) לגרסת Xcode, [ITMS-91053](https://devoria.fr/he/blog/itms-91053-missing-api-declaration-expo) ל-privacy manifest ו-[ITMS-90034](https://devoria.fr/he/blog/itms-90034-missing-or-invalid-signature-fix) לחתימה. אחרי שה-build מתקבל, תתקין אותו עם [TestFlight](https://devoria.fr/he/blog/testflight-test-iphone-app-before-submission) לפני הבדיקה.

דחיות של reviewer דורשות יותר מחשבה: השיטה נמצאת בעמוד [אפליקציה שנדחתה על ידי Apple](https://devoria.fr/he/expertise/app-rejected-by-apple), והסיבות הנפוצות ביותר נמצאות ב[למה Apple דוחה אפליקציה](https://devoria.fr/he/blog/apple-app-rejection-reasons-real-cases).

כדי לבדוק את הכול לפני ההעלאה, ה-[Review Kit](https://devoria.fr/he/kits/app-review) מאחד את הצ'קליסט שלי ו-skills של Claude Code שעוברים על הפרויקט שלך. אם ההעלאה שלך תקועה ואתה רוצה עין אנושית, [תזמין שעה איתי](https://devoria.fr/he/coaching): נעבור על הפרויקט שלך בווידאו, על המסך שלך. ואם אתה מכין את ההשקה הראשונה שלך, [תעשה את האבחון החינמי](https://devoria.fr/he/diagnostic): הוא מסדר את השלבים לפי הסדר ושולח לך את הצ'קליסט שלי נגד דחיות של Apple.

## שאלות נפוצות

### האם ITMS-90338 היא דחייה של App Review?

לא. זו בדיקה אוטומטית שנעשית בזמן עיבוד ה-build, לפני כל בדיקה אנושית. ה-build שנדחה לא מגיע ל-TestFlight ולא לבדיקה. API פרטי ש-reviewer מוצא מאוחר יותר שייך, לעומת זאת, ל-guideline 2.5.1.

### אפשר לתקן ITMS-90338 עם עדכון מרחוק?

לא. השמות המוזכרים נמצאים בקוד הנייטיב המקומפל של הבינארי. עדכון מרחוק משנה רק את ה-JavaScript והמשאבים. תתקן את התלות, תבנה build חדש ותשלח אותו.

### למה Apple מזכירה מתודות שמעולם לא כתבתי?

כי הן מגיעות מספרייה מוטמעת, או מהתנגשות שמות: תלות מצהירה על מתודה שהשם שלה זהה ל-API פרטי. Apple קוראת את השמות שקיימים בבינארי, לא את קוד המקור שלך. תגיע עד החבילה עם `grep` ו-`npm explain`.

### איך יודעים אם API ציבורי ב-iOS?

חפש את השם שלו ב-headers של SDK iOS, שאת הנתיב שלהם נותן `xcrun --sdk iphoneos --show-sdk-path`, ותקרא את הזמינות שלו. עמוד התיעוד של Apple מציין את זה גם. פונקציה שמתועדת ל-macOS בלבד היא לא ציבורית ב-iPhone.

### צריך לשנות את מספר ה-build אחרי התיקון?

כן. תן ל-EAS להעלות אותו: מספר שכבר נשלח לאותה גרסה עלול להידחות עם ITMS-90189.


## הפרויקט שלך

[Review Kit](https://devoria.fr/he/kits/app-review): הצ'קליסט שלי לבדיקת App Store, הדחיות שקיבלתי מ-Apple ואיך תיקנתי אותן, הצ'קליסט ל-Google Play ושלושה skills ל-Claude Code. ‏169 ‏⁦₪⁩.

[iOS & Android Kit](https://devoria.fr/he/kits/ios): תבנה אפליקציה לאייפון ולאנדרואיד עם Claude Code: קוד ההתחלה של האפליקציות שלי, כל הדרך עד החנויות וכל הקיטים. ‏349 ‏⁦₪⁩.

[אבחון: מה מפריד בינך לבין אפליקציית האייפון שלך](https://devoria.fr/he/diagnostic): 7 שאלות, 60 שניות: תדע מה באמת חוסם אותך מלפרסם את אפליקציית האייפון שלך, תקבל מסלול ב-4 שלבים ואת הקיט שמתאים לך.
