Development

Spend Less Time Coding

With DevMateKit, you don’t need to write tons of boring code. Integrate DevMateKit into your application to make it instantly ready for licensing, crash reporting, updates delivery, tracking, and feedback collection.



What Does DevMateKit Include?

Licensing

Activation Window

Licensing protects your application from illegal usage, supports the entire activation process, and allows you to create trial versions. For apps selling via FastSpring, automatic licensing and order fulfillment is available. Once set up, an activation key will be delivered to your customers right away.

Installing Kevlar

Enabling Activation and Trial functionality requires integration of the specific Kevlar library. This library is unique for each application in DevMate and, therefore can be used only for one particular app. It ensures secure communication between your app and DevMate as well as manages license information on the app's side.

  1. Take the steps described in Integrate DevMateKit if you haven't done it yet.

The latest version of DevMateKit is available here.

  1. Download a Kevlar library from the corresponding page of DevMate's Add Application wizard. Alternatively, choose your app from the list of applications and go to Settings > DevMateKit. Disclose 'Activations and Trial' block and click Download Kevlar Library.
  2. Extract files from the downloaded archive and drop them to your project. Apply the following settings in the dialogue that appears:
    • Select the 'Copy items if needed' checkbox
    • Choose the 'Create groups' option
    • Deselect all items in the 'Add to targets' list.
  1. Link the Kevlar library to your project. Go to the Build Settings tab and add the following string to Other Linker Flags:
    • If the Kevlar folder is located in the project root folder:
-ObjC "$(PROJECT_DIR)/Kevlar/libkevlar.a" -lc++ -framework Security -framework IOKit
  • If the Kevlar folder is located in other location:
-ObjC <path_to_Kevlar_lib> -lc++ -framework Security -framework IOKit

Enable Activations

  1. Add the following code to the import section of your application Objective-C delegate class file or to the special bridging header of your Swift target:
#import "DMKevlarApplication.h"

Before importing the DevMateKit framework, please make sure that the DMKevlarApplication.h header file is included. Otherwise, you will receive a warning console message (WARNING: Need Kevlar library for correct work).

  1. Add the following method to your application delegate class implementation:
Objective-C
Swift 3
- (IBAction)startActivationProcess:(id)sender {
    if (!DMKIsApplicationActivated(NULL)) {
        [DevMateKit runActivationDialog:nil inMode:DMActivationModeFloating];
    }
}
@IBAction func startActivationProcess(sender: AnyObject?) {
    // Swift does’t work with macros, so check our Examples project on GitHub (https://github.com/DevMate/DevMateKit)
    //     to see how to create _my_secret_activation_check variable
    var kevlarError = DMKevlarError.testError
    if (false == _my_secret_activation_check!(&kevlarError).boolValue || kevlarError != DMKevlarError.noError) {
        DevMateKit.runActivationDialog(nil, in: DMActivationMode.modal)
    }
}
  1. Connect the newly added action method with the corresponding menu item or button inside the XIB files.

See GitHub example for more information.

Enable Trial

  1. Add the following code to the applicationDidFinishLaunching method of your application delegate class to initialize time trial. This example is for a one-week trial period:
Objective-C
Swift 3
if (!DMKIsApplicationActivated(NULL)) {
		[DevMateKit setupTimeTrial:nil withTimeInterval:kDMTrialWeek];
}
// Swift does’t work with macros, so check our Examples project on GitHub (https://github.com/DevMate/DevMateKit)
//     to see how to create _my_secret_activation_check variable
if !_my_secret_activation_check!(nil).boolValue {
    DevMateKit.setupTimeTrial(nil, withTimeInterval: kDMTrialWeek)
}
  1. Build and run your application.

See our GitHub example for more information.

While we do our best to keep DevMate frameworks secure and reliable, you should protect your software carefully against cracking attempts as well.

For your convenience, we prepared a few tips that will help you protect your app.

Functionality-Based Trial

You can set a trial period that ends after a predefined number of applications is launched or when a certain action is taken (for example, a click on a button). See our GitHub example for more information.

Customize User Interfaces

There are several methods of customizing activation windows:

  • Instead of +[DMActivationController sharedController] or +[DMActivationController defaultController] methods initialize controller with -[DMActivationController initWithWindowNibName:] passing your .nib file name here.
  • Create your own subclasses of the DMStepController class (with its XIB) and register/re-register them using the -registeredStepController:withNibName:forActivationStep: method.
  • Use DMActivationController delegate methods to change controller behavior.

Setting Up Activation by URL Scheme

Activation by the URL scheme enables activation directly from an email or web page. To configure activation by URL scheme, add the following code snippet to the info.plist file of your application:

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleURLName</key>
        <string>com.devmate.ActivationScheme</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>schemename1</string>
            ... // other scheme names that also will be uses for activation
        </array>
	</dict>
    .... // other URL schemes used for other purposes
</array>

CFBundleURLTypes is a special key, which contains NSArray of NSDictionaries. One of them is used for registration of activation schemes. It contains a CFBundleURLName special key with the value set to com.devmate.ActivationScheme.

<string>schemename1</string> is the declaration of the scheme (replace schemname1 with a name of a URL scheme to be used).

You can view an example on GitHub.

FastSpring Embedded Store

FastSpring Embedded Store simplifies software purchases by integrating a checkout page directly into the application.

To enable FastSpring Embedded Store, implement some specific delegate methods from DMFsprgEmbeddedStoreDelegate protocol in <DevMateKit/DMActivationController.h> file.

You can use the DevMateKit implementation of the Embedded Store dialog with DMFsprgEmbeddedStoreNative type. In this case, you can manage the behavior of the dialog with delegate methods.

If you want to implement your own Embedded Store dialog, use DMFsprgEmbeddedStoreCustom type. In this case, your delegate should implement -activationController:waitsForCustomFsprgEmbeddedStore: method.

You can view an example on GitHub.

To change appearance of the Embedded Store, read Embedded Store Appearance Customization.

Deactivate License

If you want to add an ability to deactivate a license from UI of your app, connect -[NSApplication invalidateLicense] to the corresponding button or menu item.


Updates

The Updates mechanism, based on the improved Sparkle framework, delivers the latest version of your application to every customer.

Advanced update features allow you to offer delta updates, test update performance before rollout, limit your update for a specific OS, customize update scenarios, plan releases in advance with version drafts, and more.

Update Window

Take the steps described in Integrate DevMateKit if you haven't done it yet.

It is not necessary to add anything to SUUpdater key in info.plist if you make an initial DevMateKit setup. The DevMateKit configured in the way that update XML is already built-in. If you are migrating an application, refer to this article.

Test Updates Integration

  1. Add a new Object component from 'Object library' to your main XIB file and change its class name to SUUpdater.
  2. Connect the checkForUpdates action of a newly created SUUpdater object with the corresponding menu item or button inside the XIB files.
  3. Build and run your application. Try to update it.

See a GitHub example for more information.

We strongly recommend testing the update procedure before publishing a release. To enable testing mode, do any of the following:

  • Set SUUpdaterIsInTestMode value to YES/NO in your application user defaults
  • Implement SUUpdater delegate method -isUpdaterInTestMode:

To check how update for beta versions works, do any of the following:

  • Set SUUpdaterChecksForBetaUpdates value to YES/NO in your application user defaults
  • Implement SUUpdater delegate method -updaterShouldCheckForBetaUpdates:

Implemented delegate methods have higher priority than values saved in user preferences. For more information, check the SUUpdaterDelegate_DevMateInteraction category in the <DevMateKit/SUUpdater.h> header file.

Additionally, you can configure updates of your main application bundle from the helper app. Refer to the GitHub example to learn more.

Silent Updates

Silent updates are supported in DevMateKit v1.3 and higher. If you have an older version of DevMateKit, consider updating it.

Silent updates are turned off by default. To enable this feature, do the following:

  1. To turn on automatic check for updates (by default), add the following code snippet when initializing your SUUpdater instance:
[SUUpdater sharedUpdater].automaticallyChecksForUpdates = YES;

Alternatively, add the SUEnableAutomaticChecks key set to YES into your Info.plist file.

  1. To turn on automatic download and installation of updates, configure the SUUpdater instance:
[SUUpdater sharedUpdater].automaticallyDownloadsUpdates = YES;

We recommend giving your users the choice between silent and manual updates. For example, this capability can be implemented via the corresponding checkboxes in preferences ('Check for updates automatically' and 'Install updates automatically').


Crash Reports

Issue Reporter Window

Nobody wants their application to crash. But once it happens, you better have instant and detailed information at your fingertips to figure out what wrong and to fix it immediately.

With DevMateKit, your app quickly collects crash reports and sends them to DevMate in real time. All crash or issue reports are processed and symbolicated on DevMate’s server.

  1. Take the steps described in Integrate DevMateKit if you haven't done it yet.
  2. Add the following code snippet to the applicationDidFinishLaunching method of your application delegate class:
Objective-C
Swift
[DevMateKit setupIssuesController:nil reportingUnhandledIssues:YES];
DevMateKit.setupIssuesController(nil, reportingUnhandledIssues: true)
  1. Build and run your application.

To test issue the reporting functionality, run the following command in the terminal:

<executable> [-test_crash [delay_sec]] [-test_exception [delay_sec]]

where:

  • <executable> is a path to the main executable of your app (e.g., <my_app_path>/MyApp.app/Contents/MacOS/MyApp). You can drag and drop your application from Xcode to Terminal and add MyApp.app/Contents/MacOS/MyApp.
  • test_crash is an argument to initiate crash.
  • test_exception is an argument to initiate exception.
  • delay_sec is time in seconds before an issue dialogue appear (don't specify to open an issue dialogue immediately).

To learn more, see an example on GitHub.

Customization

You can automatically fill in 'Name' and 'Email' fields of an issue reporting dialog with the values specified by a user during activation. Also, you can add some predefined text to the problem description field.

@property (nonatomic, retain) NSDictionary *defaultUserInfo;
 
FOUNDATION_EXPORT NSString *const DMIssuesDefaultUserNameKey; // NSString
FOUNDATION_EXPORT NSString *const DMIssuesDefaultUserEmailKey; // NSString
FOUNDATION_EXPORT NSString *const DMIssuesDefaultCommentKey; // NSString

To send a custom app log in an issue report to DevMate, specify URL to this log file(s).

@property (nonatomic, retain) NSArray *logURLs;

To track the relaunch of an application after crash, pass an object that implements the reporterWillRestartApplication method as delegate for the issue controller.

@protocol DMIssuesControllerDelegate <NSObject>
@optional
- (void)reporterWillRestartApplication:(DMIssuesController *)controller;
@end

To customize the UI, you need to subclass DMIssuesWindowController class and set it to the main controller using the -[DMIssuesController setIssuesWindowControllerClass:] method.

To ensure correct work of the issue reporter, implement DMIssuesWindowController subclass and all other resources/classes in a separate framework.


Feedback

Your customers' feedback is important, especially during beta testing. By integrating feedback collection into your application, you give customers an easy way to give a feedback and suggest new features right from the app.

All feedback is collected in the DevMate Dashboard, giving you a possibility to process and work with them.

Feedback Window

  1. Take the steps described in Integrate DevMateKit if you haven't done it yet.
  2. Add the following method to your application delegate class implementation:
Objective-C
Swift
- (IBAction)showFeedbackDialog:(id)sender {
    [DevMateKit showFeedbackDialog:nil inMode:DMFeedbackIndependentMode];
}
@IBAction func showFeedbackDialog(sender: AnyObject?)  {
    DevMateKit.showFeedbackDialog(nil, inMode: DMFeedbackMode.IndependentMode)
}
  1. Connect the newly added action method with the corresponding menu item or button in the XIB files.
  2. Build and run your application.

Send a feedback message as you defined in the previous step. If everything is configured correctly, your message will be displayed in the Feedback Management section of DevMate.

To learn more, see a GitHub example.

Customization

You can automatically fill in 'Name' and 'Email' fields of a feedback reporting dialog with the values specified by a user during activation. Also, you can add some predefined text to the feedback message field.

@property (nonatomic, retain) NSDictionary *defaultUserInfo;
 
FOUNDATION_EXPORT NSString *const DMFeedbackDefaultUserNameKey; // NSString object
FOUNDATION_EXPORT NSString *const DMFeedbackDefaultUserEmailKey; // NSString object
FOUNDATION_EXPORT NSString *const DMFeedbackDefaultCommentKey; // NSString object

To send a custom app log in a feedback message to DevMate, specify URL to this log file(s).

@property (nonatomic, retain) NSArray *logURLs;

Also, you can select the feedback window behavior to:

  • independent (by default)
  • child
  • modal
  • sheet
  • floating

by setting the desired DMFeedbackMode during the method call.

You can also set additional action that will be performed after the feedback message is sent, for example:

Objective-C
Swift
- (IBAction)showFeedbackDialog:(id)sender {
    DMFeedbackController *controller = [DMFeedbackController sharedController];
    [controller showFeedbackWindowInMode:DMFeedbackIndependentMode completionHandler:^(BOOL success) {
        NSLog(@"Did finish feedback with success: %@", success ? @"TRUE" : @"FALSE");
    }];
}
@IBAction func showFeedbackDialog(sender: AnyObject?) {
    let controller = DMFeedbackController.sharedController()
    controller.showFeedbackWindowInMode(DMFeedbackMode.IndependentMode) { (success) -> Void in
        let message = success ? "TRUE" : "FALSE"
        print("Did finish feedback with success: \(message)")
    }
}

In this case, after sending a feedback successfully, a user receives 'Did finish feedback with success: TRUE' console message. Otherwise, they receive 'Did finish feedback with success: FALSE' message.



Analyze Collected Data

During updates delivery, crash reporting, application launches and activations, DevMateKit collects and reports anonymous data to DevMate. Based on this data, extensive reports are then created in the DevMate Dashboard, enabling you to monitor the number of customers that have updated, activated your software, and experienced crashes, all-in-one place.

Analyze Collected Data and Reports