Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

best way to add license section to iOS settings bundle

I think I've now managed to solve all the problems I was running into.

  • It seems to be best to use group element titles to hold the licenses (this is what Apple do in the iWork apps). There is however a limit on the length of these (and I've not yet discovered exactly what the limit is), so you need to break each license file into multiple strings.
  • You can create a line break within these by include a literal carriage return (ie. otherwise known as ^M, \r or 0x0A)
  • Make sure not to include any literal "s mid-text. If you do, some or all of the strings in the file will get silently ignored.

I've got a convenience script I use to help generate the .plist and .strings file, shown below.

To use it:

  1. Create a 'licenses' directory under your project
  2. Put script into that directory
  3. Put each license into that directory, one per file, with filenames that end .license
  4. Perform any necessary reformatting on the licenses. (eg. remove extra spaces at the beginning of lines, ensure that there are no line breaks mid-paragraph). There should be a blank line in-between each paragraph
  5. Change to licenses directory & run the script
  6. Edit your settings bundle Root.plist to include a child section called 'Acknowledgements'

Here's the script:

#!/usr/bin/perl -w

use strict;

my $out = "../Settings.bundle/en.lproj/Acknowledgements.strings";
my $plistout =  "../Settings.bundle/Acknowledgements.plist";

unlink $out;

open(my $outfh, '>', $out) or die $!;
open(my $plistfh, '>', $plistout) or die $!;

print $plistfh <<'EOD';
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
        <key>StringsTable</key>
        <string>Acknowledgements</string>
        <key>PreferenceSpecifiers</key>
        <array>
EOD
for my $i (sort glob("*.license"))
{
    my $value=`cat $i`;
    $value =~ s/\r//g;
    $value =~ s/\n/\r/g;
    $value =~ s/[ \t]+\r/\r/g;
    $value =~ s/\"/\'/g;
    my $key=$i;
    $key =~ s/\.license$//;

    my $cnt = 1;
    my $keynum = $key;
    for my $str (split /\r\r/, $value)
    {
        print $plistfh <<"EOD";
                <dict>
                        <key>Type</key>
                        <string>PSGroupSpecifier</string>
                        <key>Title</key>
                        <string>$keynum</string>
                </dict>
EOD

        print $outfh "\"$keynum\" = \"$str\";\n";
        $keynum = $key.(++$cnt);
    }
}

print $plistfh <<'EOD';
        </array>
</dict>
</plist>
EOD
close($outfh);
close($plistfh);

Setting up your Settings.bundle

If you haven't created a Settings.bundle, go to File --> New --> New File...

Under the Resource section, find the Settings Bundle. Use the default name and save it to the root of your project.

Expand the Settings.bundle group and select Root.plist. You will need to add a new section where its key will be Preference Items of type Array. Add the following information:

enter image description here

The Filename key points to the plist that was created by this script. You can change the title to what ever you want.

Execute Script At Build Time

Also, if you want this script to run whenever you build your project, you can add a build phase to your target:

  1. Go to your project file
  2. Select the target
  3. Click the Build Phases tab
  4. In the lower right corner of that pane, click on 'Add Build Phase'
  5. Select 'Add Run Script'
  6. Drag and drop your perl script into the section for your script. Modify to look something like this:
  1. cd $SRCROOT/licenses ($SRCROOT points to the root of your project)
  2. ./yourScriptName.pl

After you have finished that, you can drag the Run Script build phase sooner in the build process. You'll want to move it up before Compile Sources so that the updates to your Settings Bundle get compiled and copied over.

Update for iOS 7: iOS 7 seems to handle the "Title" key different and is messing up the rendered text. To fix that the generated Acknowledgements.plist needs to use the "FooterText" key instead of "Title". This how to change the script:

for my $str (split /\r\r/, $value)
{
    print $plistfh <<"EOD";
            <dict>
                    <key>Type</key>
                    <string>PSGroupSpecifier</string>
                    <key>FooterText</key> # <= here is the change
                    <string>$keynum</string>
            </dict>
 EOD

    print $outfh "\"$keynum\" = \"$str\";\n";
    $keynum = $key.(++$cnt);
}

Here's the same solution that @JosephH provided (without translations), but done in Python for anyone who prefers python over perl

import os
import sys
import plistlib
from copy import deepcopy

os.chdir(sys.path[0])

plist = {'PreferenceSpecifiers': [], 'StringsTable': 'Acknowledgements'}
base_group = {'Type': 'PSGroupSpecifier', 'FooterText': '', 'Title': ''}

for filename in os.listdir("."):
    if filename.endswith(".license"):
        current_file = open(filename, 'r')
        group = deepcopy(base_group)
        title = filename.split(".license")[0]
        group['Title'] = title
        group['FooterText'] = current_file.read()
        plist['PreferenceSpecifiers'].append(group)

plistlib.writePlist(
    plist,
    "../Settings.bundle/Acknowledgements.plist"
)

As an alternative, for those using CocoaPods, it will generate an 'Acknowledgements' plist for each target specified in your Podfile which contains the License details for each Pod used in that target (assuming details have been specified in the Pod spec). The property list file that can be added to the iOS settings bundle.

There's also projects under way to allow this data to be converted and displayed within the app instead:

https://github.com/CocoaPods/cocoapods-install-metadata

https://github.com/cocoapods/CPDAcknowledgements


I thought I'd throw my iteration on Sean's awesome python code in the mix. The main difference is that it takes an input directory and then recursively searches it for LICENSE files. It derives the title value from the parent directory of the LICENSE file, so it plays well with cocoapods.

The motivation was to create a build script to automatically keep the legal section of my app up to date as I add or remove pods. It also does some other things like remove forced newlines from licenses so the paragraphs look a bit better on the devices.

https://github.com/carloe/LicenseGenerator-iOS

enter image description here


I made a script in Ruby inspiered by @JosephH script. This version will, in my own opinion, better represent the individual open source projects.

Wisit iOS-AcknowledgementGenerator to download the script and sample project.

This is what acknowledgements will look like in your App:

Settings.appSettings.bundleAcknowledgementsenter image description here