/TrialLicensing

Swift framework to deal with licensing and time-based trial periods in macOS apps.

Primary LanguageSwiftMIT LicenseMIT

Time-Based Trial App Licensing

Adds time-based trial and easy license verification using CocoaFob to your macOS app.

Carthage compatible

For setup instructions of your own store and app preparation with FastSpring, have a look at my book Make Money Outside the Mac App Store!

Installation

Swift Package Manager

dependencies: [
  .package(url: "https://github.com/CleanCocoa/TrialLicensing.git", .upToNextMajor(from: Version(3, 0, 0))),
]

Add package depencency via Xcode by using this URL: https://github.com/CleanCocoa/TrialLicensing.git

CocoaFob is automatically linked statically for you, no need to do anything.

Carthage

  • Include the Trial and TrialLicense libraries in your project.
  • Include the CocoaFob (Swift 5) library in your project, too. (You have to link this in the app because a library cannot embed another library.)

Usage

  • Create an AppLicensing instance with licenseChangeBlock and invalidLicenseInformationBlock handling change events.
  • Set up and start the trial.

Example:

import TrialLicense

let publicKey = [
        "-----BEGIN DSA PUBLIC KEY-----\n",
        // ...
        "-----END DSA PUBLIC KEY-----\n"
    ].join("")
let configuration = LicenseConfiguration(appName: "AmazingApp!", publicKey: publicKey)

class MyApp: AppLicensingDelegate {

    init() {

        AppLicensing.setUp(
            configuration: configuration,
            initialTrialDuration: Days(30),

            // Set up the callbacks:
            licenseChangeBlock: self.licenseDidChange(licenseInfo:),
            invalidLicenseInformationBlock: self.didEnterInvalidLicenseCode(name:licenseCode:),

            // Get notified about initial state to unlock the app immediately:
            fireInitialState: true)
    }

    func licenseDidChange(licenseInformation: LicenseInformation) {

        switch licenseInformation {
        case .onTrial(_):
            // Changing back to trial may be possible if you support unregistering
            // form the app (and the trial period is still good.)
            return

        case .registered(_):
            // For example:
            //   displayThankYouAlert()
            //   unlockApp()

        case .trialUp:
            // For example:
            //   displayTrialUpAlert()
            //   lockApp()
            //   showRegisterApp()
        }
    }

    func didEnterInvalidLicenseCode(name: String, licenseCode: String) {

        // For example:
        //   displayInvalidLicenseAlert()
        // -- or show an error label in the license window.
    }
}

let myApp = MyApp()

Privacy Manifest

The package declares usage of UserDefaults API because it ships with these mechanisms to read/write information in principle. That's it.

If you use this to store actual customer names and/or email addresses in your app, depending on the licensing scheme you use, you should check that your app has a NSPrivacyCollectedDataTypes entry with e.g. a value of NSPrivacyCollectedDataTypeEmailAddress.

Components

LicenseInformation reveals the state your app is in:

enum LicenseInformation {
    case registered(License)
    case onTrial(TrialPeriod)
    case trialUp
}

The associated types provide additional information, for example to display details in a settings window or show remaining trial days in the title bar of your app.

License represents a valid name--license code pair:

struct License {
    let name: String
    let licenseCode: String
}

TrialPeriod encapsulates the duration of the trial.

struct TrialPeriod {
    let startDate: Date
    let endDate: Date
}

TrialPeriod also provides these convenience methods:

  • ended() -> Bool
  • daysLeft() -> Days

... where Days encapsulates the remainder for easy conversion to TimeInterval and exposing userFacingAmount: Int for display.

License

Copyright (c) 2016 by Christian Tietze. Distributed under the MIT License. See the LICENSE file for details.