Skip to content
David Siaw edited this page May 14, 2015 · 13 revisions

Structure of a Framework

A framework is a type of CPBundle that provides shared code and resources for Objective-J applications. It is similar to an Application bundle except it doesn't include a main entry point (main.j). The implementation of Frameworks in Cappuccino is similar to Cocoa Frameworks but differs in some ways.

A framework bundle is made of up several types of elements:

  • Objective-J code
  • Resources such as images, javascript code, stylesheets, etc.
  • Info.plist which describes the bundle

Objective-J Code

This is the probably the main reason for creating a framework - to share code between applications or create a library that others can use easily with their applications. The source code in a framework is written like any other Objective-J code with a few caveats:

  • When using @import for a source file within the framework, it is recommended to use the "local" flavor with double-quotes. This indicates to the reader that the import comes from within the framework.
@import "MyClass.j"              // best practice
@import "MyFramework/MyClass.j"  // requires OBJJ_SEARCH_PATHS contains parent directory
@import <MyFramework/MyClass.j>  // requires OBJJ_SEARCH_PATHS contains parent directory
@import <MyClass.j>              // doesn't indicate the file is part of the current framework
  • All of the sources of the framework must reside in the same directory. The Objj compiler currently cannot compile a framework with sources located in subdirectories, when those sources are imported by other sources in the framework. This will result in a "Duplicate Class" error at compile-time.

Resources

The "Resources" directory contains bundle resources. When CPBundle is used to load and locate resources, it looks in this directory. Since all Objj classes are loaded from bundles (either Application or Framework bundles), the Objj runtime knows what bundle each class comes from, and therefore can look to the "owning bundle" to locate resources. This is the default behavior and can be modified by calling CPBundle's methods directly.

Info.plist

This file is the bundle metadata, it contains key/value pairs of useful things to know about the bundle. There are several predefined keys that have special meaning to CPBundle:

Info.plist Key Example Value Description
CPBundleIdentifier com.mycompany.MyFramework Unique identifier for the bundle. Intent is to use as a key to store data about the bundle in cookies, databases, local storage etc. Best practice is to use reverse-dns notation to avoid name collisions with other frameworks/applications.
CPBundleInfoDictionaryVersion 6.0 Tells CPBundle what keys it can expect in the dictionary. Probably will always be 6.0.
CPBundleName MyFramework Name of the framework or application
CPBundlePackageType FMWK Type of bundle, should always be FMWK

You are free to add your own keys and access them by CPBundle's objectForInfoDictionaryKey:. This is useful for storing various constant data for the framework rather than hardcode into the sources. For example, it might contain URLs, api keys, etc to access web services the framework is calling.

Creating a Framework

The easiest way to create a framework is to use capp tool

capp gen --template Framework MyFramework

Will create a new skeleton in the folder "MyFramework". Now add any sources and resources you want to include in the framework.

Using Frameworks

OBJJ_SEARCH_PATHS Environment

This is the Objj runtime's equivalent of LD_LIBRARY_PATH on *nix systems and Mac OS X's DYLD_FRAMEWORK_PATH. It is a list of directories to look for frameworks in.

By default, OBJJ_SEARCH_PATHS is set to "Frameworks" which refers to the "Frameworks" directory in the application bundle, typically in the same directory as "main.j".

In the browser context, it is a global variable with an array of strings. In the CommonJS context it is a shell environment of colon-separated paths, also accessible through system.env.

For example, to use a common directory for the Cappuccino frameworks, they can be placed in /Frameworks on the web site. Then any application on that site, can access them by setting the following in their index.html file inside <head>:

<script type="text/javascript">
    OBJJ_MAIN_FILE = "main.j";
    OBJJ_INCLUDE_PATHS = ["/Frameworks","Frameworks"];
</script>

The second path, without the "/" indicates to also search in the application bundle's "Frameworks" directory if a framework can't be found under "/Frameworks".

In the CommonJS environment, for example the Jakefile, the search paths can be accessed via Javascript or the command line environment:

var ENV = require("system").env;
ENV["OBJJ_SEARCH_PATHS"] = "../../Frameworks:Frameworks";
bash$ OBJJ_SEARCH_PATHS="../../Frameworks:Frameworks" jake install

Loading a Resource

Say you have an Info.plist as such:

<?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>CPBundleIdentifier</key>
	<string>net.example.DesignerApp</string>
	<key>CPBundleName</key>
	<string>Designer App</string>
</dict>
</plist>

Inside the following directory structure:

 - /Info.plist
 - /Resources
 | - /Images
 | | - icon.png
 |

Loading a bundle is a simple matter of:

        var bundle = [CPBundle bundleWithIdentifier:"net.example.DesignerApp"];
        var file = [bundle pathForResource:@"Images/icon.png"];
        var image = [[CPImage alloc] initWithContentsOfFile:file];

Best Practices for Frameworks