Archives For January 2015

Screen Shot 2015-01-25 at 10.51.49 PM

#!/usr/bin/env swift

import WebKit

let application = NSApplication.sharedApplication()
application.setActivationPolicy(NSApplicationActivationPolicy.Regular)
let window = NSWindow(contentRect: NSMakeRect(0, 0, 800, 600), styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask, backing: .Buffered, defer: false)
window.center()
window.title = "Minimal Swift WebKit Browser"
window.makeKeyAndOrderFront(window)

class WindowDelegate: NSObject, NSWindowDelegate {
    func windowWillClose(notification: NSNotification?) {
        NSApplication.sharedApplication().terminate(0)
    }
}

let windowDelegate = WindowDelegate()
window.delegate = windowDelegate

class ApplicationDelegate: NSObject, NSApplicationDelegate {
    var _window: NSWindow
    init(window: NSWindow) {
        self._window = window
    }
    func applicationDidFinishLaunching(notification: NSNotification?) {
        let webView = WebView(frame: self._window.contentView.frame)
        self._window.contentView.addSubview(webView)
        var url = NSURL(string: "https://www.google.com")
        webView.mainFrame.loadRequest(NSURLRequest(URL: url!))
    }
}

let applicationDelegate = ApplicationDelegate(window: window)
application.delegate = applicationDelegate
application.activateIgnoringOtherApps(true)
application.run()

This is little more than a copy of the code published on practicalswift, “How to write a minimal WebKit browser in 30 lines of Swift.” I made several trivial changes to the code so that it would properly execute in my Xcode environment.

  1. The shebang was cleaned up.
  2. Line 30 used to be one line (see original code here). In order to get it to work I removed the NSURL instantiation that was chained in webView.mainFrame.loadRequest and created the separate variable url right above it on line 29. I then added a “!” right after url. I’d tried to add the “!” while it was still one line, but Swift wasn’t having any of that, so I split them apart. I’m certainly no Swift expert, but long years of battling other oddball errors in other languages has trained me to begin to break apart compound function calls made with instantiations into more, but simpler, lines, if for no other reason than to isolate the problem as an aid to debugging. In this instance, just breaking them apart made the whole thing begin to work (see application capture above).

There’s a lot for me to learn in these 30-something lines, and a lot of functionality to add. This surely won’t compete with the likes of Safari or Chrome. But it does show how complete WebKit is. I can go to any website via the Google search box and see it properly rendered. Quit an nifty feature when you don’t have to write your own rendering engine.

Also, this particular block of Swift scripting is bound to the Mac and OS X runtime environment. Ain’t no way this is going to be portable to anything else other than a Mac running OS X.

I’ve spent parts of Saturday and today going over Apple’s “The Swift Programming Language,” looking to get a toe-hold on how to use Swift. Using vi and some of the examples in the Swift book, I hacked together a few scripts that illustrate some of the features of the language. I also went out on the web to find a few features I couldn’t find reading the book.

This first example, reading command line arguments, came from the web. I couldn’t find an example in the book (but watch me find it later). Every language, scripting or otherwise, provides a framework for reading any arbitrary number of arguments passed to the application when the application is invoked. What follows is at least one way to read command line arguments in Swift, and what the output looks like when it executes

#!/usr/bin/env swift

println("Process.arguments gave args:")

for arg in Process.arguments {
    println(arg)
}

Screen Shot 2015-01-25 at 6.17.55 PM

Comments about the code and the output:

  • The shebang line (line 1) is standard for all scripting languages. I’ve seen more elaborate shebang lines on other postings, but this one, which is “classic” Unix/Linux, works just fine on Yosemite OS X 10.10.1.
  • The Process class (line 5) is unique to Swift.
  • In the output, the first argument (usually considered the zeroeth argument in other languages like C and C++) is the name of the script/program that was invoked. This will include the full path.
  • Process.arguments can handle any arbitrary number of command line arguments.

I first came across this on the practicalswift.com website, and it had a more elaborate shebang line (thus the comments above). But the site’s version was written back in June 2014, during the beta period. It’s a bit sad that nothing new has been written on the site since June 2014.

This second example shows a big of class declaration and class inheritance within Swift. This is all written in the same script; I’ve yet to learn how to create separate script/compilation units to be included by a main executable function.

#!/usr/bin/env swift

class NamedShape {
    var numberOfSides: Int = 0
    var name: String

    init(name: String, numberOfSides: Int) {
        self.name = name
        self.numberOfSides = numberOfSides
    }

    func simpleDescription() -> String {
        return "A \(name) with \(numberOfSides) sides."
    }
}

var nshape = NamedShape(name: "square", numberOfSides: 4)
println(nshape.simpleDescription())

class Square: NamedShape {
    var sideLength: Double

    init(sideLength: Double, name: String) {
        self.sideLength = sideLength
        super.init(name: name, numberOfSides: 4)
    }

    func area() -> Double {
        return sideLength * sideLength
    }

    override func simpleDescription() -> String {
        return "A square with sides of length \(sideLength)."
    }
}

let square = Square(sideLength: 5.2, name: "my test square")
println(square.simpleDescription())
println("The area is \(square.area()).")

Screen Shot 2015-01-25 at 7.49.53 PM

Comments about the code and the output:

  • This is modified code from pages 24 and 26 of “The Swift Programming Language.” I added NamedShape’s init() function and changed NamedSquare’s init() accordingly. I’m a firm believer in constructor initialization, no matter what.
  • Both classes’ have public members. Not shown here, I wrote code that accessed both classes attributes. I don’t know how to make class attributes non-public.
  • Both classes have the equivalent of constructors (the init function).
  • When instantiated you must declare the argument type by name (see lines 17, 25, and 37). I was surprised by this at first, but whatever, it’s one of the rules, and when in Swiftville, you do what the Swifties do.
  • Class Square shows inheritance from NamedShape (line 20) as well as over ┬áriding Square’s simpleDescription() at line 32.

There are other scripts I’ve written, such as a largish one investigating the use of dictionaries (associative arrays). The syntax is clean and easy to read, allowing for some fairly sophisticated data structures.

There’s a lot more I need to learn, the most important being how to easily set up file I/O. Other facts I’d like to learn about Swift is how to spawn applications, script interop communications, some possible threading, and how to link to other Swift scripts/classes. But it’s late and the Labs are staring at me for their walks. Time to sign off.

adding go to yosemite

January 24, 2015

Screen Shot 2015-01-24 at 5.13.00 PMI’ve also added the Go programming language to my collection of tools on the Mac Mini. Installation of a basic Go environment, which is just the language and all its support tools, is dead simple. Just head over to download Go and follow the instructions there for installing Go.Screen Shot 2015-01-24 at 8.47.27 AMScreen Shot 2015-01-24 at 8.47.37 AMScreen Shot 2015-01-24 at 8.47.47 AMScreen Shot 2015-01-24 at 8.48.12 AMScreen Shot 2015-01-24 at 8.48.28 AMInstallation is as simple as the five steps shown above. Just know that when you click to install that you’ll have to type your login account password to fully install. Also note that the default location for go is under /usr/local (/usr/local/go). For Go to be fully visible at the command prompt you’ll need to add /usr/local/go/bin to $PATH. You’ll also want to define your $GOPATH environmental variable in your shell. That’s the root directory down which you’ll keep your sources as well as where other Go utilities will be stored.

For development tools I’m staying away from vi (Vim) as it’s an older version (7.3) that what you’ll find in the Linux distributions. I don’t feel the need to hassle with installing the later version. I tried IntelliJ’s IDEA with a Go plugin, but that didn’t work out too well. Right now I’m giving Sublime Text 2.0.2 a spin. Sublime Text is supposed to have a Go plugin to help with Go coding, but I don’t have it installed right now. Sublime Text is a paid application (US$70), and I’m about ready to spend the cash. Sometimes all I need is a good editor, and Sublime Text looks to be that good editor, at least on Yosemite.

I’ve been adding capabilities to my Mac Mini since powering it up the middle of this past week. After performing all the necessary OS updates, I installed Xcode 6.2.2. Xcode comes with a series of tools, one of which will be shown later in this article.Screen Shot 2015-01-23 at 8.01.20 PMThe installation of Xcode is important to the clean installation of two Java-based tools later on in this post, Android Studio and IntelliJ IDE 14.

System Specifics

  • Mac mini Server (Late 2012)
  • Processor 2.3 GHz Intel Core i7
  • Memory 4GB
  • Two, 2 TB HDD

Preconditions

  • Mac OS X Yosemite Version 10.10.1 (update free from App Store)
  • Mac Server 4.0.3 (update free from App Store)
  • Xcode 6.2.2 (installed free from App Store)
  • Apple’s Java (Java 6) is not installed

Software Installed

Installation of Latest Java

Download the latest version from the Oracle Technology Network. You should be able to find the latest under New Downloads in the upper right corner of the web page. For this example I’m installing the 64-bit Java 8 update 31 (jdk-8u31-macosx-x64.dmg). After download start the installation by clicking or double clicking, depending on where you find the dmg file, to launch the installer.
Screen Shot 2015-01-23 at 9.38.43 PMSimply follow the directions and double click the icon (the transparent yellow square in the open box) to start the installation of Java. The first screen presented to you will be an introduction. There’s very little there, so click through.
Screen Shot 2015-01-23 at 9.39.11 PMScreen Shot 2015-01-23 at 9.39.29 PM
Screen Shot 2015-01-23 at 9.39.42 PMWhen you click to install, it will ask you for your login account password, so type it in to continue.
Screen Shot 2015-01-23 at 9.40.16 PMThe installation is a simple progress bar. When it reaches the end all the software will be installed on your system. The last dialog is the summary with a close button. Once installed you can add a JAVA_HOME variable if you like with an “export JAVA_HOME=$(/usr/libexec/java_home)” in your .profile, but I don’t believe it’s necessary. You certainly don’t need to edit $PATH, as there are links to java, javac, and jar in /usr/bin.

You can, if you also wish, download from the Oracle site the Java 8 demo examples. They’re all in a zip file. Once unzipped you’ll find all the demo and example applications that were once bundled with Java. Also included in this collection are example JavaFX applications which are well work looking at. JavaFX is so much better for UI development than the older and much worse looking Swing.

Android Studio and IntelliJ 14 IDE Pre-Installation Preparation

The installation for both is very similar, which they should be, considering that Android Studio is based on IntelliJ IDE. For both download their respective dmg files, double click them to open them in Finder, then drag them into the Application folder. Once in the Application folder you need to make a change to their respective plist files. Find the applications in the Application folder, and right click on them. Select Show Package Contents from the menu.

Screen Shot 2015-01-23 at 10.57.57 PM

When the Contents folder appears, click through to the contents where info.plist is located. Double clock on info.plist.Screen Shot 2015-01-23 at 11.05.26 PMScreen Shot 2015-01-23 at 11.07.52 PMWhen Xcode opens up the plist (you did install Xcode, did you not?) open up JVMOptions, then select JVMVersion and clock on the string to edit it. The string, as shipped, is “1.6*”. Note the asterisk. If the string is left this way you will be prompted by both applications to install Apple’s much older Java 6. Changing the string to “1.6+”, with a “+” replacing “*”, tells the application to use the version of Java installed on the machine. Save this file and then double click the application to finish installation configuration.

For Android Studio I chose to download the separate Android SDK and install it (for my purposes) under $HOME/Java. The SDK comes as a zip file, so unzipping it under $HOME/Java produces $HOME/Java/android-sdk-macosx.

Installing Android Studio 1.0.1

With everything properly prepared, we’re ready to finish Android Studio’s installation (and IntelliJ if you’re so inclined). I’m only going to show Android Studio because it’s the more complex due to the Android SDK, but other than that they’re nearly identical.Screen Shot 2015-01-22 at 10.24.08 PMScreen Shot 2015-01-22 at 10.24.46 PMScreen Shot 2015-01-22 at 10.29.23 PMScreen Shot 2015-01-22 at 10.29.36 PMScreen Shot 2015-01-22 at 10.30.12 PMMake sure you open both top-level menu items and agree to their licensing.Screen Shot 2015-01-22 at 10.40.26 PMYou’ll go through several minutes (or longer) of watching Android Studio download any Android SDK files it thinks it needs. This is just a typical capture of this process, and towards the end.Screen Shot 2015-01-22 at 10.42.29 PMWe’re finally done!Screen Shot 2015-01-22 at 10.42.47 PMAnd ready to start development. The installation of IntelliJ is essentially the same but shorter. I’ll spare you the details.

Notes to The Gentle Reader

  • If you think this is overly complicated, it’s not. It’s no worse (and some might argue even simpler) than installation on Linux and Windows. The only obscure piece I had to go looking for was the plist adjustment. This information is missing on both Google’s Android Studio and Jetbrain’s IntelliJ IDEA websites. You’re welcome.
  • This is an initial install. I have no idea what will happen when I update Java, and I will update Java as each version is released. You update for the bug fixes. Failure to update any software is an invitation to grief, which is so easy to avoid. If any problems crop up when an update occurs then I’ll post an update about it here.

There comes a time in any programmer’s life when they write logging functions. Sometimes it’s little more than std::cout <<, or sometimes it’s a full bore implementation along the lines of Boost.Log. Sometimes you want more than a print statement without the overhead of a Boost.Log implementation. What I’m about to list is that kind of logging function. It comes in just two files, a header and implementation, with a simple test file to try it out.

First, the code listings.

#ifndef IT_LOGGER_H
#define IT_LOGGER_H

#include <iostream>

namespace IT
{
    class Logger {
        public:
        enum Level {
            INFO,   // Everything.
            ERROR,  // An error in the code itself.
            FATAL   // What is caused by a runtime exception.
        };

        // Set Logger to an output stream. Default is std::cout.
        //
        explicit Logger(std::ostream &ostr);

        // Set the stream we want to log to. Default is std::cout.
        //
        static std::ostream &setStream(std::ostream *ostr = 0);

        // Set the reporting level. Default is INFO.
        //
        static void setLevel(const IT::Logger::Level = INFO);

        // The logging function. This is wrapped in the LOG macro below.
        //
        static void log(const Level, const std::string fileName, const int line, const std::string msg);
    };
}

// Generate a logger entry. Macros are evil, but this is a necessary evil.
//
#define LOG(level, string) \
    IT::Logger::log((IT::Logger::level), __FILE__, __LINE__, (string));

#endif

it_logger.h

#include <cctype>
#include <cstdlib>
#include <ctime>
#include <fstream>
#include <sstream>
#include <strings.h>

#include "it_logger.h"

// All the code in this anonymous namespace block is executed once and only once
// during application startup. This is a "silent" new for Logging to make sure
// it is properly configured and sane.
//
namespace
{
    // Check to see where logging output will go. Default is standard out.
    //
    // If the user defines the environmental variable ITLOGFILE, then that
    // will be the logging output. The file will be opened relative to the
    // home directory of the user running the application. For example, if
    // the user, using bash, performs 'export ITLOGFILE=foo.txt', then runs
    // the application, all logging will go to $HOME/foo.txt.
    //
    // Failures to open are silent, and the default on failure is standard out.
    //
    std::ostream* checkLoggingOutput(void) {
        std::ostream *logout = &std::cout;
        std::stringstream logfilename;
        char *home = getenv("HOME");
        char *env = getenv("ITLOGFILE");

        if ((home != NULL) && (env != NULL)) {
            logfilename << home << "/" << env;
            std::ofstream *ofs =
                new std::ofstream(
                    logfilename.str().c_str(), std::ofstream::out | std::ofstream::app);

            if (ofs != NULL && ofs->is_open()) {
                logout = ofs;
            }
        }

        return logout;
    }

    std::ostream *outPtr = checkLoggingOutput();

    // Check what logging level to use. Default is IT::Logger::ERROR.
    //
    // If the user defines the environmental variable ITLOGLEVEL, then that
    // will be the logging output. Levels are INFO, ERROR, and FATAL.
    // Logging level is set, using bash as an example,
    // by 'export ITLOGLEVEL=INFO' (if you want INFO level logging or higher).
    // The three levels are case insensitive (info is the same as INFO, etc).
    //
    // Failures due to misspellings are silent. Default level is ERROR.
    //
    const IT::Logger::Level checkLoggingLevel(void) {
        IT::Logger::Level level = IT::Logger::ERROR;
        char *env = getenv("ITLOGLEVEL");

        if (env != NULL) {
            if (strcasecmp(env, "INFO") == 0) {
                level = IT::Logger::INFO;
            }
            else if (strcasecmp(env, "ERROR") == 0) {
                level = IT::Logger::ERROR;
            }
            else if (strcasecmp(env, "FATAL") == 0) {
                level = IT::Logger::FATAL;
            }
        }

        return level;
    }

    IT::Logger::Level levelFilter = checkLoggingLevel();

    std::ostream &setStream(std::ostream *ostr) {
        outPtr = ostr != NULL ? ostr : &std::cout;
        return *ostr;
    }

    static const char* lname[] = {
        "-  - INFO  : ",
        "!!!! ERROR : ",
        "**** FATAL : "
    };

    const char* levelToString(const IT::Logger::Level level) {
        return (level >= 0 && level < sizeof(lname)) ? lname[level] : "UNKNOWN: ";
    }
}
// end anonymous namespace

IT::Logger::Logger(std::ostream &ostr) {
    setStream(&ostr);
}

std::ostream &IT::Logger::setStream(std::ostream *ostr) {
    return setStream(ostr);
}

void IT::Logger::setLevel(const IT::Logger::Level level) {
    ::levelFilter = level;
}

// A LOG macro is wrapped around this specific function call.
// The level is one of the three IT::Logger::Levels defined in the header.
// The filename is the source file name in which this was invoked via the macro.
// The line number is the source file line number, i.e. where you
// would expect to find when you open up the source file in a text editor.
// The message is the explicit message written with the macro.
// Logging is timestamped with the system's current local time.

void IT::Logger::log(
    const Level level,
    const std::string filename,
    const int line,
    const std::string message) {

    if (level >= ::levelFilter) {
        char atime[80];
        time_t rawtime = time(NULL);
        tm *curtime = localtime(&rawtime);
        strftime(atime, sizeof(atime), "%c %Z : ", curtime);
        *outPtr << levelToString(level) << atime;
        *outPtr << filename << " : " << line << " : " << message << std::endl;
    }
}

it_logger.cpp

#include "it_logger.h"

int main(int argv, char *argc[]) {
    std::cout << "Starting..." << std::endl;
    LOG(INFO, "Info logging.");
    LOG(ERROR, "Error logging.");
    LOG(FATAL, "Fatal logging.");

    IT::Logger::setLevel(IT::Logger::ERROR);
    std::cout << std::endl;

    LOG(INFO, "Info logging 2.");
    LOG(ERROR, "Error logging 2.");
    LOG(FATAL, "Fatal logging 2.");

    return 0;
}

logtest.cpp

Usage

Include the header file in the source you want to add logging; see logtest above for examples on how to use it in code. See the notes in the code for details about the environmental variables ITLOGLEVEL and ITLOGFILE for simple tuning and controlling output. Compile and then use. I’ve tested this on RHEL 5 and Ubuntu 14.10, both with g++.