agentsclimarketplace

Sap mobile

Skill efeumutaslan/SAP-SKILLS/skills/sap-mobile

23 SAP development skills for Claude Code — ABAP, RAP, CAP, Fiori, BTP, HANA, S/4HANA, Integration Suite and more. Agent Skills Specification compatible.

Install
npx -y skills add efeumutaslan/SAP-SKILLS --skill sap-mobile

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

SAP Mobile development skill. Use when building apps with MDK (Mobile Development Kit), SAP BTP SDK for iOS/Android, configuring offline OData store, push notifications, or mobile security. If the user mentions SAP mobile app, MDK, Mobile Services, offline sync, or BTP SDK iOS/Android, use this skill.

The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.

SKILL.md

12.3 KB, as published. Nobody here has run it

SAP Mobile Development

Related Skills

  • sap-s4hana-extensibility — Backend OData services consumed by mobile apps
  • sap-security-authorization — Mobile app authentication and security policies
  • sap-build-apps — Alternative low-code approach for mobile apps

Quick Start

Choose your mobile framework:

FrameworkLanguageOfflineBest For
MDK (Mobile Dev Kit)Metadata/JSBuilt-inCross-platform business apps
BTP SDK for iOSSwiftBuilt-inNative iOS enterprise apps
BTP SDK for AndroidKotlin/JavaBuilt-inNative Android enterprise apps
SAP Build AppsNo-codeManualSimple apps, citizen developers
SAP Mobile StartConfig onlyN/AUnified mobile launchpad

MDK Quick Start:

  1. SAP Business Application Studio → MDK project from template
  2. Define application → Pages → Actions → Rules → Services
  3. Deploy to Mobile Services → Test with SAP Mobile Services Client

Core Concepts

SAP Mobile Services

Central management platform for all SAP mobile apps:

  • App Configuration — Define app features, security, connectivity
  • Push Notifications — APNS (iOS), FCM (Android) integration
  • Offline Store — OData offline delta sync engine
  • App Update — OTA (over-the-air) updates without app store
  • Usage Analytics — User sessions, feature usage, crash reports
  • Security Policies — Passcode, biometrics, jailbreak detection

Offline Architecture

Mobile App ──► Local SQLite DB ──► Offline OData Store
                                        │
                                   [Sync Engine]
                                        │
                              SAP Mobile Services
                                        │
                              Backend OData Service

Sync modes:

ModeDescriptionUse Case
DownloadServer → deviceInitial load, refresh
UploadDevice → serverSubmit local changes
Delta syncChanged records onlyIncremental updates
Defining requestsSubset of data per entityControl offline data scope

MDK Application Structure

MDKApp/
├── Application.app           # App-level config
├── Pages/
│   ├── Main.page             # Landing page
│   ├── CustomerList.page     # List page
│   └── CustomerDetail.page   # Detail page
├── Actions/
│   ├── Service/
│   │   ├── InitializeOffline.action
│   │   ├── SyncStartedMessage.action
│   │   └── UploadOffline.action
│   └── Customers/
│       ├── CreateCustomer.action
│       └── UpdateCustomer.action
├── Rules/
│   ├── OnWillUpdate.js
│   └── FormatAddress.js
├── Services/
│   └── MyODataService.service
├── Styles/
│   └── Styles.less
└── i18n/
    └── i18n.properties

Common Patterns

Pattern 1: MDK List Page with Search

// Pages/CustomerList.page
{
  "_Type": "Page",
  "_Name": "CustomerList",
  "Caption": "Customers",
  "Controls": [
    {
      "_Type": "Control.Type.SectionedTable",
      "_Name": "CustomerTable",
      "Sections": [
        {
          "_Type": "Section.Type.ObjectTable",
          "Target": {
            "EntitySet": "Customers",
            "Service": "/MDKApp/Services/MyODataService.service",
            "QueryOptions": "$orderby=CompanyName&$top=50"
          },
          "ObjectCell": {
            "Title": "{CompanyName}",
            "Subhead": "{CustomerID}",
            "Description": "{City}, {Country}",
            "StatusText": "{ContactName}",
            "AccessoryType": "DisclosureIndicator",
            "OnPress": "/MDKApp/Actions/Customers/NavToDetail.action"
          },
          "Search": {
            "Enabled": true,
            "Placeholder": "Search customers...",
            "BarcodeScanner": true
          },
          "EmptySection": {
            "Caption": "No customers found"
          }
        }
      ]
    }
  ],
  "ToolBar": {
    "Items": [
      {
        "_Type": "Control.Type.ToolbarItem",
        "_Name": "AddButton",
        "Caption": "Add",
        "SystemItem": "Add",
        "OnPress": "/MDKApp/Actions/Customers/NavToCreate.action"
      }
    ]
  }
}

Pattern 2: Offline Initialization Action

// Actions/Service/InitializeOffline.action
{
  "_Type": "Action.Type.ODataService.Initialize",
  "_Name": "InitializeOffline",
  "Service": "/MDKApp/Services/MyODataService.service",
  "ShowActivityIndicator": true,
  "OnSuccess": "/MDKApp/Actions/Service/InitializeOfflineSuccessMessage.action",
  "OnFailure": "/MDKApp/Actions/Service/InitializeOfflineFailureMessage.action",
  "DefiningRequests": [
    {
      "Name": "CustomersReq",
      "Query": "/Customers",
      "AutomaticallyRetrievesStreams": false
    },
    {
      "Name": "OrdersReq",
      "Query": "/Orders?$filter=OrderDate gt datetime'2026-01-01T00:00:00'",
      "AutomaticallyRetrievesStreams": false
    }
  ]
}

Pattern 3: MDK Rule — Custom Validation

// Rules/ValidateCustomer.js
/**
 * @param {IClientAPI} clientAPI
 * @returns {string} Empty string if valid, error message if invalid
 */
export default function ValidateCustomer(clientAPI) {
    const formProxy = clientAPI.getPageProxy().getControl('FormCellContainer');
    const companyName = formProxy.getControl('CompanyName').getValue();
    const email = formProxy.getControl('Email').getValue();
    const phone = formProxy.getControl('Phone').getValue();

    if (!companyName || companyName.trim().length < 2) {
        return 'Company name must be at least 2 characters';
    }

    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (email && !emailRegex.test(email)) {
        return 'Invalid email address';
    }

    if (phone && !/^\+?[\d\s-()]{7,15}$/.test(phone)) {
        return 'Invalid phone number';
    }

    return '';
}

Pattern 4: Push Notification (iOS SDK — Swift)

import SAPFoundation
import SAPFiori
import UserNotifications

class PushNotificationManager {
    static func registerForPush(application: UIApplication) {
        UNUserNotificationCenter.current().requestAuthorization(
            options: [.alert, .badge, .sound]
        ) { granted, error in
            guard granted else { return }
            DispatchQueue.main.async {
                application.registerForRemoteNotifications()
            }
        }
    }

    static func sendTokenToMobileServices(deviceToken: Data) {
        let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()

        // Register with SAP Mobile Services
        let url = URL(string: "\(mobileServicesURL)/mobileservices/push/v1/register")!
        var request = URLRequest(url: url)
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.httpBody = try? JSONSerialization.data(withJSONObject: [
            "deviceToken": tokenString,
            "deviceType": "iOS",
            "applicationId": Bundle.main.bundleIdentifier ?? ""
        ])

        URLSession.shared.dataTask(with: request).resume()
    }
}

Pattern 5: Offline Sync with Conflict Resolution (Android SDK)

import com.sap.cloud.mobile.odata.offline.*

class OfflineSyncManager(private val offlineStore: OfflineODataProvider) {

    fun syncData(callback: SyncCallback) {
        // Upload local changes first
        offlineStore.upload(
            successHandler = {
                // Then download server changes
                offlineStore.download(
                    successHandler = {
                        callback.onSuccess()
                    },
                    failureHandler = { error ->
                        callback.onError("Download failed: ${error.message}")
                    }
                )
            },
            failureHandler = { error ->
                if (error is OfflineODataException &&
                    error.errorCode == OfflineODataErrorCode.UPLOAD_CONFLICT) {
                    handleConflict(error, callback)
                } else {
                    callback.onError("Upload failed: ${error.message}")
                }
            }
        )
    }

    private fun handleConflict(error: OfflineODataException, callback: SyncCallback) {
        val errorArchive = offlineStore.getErrorArchive()
        for (entry in errorArchive) {
            // Strategy: server wins (discard local change)
            entry.cancelUpload()
            // Alternative: client wins — entry.retryUpload()
        }
        // Retry sync after conflict resolution
        syncData(callback)
    }

    interface SyncCallback {
        fun onSuccess()
        fun onError(message: String)
    }
}

Pattern 6: Mobile Security Configuration

// Mobile Services — App Security Config
{
  "security": {
    "passcode": {
      "enabled": true,
      "minLength": 6,
      "hasDigit": true,
      "retryLimit": 5,
      "lockTimeout": 300,
      "biometricEnabled": true
    },
    "certificatePinning": {
      "enabled": true,
      "pins": [
        {
          "host": "*.hana.ondemand.com",
          "pins": ["sha256/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="]
        }
      ]
    },
    "rootDetection": {
      "enabled": true,
      "action": "WARN"
    },
    "dataProtection": {
      "encryptDatabase": true,
      "preventScreenCapture": true,
      "clipboardTimeout": 60
    }
  }
}

Error Catalog

ErrorMessageRoot CauseFix
Offline init failedCannot create offline storeDefining request error or backend downCheck OData service; validate defining request query
Upload conflictEntity modified on serverConcurrent edit during offline periodImplement conflict resolution (server/client wins)
Push: Token invalidAPNS/FCM token rejectedApp reinstalled or token expiredRe-register device token on app launch
Sync timeoutDownload timed outLarge dataset or slow networkReduce defining request scope; increase timeout
Certificate errorSSL handshake failedCertificate pinning mismatchUpdate pinned certificates; check cert rotation
Passcode lockedToo many failed attemptsUser exceeded retry limitAdmin reset via Mobile Services console
MDK deploy errorMetadata upload failedInvalid page/action JSONValidate JSON syntax; check MDK version compatibility
OData 413Payload too largeOffline store sync payload exceeds limitSplit defining requests; limit data scope

Performance Tips

  1. Defining requests — Scope offline data narrowly; don't sync entire entity sets
  2. Delta tokens — Enable server-side delta tokens for incremental sync
  3. Image handling — Download images on demand, not during sync; use thumbnail URLs
  4. Batch size — Configure upload batch size (default 100); reduce for slow networks
  5. Sync frequency — Don't auto-sync on every app resume; use manual or timed sync (every 15-30 min)
  6. MDK page loading — Use lazy loading for detail pages; preload only list pages
  7. SQLite optimization — Offline store uses SQLite; keep total offline data under 500 MB

Gotchas

  • MDK vs. native SDK: MDK is cross-platform but less flexible; native SDKs offer full platform APIs
  • Offline data scope: Defining requests determine what's available offline — missing data = blank screens
  • iOS enterprise distribution: Apps need MDM or Apple Business Manager for enterprise deployment
  • Mobile Services regions: Ensure Mobile Services instance is in same BTP region as backend
  • OData version: Offline OData store supports V2 and V4; check backend service version
  • App update strategy: MDK supports OTA updates for metadata; native apps need app store updates

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.