Skip to main content

Command Palette

Search for a command to run...

Base44 App Permissions: Fix the 5.1.1 Rejection

Apple is auto-rejecting Base44 apps over placeholder purpose strings. What the Guideline 5.1.1 check looks at, what to clear, and how to resubmit.

Updated
15 min readView as Markdown
Base44 App Permissions: Fix the 5.1.1 Rejection

Since around July 20, 2026, submissions have been coming back with an automated message about placeholder or otherwise insufficient purpose strings, filed under Guideline 5.1.1. It names no key, no resource, and no file. If you shipped a Base44 app this week you probably received it, and it reads like Apple deciding that apps built this way do not belong on the store. That is not what happened, and nothing in your Base44 project caused it.

The short version

  • This is Guideline 5.1.1 (Privacy: Data Collection and Storage), not Guideline 4.2. Base44 apps are not banned.

  • An automated pass reads the Info.plist in your binary and flags usage description strings that are generic, or that declare a resource your app never uses.

  • It is hitting native iOS and native macOS apps in the same week, including apps whose strings have not changed in years.

  • Apps built on a hosted platform get caught more often, because the category ships a blanket permission set in every binary.

  • The fix is in the binary. New strings, a new build number, a resubmission. It cannot be pushed over the air, and there is nothing to change in Base44.

  • In Despia every permission is now a field under App > Settings > Usage Descriptions, and an empty field removes the key from iOS and Android at once.

What is a purpose string?

A purpose string, also called a usage description, is the sentence iOS shows a user the first time your app asks for a protected resource. It lives in Info.plist under a key named for that resource, such as NSCameraUsageDescription or NSMicrophoneUsageDescription, and it is compiled into the binary at build time.

Apple's requirement is that the sentence describes what the app does with the data and gives a specific example. A string that only restates the permission dialog fails. Apple's own published examples of insufficient strings are the generic forms, along the lines of an app saying it would like access to your contacts, or that it needs microphone access.

Why Base44 apps are not banned under Guideline 5.1.1

The check does not care how your app was built or what renders it. It reads the plist and compares your declarations against what the app appears to do.

The clearest evidence that this is not a purge of hosted or AI-built apps comes from somewhere else entirely. Indie Mac developer Matthias Gansrigler-Hrad wrote up his week: ScreenFloat went to the Mac App Store on July 16, 2026 and was approved the same day. He then found a crash, fixed it with a one-line change, resubmitted, and got back the identical automated purpose-string rejection. His next attempt was rejected again, this time by a human, over an address book entitlement that App Review said had no matching functionality in the app. That is a native AppKit utility with hand-written strings that had shipped unchanged for years.

So: a new or newly tightened automated pass, applied to every submission on both stores. Guideline 4.2 minimum functionality has not moved, and it remains the rule your app actually has to clear on its own merits.

Why your Base44 app declared things it has never done

Here is the part my own category does not like saying out loud.

Every platform that ships web apps as native binaries, Despia included until this week, compiled a broad permission set into every build it produced. Camera, microphone, photo library, location, contacts, calendar, Face ID, the lot. Not because your app used them, but because the vendor cannot know what you will build next, and the entire promise of the model is that web changes reach users without cutting a new binary. If the string is not compiled in, the API is not reachable, and no over-the-air update can add it. So the template declared everything, every customer got the same plist, and nobody had to think about it.

That worked for over a decade. It was also, viewed honestly, over-declaration at scale, and an automated check that flags declared-but-unused resources reads a stock plist as a long list of things to reject.

A native developer hand-writes strings for the four or five resources they actually use, so their worst case is wording. Your worst case is a Base44 expense tracker declaring NSContactsUsageDescription, NSFaceIDUsageDescription, and NSCalendarsUsageDescription for features it has never had. Apple's message covers both failures and both are in play at once:

  • Insufficient wording. The string names the resource without describing the use.

  • Declared but unused. The key is present for a resource the app never reaches. This is what produced the second, human rejection in the ScreenFloat case, and it is what puts hosted apps in the net.

The message does not tell you which key tripped it, so assume the audit covers all of them.

What changed in Despia on July 26, 2026

We rebuilt the permission system in under 24 hours. Every usage description in a Despia build is now yours to edit or delete, under Despia > App > Settings > Usage Descriptions.

  1. Edit any string. Write your own copy, or hit Revert to Default to drop our wording back in and edit from there.

  2. Delete a permission by clearing the field. An empty field removes the key from Info.plist, and it strips the corresponding feature, native code, and any dependencies or libraries it pulled in. Your exported Xcode and Android Studio projects come out clean rather than carrying dead capability.

  3. Android follows iOS. The plist keys are mapped to the Android manifest. Fill in an iOS usage description and the matching Android feature and permission are written into the manifest. Clear it and both come out. One field, two platforms.

One warning that matters more than it should: the field has to be genuinely empty. A single space counts as a value and the key stays in the build. Check it twice before you submit.

Every usage description key you can now control

Despia field iOS Info.plist key Prompt appears when
Camera NSCameraUsageDescription The app opens the camera for photo or video capture
Microphone NSMicrophoneUsageDescription The app records audio
Photo Library NSPhotoLibraryUsageDescription The app reads from the photo library, such as an image picker
Save to Photos NSPhotoLibraryAddUsageDescription The app only writes images to the library and never reads it
Location While Using NSLocationWhenInUseUsageDescription The app first requests location with the app open
Background Location NSLocationAlwaysAndWhenInUseUsageDescription The app asks for always-on location while it already has when-in-use
Always-On Location NSLocationAlwaysUsageDescription Legacy always-on prompt for iOS 10 and earlier
Bluetooth NSBluetoothAlwaysUsageDescription The app connects to Bluetooth accessories
Contacts NSContactsUsageDescription The app reads the address book
Calendar NSCalendarsUsageDescription The app reads or creates calendar events
Face ID NSFaceIDUsageDescription The app first authenticates with Face ID
Health Read NSHealthShareUsageDescription The app reads Health data such as steps or workouts
Health Write NSHealthUpdateUsageDescription The app writes samples back to the Health app
Speech Recognition NSSpeechRecognitionUsageDescription Audio is sent for speech-to-text transcription
NFC Scanning NFCReaderUsageDescription The scan sheet opens to read an NFC tag
Tracking (ATT) NSUserTrackingUsageDescription The App Tracking Transparency prompt appears, before the app tracks you across other apps and websites
Push Notifications None Your own priming screen, shown ahead of the system notification prompt

Two rows behave differently from the rest.

Tracking is the App Tracking Transparency string, and it is required if your app collects an advertising identifier. Ship an ad identifier without it and you fail review on a different front, so this is one to check rather than reflexively clear.

Push Notifications has no Info.plist key at all. iOS does not allow custom text in the system notification alert, so this string is yours to use on a priming screen shown before the system prompt. Clearing it does not remove a declaration, because there is no declaration to remove.

Most of these have a matching Android permission that Despia writes into the manifest when the field is filled and removes when it is empty. Camera maps to the camera permission, Microphone and Speech Recognition to record audio, Contacts to read contacts, Calendar to the calendar read and write pair, Location to fine, coarse, and background location, Face ID to biometrics, Bluetooth to the Bluetooth permissions, and NFC to the NFC permission. Three have no Android side: the Health keys, because HealthKit has no Android counterpart in a Despia build, Tracking, because App Tracking Transparency is an iOS framework, and Push Notifications, because that string is your own priming copy rather than a system declaration.

Work down that table against your Base44 app as it exists today. Anything with no feature behind it goes.

How to write a purpose string that passes review

Look at the shape of the strings Despia ships as defaults, because it is the shape Apple asks for. Each one is a verb, the data, and the outcome the user gets:

  • Camera: Opens the camera so you can take photos and videos in the app.

  • Microphone: Records audio so you can capture voice notes in the app.

  • Photo Library: Opens your photo library so you can pick images to upload.

  • Location While Using: Reads your location so you can see yourself on the map and find nearby results.

  • Health Read: Reads your steps and workouts so you can see your activity in the app.

The formula is what the app touches, and what the user gets because of it. A string that only names the resource fails, because it tells the user nothing the permission dialog did not already say.

Rewrite the defaults where your app does something more specific. A generic string is not automatically a rejection, but a specific one is what the guideline asks for, and it converts better on the prompt:

  • Weak: Opens the camera so you can take photos and videos in the app.

  • Strong: Opens the camera so you can photograph a receipt and attach it to an expense claim.

Write them by hand or hand the field to a model. The copy is yours now, not ours.

The trap: deleting a permission you will need later

Read this before you start clearing fields, because it is the part that bites people who build fast.

Usage descriptions live in the binary. Your Base44 app ships over the air. Those two things update on completely different clocks, and the permission system is where that asymmetry bites.

Say you clear the microphone field to pass review, cut a new build, get approved. Two weeks later you add a voice note feature in Base44 and it reaches your users within minutes. On iOS, touching a protected resource with no matching usage description is not a denied promise, it is a terminated process. The user taps the button and your app disappears. There is no runtime workaround and no over-the-air fix, because the fix is in the binary. Android is more forgiving and will usually fail the permission request instead of killing the app, but iOS is the platform that decides whether you ship.

There are two honest ways to handle this.

The old way, which the industry has run on for a decade. Keep the declaration for anything you plan to ship inside this binary's lifetime, and write the string as if the feature is already there. No engineer is going to congratulate you for it. It is also, right now, exactly the pattern the automated check was built to find, and the second ScreenFloat rejection shows a human reviewer following up on declared-but-unused capability after the automated pass cleared. This route is getting less viable by the month.

The way that survives the new check. Delete what you do not use, and gate the feature on the app version when you add it. Despia hands your web code the version number you set in the dashboard, the human-readable one like 1.2.0, so your Base44 app can tell which native capabilities are compiled into the shell it is running inside:

const isDespia = navigator.userAgent.toLowerCase().includes('despia')

// "1.2.0" -> 10200, so versions compare as plain numbers
const versionValue = (v) => v.split('.').reduce((acc, part) => acc * 100 + Number(part), 0)

let micAvailable = false

if (isDespia) {
  const { versionNumber } = await despia('getappversion://', ['versionNumber'])
  micAvailable = versionValue(versionNumber) >= versionValue('1.2.0')
}

if (micAvailable) {
  showVoiceNoteButton()
}

Keep the two numbers straight. The version is the one you set in Despia and the one your code gates on, and it changes when the native layer gains a capability. The build number is what App Store Connect wants incremented on every upload, including a resubmission that carries no capability change at all.

Bump the version when you add the usage description, ship that binary, then push the Base44 update that turns the feature on. Users still on 1.1.0 never see the button, so they never reach the API that would kill their app, and users on 1.2.0 get the feature the moment it lands. The version gate does the job the blanket declaration used to do by brute force.

The rule that falls out of this: your plist should describe what your app does in this binary, and adding a native capability is one of the few things that legitimately costs a rebuild. Everything else still ships over the air.

How to fix the rejection, step by step

  1. Open Despia > App > Settings > Usage Descriptions and go through every key. Anything your app does not use, clear the field. Confirm it is empty and not a space.

  2. Rewrite what remains so each string names the data and the outcome. Do not leave a default in place for a resource that is central to your app.

  3. Cut a new build with a new build number.

  4. Upload it and resubmit. Replying in Resolution Center without a new build will not clear this, because the plist is compiled into the binary. There is nothing to change in Base44 and nothing to change on your hosting.

  5. If you plan to add a native capability soon, add its string now, in this build, and gate the feature on the app version rather than shipping the declaration and hoping nobody looks.

FAQ

Are Base44 apps banned from the App Store? No. This is Guideline 5.1.1, a privacy rule that applies to every app on the store, and it is hitting native iOS and macOS apps in the same week. Guideline 4.2 minimum functionality is unchanged, and it remains the rule your app has to clear.

Do I need to change anything in my Base44 project? No. Purpose strings are part of the native build. There is nothing on your web app or your hosting to change.

What does "placeholder or otherwise insufficient purpose strings" mean? Either the string is too generic to describe the actual use, or the key is declared for a resource your app never touches. Both fail the same automated check.

Which purpose string did Apple flag? Apple does not say. The message names no key and no resource, so you have to audit all of them.

Can I fix a 5.1.1 rejection over the air? No. Usage descriptions are compiled into the binary, so it takes a new build and a resubmission. Everything on the web side of your app still updates over the air.

Do I need to upload a new build if my last submission was rejected? Yes. A rejected build cannot be edited. Change the strings, increment the build number, upload, and resubmit.

How do I remove a permission from Info.plist? Clear the field under Despia > App > Settings > Usage Descriptions and rebuild. The key leaves Info.plist, the matching Android permission leaves the manifest, and the feature code is stripped from the exported native projects.

Does removing a permission break my app? It breaks any feature that reaches the removed resource. On iOS the app terminates rather than failing gracefully, which is why you gate new native capabilities on the app version instead of assuming everyone is on the latest binary.

Does this affect Android too? Google does not run this check, but over-declared permissions are a Data Safety liability there and they cost you installs at the permission screen. The usage description fields map to the Android manifest, so cleaning up one cleans up both.

Will a well-written string still get rejected if the app does not use the resource? Yes, and that is the second failure mode. Wording does not save a declaration with no matching functionality behind it.

Add this to your app

Every permission your Base44 app declares is now a field you control, on both platforms, from one place. No Xcode, no Android Studio, and no native project to keep in sync.

Read the full reference at setup.despia.com