Best practices for iOS apps

When developing iOS apps, use these best practices to improve compatibility between Citrix Endpoint Management and mobile apps for iOS devices.

MDX App SDK Framework and wrapping

If your app uses the MDX App SDK Framework, then you must use the matching MDX Toolkit version for wrapping. A version mismatch between these two components might cause improper operation.

To prevent such a mismatch, wrap the app as an ISV app and specify an app mode of Premium or General. That lets you deliver a pre-wrapped app. As a result, your customer doesn’t need to wrap the app, thus avoiding use of a mismatched MDX Toolkit. For details about ISV wrapping, see Wrapping iOS mobile apps.

Use explicit app IDs

If your iOS Developer Enterprise account does not support wildcard App IDs, be sure to create an explicit App ID for each app you plan to wrap with the MDX Toolkit. Also, create a provisioning profile for each App ID.

Don’t block the main thread

Don’t use blocking code when running on the main thread. This is an Apple guideline, but it is even more crucial with Citrix Endpoint Management. Some actions can take more time in a managed app or can block further thread execution. File, database, and network operations are examples of operations that might block the currently running thread and should be avoided on the main thread.

Write robust code

In particular, you should write apps following the best practices as documented in the Apple programming guides, such as the Apple Application Programming Guide.

Use only Apple published interfaces

Check return values from all API calls and handle any exceptions that may occur as a side effect of an API call. This effort ensures a graceful error recovery or graceful termination of the app. While this is a common programming best practice, it is especially important for managed apps.

Various APIs that you’d expect to work fail if the underlying functionality has been blocked due to Citrix Endpoint Management policies. Examples would include any of the capabilities described earlier:

  • Networking APIs fail as if there is no network available.
  • Sensor APIs, such as GPS and camera, return null or throw an exception.

The following Objective-C runtime selectors return nil if the underlying functionality has been blocked due to Citrix Endpoint Management policies and so should be handled accordingly.

Object class: AVCaptureDevice

  • Selector name: devicesWithMediaType:

Object class: MFMailComposeViewController

  • Selector name: init:

Object class: MFMessageComposeViewController

  • Selector name: initWithNibName:bundle:

Object class: NSFileManager

  • Selector name: URLForUbiquityContainerIdentifier:

Object class: NSUbiquitousKeyValueStore

  • Selector name: defaultStore:

Object class: PHPhotoLibrary

  • Selector name: sharedPhotoLibrary:

Object class: UIImagePickerController

  • Selector name: availableCaptureModesForCameraDevice:

Object class: UIPasteboard

  • Selector name:

    dataForPasteboardType:

    valueForPasteboardType:

    items:

    dataForPasteboardType:inItemSet:

    valuesForPasteboardType:inItemSet:

Object class: UIPopoverController

  • Selector name: initWithContentViewController:

Object class: UINavigationController

  • Selector name:

    ctxInitWithRootViewController:

    ctxPopToViewController:animated:

Redirect runtime interfaces

Citrix Endpoint Management provides UI Pin Prompt interaction so you don’t have to do it in your app.

To ensure Citrix Endpoint Management readiness, we suggest that you don’t redirect or substitute Objective-C runtime selectors. The reason is that Citrix Endpoint Management swizzles the underlying methods of several object class selectors to control or modify the runtime behavior of an app. The following table lists the Objective-C class selectors that Citrix Endpoint Management redirects:

Object Class Name: NSURLProtectionSpace

  • Selector name: serverTrust

Object Class Name: NSURLAuthenticationChallenge

  • Selector name: sender

Object Class Name: NSURLConnection

  • Selector name:

    sendSynchronousRequest:returningResponse:error:

    initWithRequest:delegate:startImmediately:

    initWithRequest:delegate:

    connectionWithRequest:delegate:

Object Class Name: NSURLConnectionDelegate

  • Selector name:

    connection:canAuthenticateAgainstProtectionSpace:

    connection:didReceiveAuthenticationChallenge:

    connection:willSendRequestForAuthenticationChallenge:

Object Class Name: NSURLSessionConfiguration

  • Selector name:

    defaultSessionConfiguration

    ephemeralSessionConfiguration

Object Class Name: ALAssetsLibrary

  • Selector name: authorizationStatus

Object Class Name: AVAudioRecorder

  • Selector name:

    record

    prepareToRecord

    recordForDuration:

    recordAtTime:

    recordAtTime:ForDuration:

Object Class Name: AVAudioSession

  • Selector name: recordPermission

Object Class Name: AVCaptureDevice

  • Selector name:

    devices

    devicesWithMediaType:

Object Class Name: AVAsset

  • Selector name: assetWithURL:

Object Class Name: AVURLAsset

  • Selector name:

    initWithURL:options:

    URLAssetWithURL:options:

Object Class Name: AVPlayerItem

  • Selector name:

    playerItemWithAsset:

    initWithURL:

    playerItemWithURL:

Object Class Name: AVPlayer

  • Selector name:

    playerWithPlayerItem:

    initWithPlayerItem:

    initWithURL:

Object Class Name: CLLocationManager

  • Selector name: startUpdatingLocation

Object Class Name: UIScrollView

  • Selector name: setContentOffset:

Object Class Name: MFMailComposeViewController

  • Selector name:

    canSendMail

    init

Object Class Name: MFMessageComposeViewController

  • Selector name:

    canSendText

    initWithNibName:bundle:

Object Class Name: NSFileManager

  • Selector name: URLForUbiquityContainerIdentifier:

Object Class Name: NSUbiquitousKeyValueStore

  • Selector name: defaultStore

Object Class Name: PHPhotoLibrary

  • Selector name: authorizationStatus

Object Class Name: QLPreviewController

  • Selector name:

    setDataSource:

    canPreviewItem:

Object Class Name: QLPreviewControllerDataSource

  • Selector name:

    numberOfPreviewItemsInPreviewController:

    previewController:previewItemAtIndex:

Object Class Name: SLComposeViewController

  • Selector name: isAvailableForServiceType:

Object Class Name: UIActivityViewController

  • Selector name:

    initWithActivityItems:applicationActivities:

    setExcludedActivityTypes:

Object Class Name: UIApplication

  • Selector name:

    openURL:

    canOpenURL:

    setApplicationIconBadgeNumber:

Object Class Name: UIDocument

  • Selector name:

    closeWithCompletionHandler:

    contentsForType:error:

Object Class Name: UIDocumentInteractionController

  • Selector name:

    interactionControllerWithURL:

    setURL:

    setDelegate:

    presentPreviewAnimated:

    presentOpenInMenuFromBarButtonItem:animated:

    presentOpenInMenuFromRect:inView:animated:

    presentOptionsMenuFromBarButtonItem:animated:

    presentOptionsMenuFromRect:inView:animated:

Object Class Name: UIDocumentMenuViewController

  • Selector name: initWithDocumentTypes:inMode:

Object Class Name: UIImage

  • Selector name: imageNamed:

Object Class Name: UIImagePickerController

  • Selector name: setSourceType:

    takePicture

    startVideoCapture

    isSourceTypeAvailable:

    isCameraDeviceAvailable:

    isFlashAvailableForCameraDevice:

    availableCaptureModesForCameraDevice:

    setMediaTypes

Object Class Name: UINavigationController

  • Selector name:

    ctxInitWithRootViewController:

    ctxPushViewController:animated:

    ctxPopToViewController:animated:

Object Class Name: UIPasteboard

  • Selector name:

    generalPasteboard

    pasteboardWithName:create:

    pasteboardWithUniqueName

    setValue:forPasteboardType:

    setData:forPasteboardType:

    setItems:

    addItems:

    dataForPasteboardType:

    valueForPasteboardType:

    numberOfItems

    pasteboardTypes

    pasteboardTypesForItemSet:

    containsPasteboardTypes:

    containsPasteboardTypes:inItemSet:

    items

    itemSetWithPasteboardTypes:

    dataForPasteboardType:inItemSet:

    valuesForPasteboardType:inItemSet:

    string

    strings

    URL

    URLs

    image

    images

    color

    colors

Object Class Name: UIPopoverController

  • Selector name: initWithContentViewController

Object Class Name: UIPrintInteractionController

  • Selector name:

    isPrintingAvailable

    presentAnimated:completionHandler:

    presentFromBarButtonItem:animated:completionHandler:

    presentFromRect:inView:animated:completionHandler:

Object Class Name: UIViewController

  • Selector name: presentViewController:animated:completion:

Object Class Name: UIWebView

  • Selector name:

    loadRequest:

    setDelegate:

    UIWebViewDelegate

    webView:shouldStartLoadWithRequest:navigationType:

    webViewDidStartLoad:

    webViewDidFinishLoad:

    webView:didFailLoadWithError:

Object Class Name: UIWindow

  • Selector name: makeKeyAndVisible

Object Class Name: UIApplicationDelegate

  • Selector name:

    applicationDidFinishLaunching:

    application:didFinishLaunchingWithOptions:

    application:willFinishLaunchingWithOptions:

    applicationWillResignActive:

    applicationDidEnterBackground:

    applicationWillEnterBackground:

    applicationDidBecomeActive:

    applicationWillTerminate:

    application:openURL:sourceApplication:annotation:

    application:handleOpenURL:

    applicationProtectedDataWillBecomeUnavailable:

    applicationProtectedDataDidBecomeAvailable:

    application:performFetchWithCompletionHandler:

    application:handleEventsForBackgroundURLSession:completionHandler:

    application:didReceiveLocalNotification:

    application:didReceiveRemoteNotification:

    application:didReceiveRemoteNotification:fetchCompletionHandler:

    application:didRegisterForRemoteNotificationsWithDeviceToken:

    application:didFailToRegisterForRemoteNotificationsWithError:

    applicationSignificantTimeChange:

    application:shouldAllowExtensionPointIdentifier:

Object Class Name: QLPreviewController

  • Selector name: allocWithZone:

Ensure data encryption compatibility

One of the primary features of MDX is that all persisted data is transparently encrypted. You don’t need to modify your app to gain this functionality and, in fact, you can’t directly avoid it. The Citrix Endpoint Management admin can disable encryption either selectively or entirely, but not the app.

This is one of the more heavyweight aspects of MDX and requires an understanding of the following points:

  • File encryption is present for all native code that runs in managed processes.

    The file data encryption implementation supports all native code and not just code for apps using the Apple frameworks and the Apple Objective-C runtime. Any file data encryption implemented within and solely for the Objective-C runtime can be easily subverted.

  • Some framework APIs, such as AVPlayer class, UIWebView class, and QLPreviewController, are implemented by iOS service processes in a different execution context than the user’s managed app process.

    These service processes cannot decrypt MDX encrypted file data. Therefore, the managed app must provide the service process with a temporary unencrypted copy of the data. The copy is deleted by the managed app after 5 seconds. It is important that you are aware of the limitation when using these classes. The reason is that we lose containment control of the data provided to these classes due to Apple implementation of the specific classes.

  • Memory mapping is problematic for Citrix Endpoint Management encryption since it relies on app calling file I/O system call interfaces.

    After a file is memory mapped, the I/O requests for the file are managed outside of the context of the user app bypassing Citrix Endpoint Management encryption. All POSIX mmap(2) calls by a managed app are mapped as MAP_PRIVATE and MAP_ANON and not associated with any file description. An attempt is made to read in all the mapped data during the mmap call if a file description is specified to fault in all the data since any subsequent paging in of data by the operating system results in reading encrypted data without it being decrypted by Citrix Endpoint Management. This technique has been successful in all apps tested with Citrix Endpoint Management since the amount of data that is memory mapped is small with no memory page reclaims happening within the app.

  • Encryption adds measurable overhead. Developers should optimize disk I/O to prevent performance degradation. For example, if you’re repeatedly reading and writing the same information, you might want to implement an app level cache.

  • Citrix Endpoint Management only encrypts instances of the Apple libsqlite.dylib . If the application directly links with and/or embeds an private version of the libsqlite.dylib , the databases instances of that private library are not encrypted by Citrix Endpoint Management.

  • Apple SQLite databases are encrypted by Citrix Endpoint Management using the SQLite Virtual File System layer.

    Performance can be an issue. The standard database cache size is 2000 pages or 8 megabytes. If your database is large, a developer may need to specify SQLite pragma to increase the database cache size. In Objective-C Core Data Framework, the SQLite pragma can be added as an option dictionary when adding the Persistent Store object to the Persistent Store Controller object.

  • SQLite WAL mode is not supported since the library is relinked to file I/O interfaces and internally uses memory mapping extensively.

  • NSURLCache DiskCache is implemented by iOS using a SQLite database. Citrix Endpoint Management disables the disk cache associated since this database is referenced by unmanaged iOS service processes.

  • The following is a list of the hardcoded excluded file path name patterns:

    • .plist: Excluded due to access by iOS system processes outside process context.
    • .app: Legacy substring in the Application Bundle name. This substring is deprecated because an explicit Application Bundle path is now excluded.
    • .db: A file with this suffix is not encrypted if the file is not a SQLite database.
    • /System/Library: File paths that exist within the app bundle sandbox directory and file paths outside the app data sandbox cannot be encrypted. On iOS, the installed app is read only and is in a different directory than the app data files that the app produces and stores when it is run.
    • Library/Preferences: Files are accessed by iOS directly. Normally only .plist files are present in this directory path.
    • /com.apple.opengl/: iOS accesses the files directly.
    • csdk.db: Legacy Citrix SSLSDK SQLite database
    • /Library/csdk.sql: Citrix SSLSDK SQLite database
    • CtxLog_: Citrix log file name prefix
    • CitrixMAM.config: MDX internal file name
    • CitrixMAM.traceLog: Legacy MDX internal file name
    • CtxMAM.log: MDX internal file name
    • data.999: MDX internal file name
    • CTXWrapperPersistentData: MDX internal file name
    • /Documents/CitrixLogs: MDX log directory
    • /Document/CitrixLogs.zip: Compressed MDX log directory name
    • Any file in the app Bundle directory path: Read-only directory of app files
  • Citrix Endpoint Management substitutes an instance of the private Citrix Endpoint Management SecureViewController class for instances of the Apple Objective-C QLPreviewController object class at runtime. Citrix Endpoint Management SecureViewController class is derived from the Apple Objective-C UIWebView object class. The QLPreviewController object class natively supports a few file formats which the UIWebView object class doesn’t natively support, such as the audio and PDF types.

  • For best performance, file I/O requests should be issued to file offsets which are a multiple of 4096 bytes and should be issued for a length which is also a multiple of 4096 bytes.

  • The O_NONBLOCK file mode flag is not supported by Citrix Endpoint Management encryption. This file mode flag is removed from the list of modes when processed by Citrix Endpoint Management.

Encryption user entropy

One Citrix Endpoint Management option for encryption requires the end user to enter a PIN before the encryption key can be generated. This option is called user entropy. It can cause a particular issue for apps.

Specifically, no file or database access can be performed until the user enters a PIN. If such an I/O operation is present in a location that runs before the PIN UI can be displayed, it will always fail.

To ensure that this issue isn’t present in your app, test with user entropy enabled. The Citrix Endpoint Management client property, Encrypt secrets using Passcode, adds user entropy. You configure that client property, which is disabled by default, in the Citrix Endpoint Management console under Configure > Settings > More > Client Properties.

Data containment compatibility

  • Any remote view controllers will not have security containment (for example, data encryption; copy, cut, and paste policy blocking; and so on) because a remote view controller runs in a different process context than the MDX-managed app.
  • The Copy action is only action supported from UIResponder. Other actions, such as Cut and Delete, are not supported.
  • AirDrop is only intercepted at the UI level, not at a lower level.
  • MFI and Bluetooth are not intercepted.

Icon file support

MDX wrapping requires the presence of at least one icon that can be used as the home screen icon or app icon. App developers can add their icons to the Asset Catalog, or use the CFBundleIcons or CFBundleIconFiles keys in Info.plist.

The MDX Toolkit picks the first one from the list of known plist locations in Info.plist:

  • CFBundleIcons
  • CFBundlePrimaryIcon
  • CFBundleIconFiles
  • UINewsstandIcon
  • CFBundleDocumentTypes

If none of those keys is found in Info.plist, the MDX Toolkit will identify one of the following icons in the root folder of the app bundle:

  • Icon.png
  • Icon-60@2x.png
  • Icon-72.png
  • Icon-76.png

Networking and micro VPN

MDX currently manages only those networking calls issued directly by an app. Some DNS queries are issued directly by the Apple framework and so are not managed by MDX.

Several Citrix Endpoint Management policy options are available to administrators for networking.

The Network access policy prevents, permits or redirects app network activity.

Important:

The MDX Toolkit version 18.12.0 release included new policies that combined or replaced older policies. The Network Access policy combines Network access, Preferred VPN mode, and Permit VPN mode switching. The Exclusion list policy replaces Split tunnel exclusion list. The micro VPN session required policy replaces Online session required. For details, see What’s new in earlier releases.

Tunneled - Web SSO is the name for Secure Browse in the settings. The behavior is the same.

The options are as follows:

  • Use Previous Settings: Defaults to the values you had set in the earlier policies. If you change this option, you shouldn’t revert to Use Previous Settings. Also note that changes to the new policies do not take effect until the user upgrades the app to version 18.12.0 or later.
  • Blocked: Networking APIs used by your app will fail. Per the previous guideline, you should gracefully handle such a failure.
  • Unrestricted: All network calls go directly and are not tunneled.
  • Tunneled - Full VPN: All traffic from the managed app tunnels through Citrix Gateway.
  • Tunneled - Web SSO: The HTTP/HTTPS URL is rewritten. THis option allows only the tunneling of HTTP and HTTPS traffic. A significant advantage of Tunneled - Web SSO is single sign-on (SSO) for HTTP and HTTPS traffic and also PKINIT authentication. On Android, this option has low setup overhead and is thus the preferred option for web browsing types of operations.
  • Tunneled - Full VPN and Web SSO: Permits switching between VPN modes automatically as needed. If a network request fails due to an authentication request which cannot be handled in a specific VPN mode, it is retried in an alternate mode.

Limitations

  • Users cannot play videos hosted on internal websites in iOS wrapped MDX apps because the videos play in a media player process on the device that MDX does not intercept.
  • NSURLSession background download (NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier) is not supported.
  • We block UDP traffic if the Network access policy is set to Blocked. We don’t tunnel UDP traffic if the Network access policy is set to Tunneled - Full VPN.
  • MDX-wrapped apps cannot instantiate a socket server that listens for inbound connections. However, MDX-wrapped apps can use a client socket to connect to a server.

Third-party library support

Some app frameworks have compatibility issues with Citrix Endpoint Management:

  • Apps developed with Xamarin cross platform development environment are supported. Citrix does not officially declare support for other cross platform development environments due to insufficient examples of use and testing.
  • SQLCipher doesn’t work with encryption because it uses memory mapping. One solution is to not use SQLCipher. A second solution is to exclude the database file from encryption using an encryption exclusion policy. A Citrix Endpoint Management administrator must configure the policy in the Citrix Endpoint Management console.
  • App and third-party libraries which link directly to OpenSSL libcrypto.a and libssl.a libraries can result in a link error due to missing symbols and link errors due to multiple symbol definition.
  • Apps requiring support for Apple Push Notification Service needs to follow specific steps which Apple requires.
  • Citrix Endpoint Management explicitly sets the SQLite database version to 1 in order to disable Write Ahead Logging (WAL) file and memory mapped file support within SQLite databases. Any attempt to directly access SQLite interfaces in SQLite version 2 or version 3 fails.