Swift: workaround for closure as protocol

Swift Language

In Java, it was common to implement callbacks as anonymous classes. It was nice to be able to define the callback right where you define the button.

Here is a simple JButton ActionListener example:

JButton button = new JButton("Press me");
button.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    System.out.println("You clicked the button");

Wouldn’t it be cool to do the same thing in Swift?

Let’s create a Swift protocol. Just a single method that takes an array of Strings.

protocol SimpleProtocol {
  func didReceiveResults(s: [String]) -> Void

Now a method in that uses the protocol.

func frob(s:String, delegate: SimpleProtocol) {
  delegate.didReceiveResults(["foo", "bar"])

Straightforward stuff so far.

Now let’s try calling our method using a trailing closure for the delegate.

frob("hi") { (a:[String]) in

Blammo. Can’t do it.
How about leaving out the params like this?

frob("hi") {
   // use $0 for the params

Nope. I am disappoint. I wish I could do either of these. So what can we do?

I guess we need a named class the implements the protocol.

class NonAnon:SimpleProtocol {
    func didReceiveResults(s: [String]) -> Void {
        for str in s {


 var resp:NonAnon = NonAnon()
self.frob("handler", delegate:resp)
// or typed to the protocol
 var handler:SimpleProtocol = resp
self.frob("handler", delegate:handler)

Yeah, ok. What if you want to call a method from the calling class?
One way is to pass in the calling class (in this case that is ViewController which has a method named blob()).

class NonAnonDelegate:SimpleProtocol {
    var vc:ViewController?
    init(c:ViewController) {
        self.vc = c
    func didReceiveResults(s: [String]) -> Void {

In a Java anonymous inner class you have access to the methods and variables of the outer class. Will that work?

class ViewController {
blah blah
func blob() {}
 class NonAnonDelegate:SimpleProtocol {
    var vc:ViewController?
    init(c:ViewController) {
        self.vc = c
    func didReceiveResults(s: [String]) -> Void {
        // ok
        // blows up XCode

Nope. The Swift compiler barfs on its shoes and the XCode editor goes into convulsions.
So, yes, it’s ugly. I’ll be using the NonAnonDelegate:SimpleProtocol version. Swift nested classes don’t have access to the outer class, but you still are able to define them near where they are needed.

Unless you know a better way. Please let me know.

Download the project from this Github repository.

Posted in Swift | Tagged , , | Leave a comment

UIActivityViewController in Swift

Swift Language

Just a simple example of using the UIActivityViewController in Swift.

You need to be logged into Facebook and Twitter for the options to share to them shows up. You can do this in the simulator by going to the settings app. If you do not log in, then you will just see Mail and Copy as options.

Set up

All that is needed is to instantiate UIActivityViewController with the item[s] to share, then present it. Here is a simple example:

let someText:String = textView.text
let google:NSURL = NSURL(string:"http://google.com/")
// let's add a String and an NSURL
let activityViewController = UIActivityViewController(
            activityItems: [someText, google],
            applicationActivities: nil)
   animated: true, 
   completion: nil)

You can specify that there are sharing options that you do not want like this. This is all of them, so in real life you wouldn’t want to exclude them all.

activityViewController.excludedActivityTypes =  [

If you want to do something upon completion, install a handler like this:

activityViewController.completionHandler = {(activityType, completed:Bool) in
            if !completed {
            if activityType == UIActivityTypePostToTwitter {
            if activityType == UIActivityTypeMail {

Download the project from this Github repository.

Posted in Swift | Tagged , , | Leave a comment

JavaScript/AngularJS adventures of a Java guy Part 3 – Grunt


In Part 2, I talked about managing dependencies with Bower. The next step is to see how to execute build tasks.

For example, when you build with Maven, one of the default behaviors is to copy the contents of src/resources to your build directory (target). We can do things like that with a tool named Grunt.


There are currently two task tools for JavaScript. (I haven’t looked in the past 2 minutes, but there may be more now.) Grunt is the one that is most in use right now. Grunt uses JSON to configure the tasks. Gulp is a newer tool that uses JavaScript for configuration. Gulp benefits from hindsight and tries to avoid some of Grunt’s inconveniences. Personally, I don’t think you can go wrong using either tool.


If you’ve followed along with the previous blog posts, you already have npm installed. Grunt is installed globally (not per project) via npm with this incantation:

npm install -g grunt-cli

This “command line interface (cli)” puts the command “grunt” on your path, so you do not have to type “npm grunt” or even worse “./node_modules/.bin/grunt”.

The minimal project

You need two files in your project: Grunt’s Gruntfile.js which describes the tasks you wish to execute, and npm’s package.json, which specifies general project information along with dependencies.

You can create a package.json file by hand, or by invoking npm init and answering the prompts.
Here is a simple “by hand” package.json:

    "name": "grunt01",
    "version": "0.1.0"

To use Grunt, you will need to add it to package.json as a development dependency, and then install it. This can be accomplished with the following command.

npm install grunt --save-dev

List the directory node_modules, and you’ll see grunt there. (You may be a later version, of course).

Now take a look at package.json to see the added dependency.

    "name": "grunt01",
    "version": "0.1.0",
    "devDependencies": {
        "grunt": "~0.4.4"

The gruntfile can be written in JavaScript or CoffeeScript. I’ll just use JavaScript here.

Here is a simple Gruntfile.js – that does nothing! (Actually, it registers a default task that does nothing).

module.exports = function (grunt) {
    grunt.registerTask('default', []);

Each Gruntfile.js will have this format. You can run this by typing:

grunt --verbose

Adding Grunt Tasks

Grunt tasks are defined by plugins. There is a registry of Grunt plugins.

Let’s start with the concat task.
Install the task.

npm install grunt-contrib-concat --save-dev

This updates node_modules (go ahead and list it), and modifies package.json:

  "name": "grunt01",
  "version": "0.1.0",
  "devDependencies": {
    "grunt": "^0.4.4",
    "grunt-contrib-concat": "^0.4.0"

This is what you will do with each Grunt plugin: npm install pluginname –save-dev

Now in Gruntfile.js, you will have to add this task.
That is not enough. We need to tell the task what files it should concatenate, and where it should put the output. You configure all tasks in grunt by invoking grunt.initConfig(configObject); The configObject contains the configuration for each task you’re going to use. Here is an example for concat.

module.exports = function (grunt) {
    concat: {
      dist: {
        src: ['a.js', 'b.js'],
        dest: 'built.js'
  grunt.registerTask('default', ['concat']);

In grunt.initConfig, I’m defining a target named dist in the concat task. You can specify multiple targets. At a minimum you need to specify the source files and the destination as shown. These file can be – and probably are – in subdirectories such as src/a.js and distribution/built.js. You can also specify file globbing of course.

Because I defined the “default” task as containing ‘concat’, you can run this by simply typing grunt. You will have multiple tasks in a real build, so to execute a specific task you can type the name: grunt concat.

Here is the concat task with two targets: dist and bar. Note that they write to the same destination file. When you run grunt concat, all the targets are executed. That means in this case that the output of dist will be overwritten by the output of bar. So, be careful here; I wouldn’t write targets like this in a real project. If you want to run just one target, type grunt concat:dist or grunt concat:bar.

module.exports = function (grunt) {
    concat: {
      dist: {
        src: ['a.js', 'b.js'],
        dest: 'built.js'
      bar: {
        src: ['b.js', 'a.js'],
        dest: 'built.js'
  grunt.registerTask('default', ['concat']);

Copy task

To copy files, install the copy task, then load it into your gruntfile as you did with the concat task using loadNpmTasks.

npm install grunt-contrib-copy --save-dev

And the Gruntfile with the two tasks loaded and configured.

module.exports = function (grunt) {
    concat: {
      dist: {
        src: ['a.js', 'b.js'],
        dest: 'built.js'
      bar: {
        src: ['b.js', 'a.js'],
        dest: 'built.js'
    copy: {
      main: {
        src: 'src/*',
        dest: 'dest/'
  grunt.registerTask('default', ['concat']);

Live reloading

You know how you change your html, css, or js files in your editor, then switch to your browser and hit reload to see the new wonders you have just wrought?
Wouldn’t it be nice to have your browser reload automatically? That’s what the watch task is for.
(The livereload task has been deprecated in favor of the watch task).

Do the usual installation chacha:

npm install grunt-contrib-watch --save-dev

Then load it into your gruntfile:


Install the LiveReload Chrome Extension. Configure the extension to “Allow access to file URLs”.


You can get a bit of help from
which reads “templates” that you install into ~/.grunt-init. They are sort of like Maven archetypes.
First install grunt-init

npm install -g grunt-init

Now install a template. Here is one that just sets up the gruntfile.

git clone https://github.com/gruntjs/grunt-init-gruntfile.git ~/.grunt-init/gruntfile

then use it

grunt-init gruntfile
Posted in Javascript, Tooling | Tagged , | Leave a comment

Swift REPL

Swift Language

Back in the early 80s I learned the power of LISP’s REPL (Read-Eval-Print-Loop). (As a music major, mind you). Clojure gave us a REPL for the Java JVM. Kids today use Python’s REPL. Swift playgrounds are peachy, but are a bit buggy right now.

Set up

First, you need to say you want to use XCode 6 beta’s Developer Toolchain. Here is the incantation.

sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer/

(or Xcode6-Beta2.app if you have that installed now)

And to go back to XCode 5:

sudo xcode-select -s /Applications/Xcode.app/Contents/Developer/

You might want to define aliases for those in your shell’s .rc file. You could also just set your shell’s DEVELOPER_DIR environment variable to point to it too.

DEVELOPER_DIR=/Applications/Xcode6-Beta.app/Contents/Developer/ xcrun swift

Using the REPL

Find out which SDKs are available:

xcodebuild -showsdks

Then run with one of the listed SDKs.

xcrun --sdk iphoneos8.0 swift


xcrun --sdk macosx10.10 swift

Read the man page xcrun(1) for more info.

man 1 xcrun

Once inside the REPL, you can type :help, or :quit to exit. Control-D works for me too to exit.

xcrun --sdk iphoneos8.0 swift
Welcome to Swift!  Type :help for assistance.
  1> var a = 2
a: Int = 2
  2> a + a
$R1: Int = 4
  3> func foo() -> String {return "hello"}
  4> foo()
$R2: String = "hello"
  5> var re = foo()
re: String = "hello"
  6> re
$R3: String = "hello"
  7> println(re)

Use Swift in a shell script? Chris Lattner tweeted this.

Here’s a simple script.

#!/usr/bin/env xcrun swift -i
println("Hello World!")
Posted in Swift | Tagged , , , | Leave a comment

Swift and Core Audio

Swift Language

Like many of you, I’ve been knee deep into Swift this week. Once you get beyond the hello world things and try something a bit more complicated, you start to learn the language. So, why not go off the deep end and try to work with what is essentially a C library: Core Audio? Turns out that’s a good way to get the types grokked.

First, don’t try to use core audio in a Swift playground. I wasted a day trying to do this. It doesn’t work yet. So, create a project.

I couldn’t do something as simple as this in the playground:

var processingGraph:AUGraph = AUGraph()

So, ok I put that in a Swift class as an instance variable and created it in the init function.

Most core audio functions return a status code which is defined as OSStatus. You need to the type on the var.

var status : OSStatus = 0
status = NewAUGraph(&processingGraph)

Or, if you want, you can cast noErr like this.

var status : OSStatus = OSStatus(noErr)

Here’s an adventure with Boolean.

The function AUGraphIsInitialized is defined like this:

func AUGraphIsInitialized(inGraph: AUGraph, outIsInitialized: CMutablePointer) > OSStatus

So, you call it like this:

var status : OSStatus = OSStatus(noErr)
var outIsInitialized:Boolean = 0
status = AUGraphIsInitialized(self.processingGraph, &outIsInitialized)

That works. But how do you check it?

Boolean is defined as an CUnsignedChar (in MacTypes.h)

So, you cannot do this:

if outIsInitialized {
    // whatever

And you cannot cast it (could not find an overload…)

var b:Bool = Bool(outIsInitialized)

or with Swift’s “as”

var b:Bool = outIsInitialized as Bool

I’m clearly overthinking this. Because this is all that is needed. D’oh!

var outIsInitialized:Boolean = 0
status = AUGraphIsInitialized(self.processingGraph, &outIsInitialized)
if outIsInitialized == 0 {
     status = AUGraphInitialize(self.processingGraph)

Another problem I had was using constants such as kAudioUnitSubType_Sampler while trying to create an AudioComponentDescription. The trick was to simply cast to OSType.

var cd:AudioComponentDescription = AudioComponentDescription(componentType: OSType(kAudioUnitType_MusicDevice),componentSubType: OSType(kAudioUnitSubType_Sampler),componentManufacturer: OSType(kAudioUnitManufacturer_Apple),componentFlags: 0,componentFlagsMask: 0)
        status = AUGraphAddNode(self.processingGraph, &cd, &samplerNode)

Here is my Github repository for this project. It’s a simple 3 button iPhone app that plays sine tones (based on the button’s tag value).

Posted in Swift | Tagged , | 7 Responses

Swift language video download problem

Swift Language

There are quite a few WWDC videos covering Swift. It would be nice if I could view them on my Apple TV 3 with the Apple Events app. Unfortunately, the only thing available there are the WWDC keynotes. Not sure I can take more than 5 minutes of hearing “beautiful” and “gorgeous”.

So, I wanted to download them and then add them to my iMac’s iTunes library. Then I could watch them on the ATV3. The problem is, they kept timing out. Then I tried to view them in the browser (Chrome). The error message suggested that I use Safari. I did, and they downloaded just fine. That’s not really a nice trick they’re pulling there, but now you know the solution to the download problem.

Posted in Swift | Tagged , , | Leave a comment

Apple’s new Swift language

Swift Language

Apple has introduced a new programming language at WWDC14. The Swift Language has been years in the making. It combines the features of many other languages, including scripting languages. It has the potential to replace Objective-C as the primary development language on Apple devices.

I first used Objective-C back in the late 80s when I was writing my dissertation on a NeXT. I was delighted when Apple bought NeXT in 1996 and adopted their software. By that time, I was on to Java, but this kept me in the Objective-C camp. Over those years, I saw many improvements to Objective-C. Unfortunately, along with the improvements, there were many downright ugly contortions that the language needed to make in order to accomodate new features. Personally, I think OC jumped the shark with their closure (aka blocks) syntax. Even Java 8 did a better job with closures. So, the time had come to bring Apple development into the 21st century with a new language that was not burdened with OC’s backward compatibility problems.

To use Swift, you need to be a registered Apple developer in order to download the beta of XCode 6. I had let my account lapse, but this was enough for me to send another $99 to Apple.

Apple is making a book – The Swift Programming Language – available as a free download on iBooks. This is good. Already there is a flood of truly crappy “tutorials” and even worse screencasts, so we need something “definitive”.

I’m trodding through the book and playing with the language. I don’t think I want to add to the “crappy tutorial” flood, but perhaps it would be useful to post fairly short observations/snippets/tips as I go. If for nothing else, to remind myself of where the traps lie.

So, here we go.

First observation. The name. I’d really rather not have Taylor Swift take bandwidth from any of my synapses. Do a search for Swift and there she is. The good news is that they didn’t name the language “Bieber”.

Posted in Apple, iOS, Objective-C | Tagged , , , | Leave a comment

JavaScript/AngularJS adventures of a Java guy Part 2 – npm and Bower



In Part 1, I talking about project structure and only hinted at development tools. Let’s talk about some dependency tools now. In the next blog post, we’ll cover even more tools.

As a Java developer, I’ve already used Git and Github quite a lot. Not surprisingly, it works well with JavaScript too. If you haven’t used it, now is a good time to start.

Tools for Dependencies

npm and Bower

Do you know how you tell Maven that your project has a dependency, say, spring-core 4.0.3-RELEASE? You put a few lines in your pom.xml, and maven will download the internet for you. Just kidding, it will grab that dependency and all the things that it depends on. So, you don’t have to go to a web site, download a zip/jar, and paste them into your project. Wouldn’t it be nice if JavaScript had that?

It does. But it’s not just one tool like maven. The JavaScript ecosystem is using tools developed for Node.js. Node.js let you write server side JavaScript. I prototyped a few RESTful web services last summer for a client start up last summer using Node.js. It was fun and fast. I just wouldn’t use it in production. Even if your back end is in Java using Spring, you should install it just to get the Node Package Manager, npm (read the FAQ and you’ll see that some snarkasaurus says it doesn’t mean this). Go to the Node.js website, and press the big green install button. On my Macbook, it will live at /usr/local/bin/npm. npm will install binaries along with node modules.

Another package manager is Bower which is installed via npm.

npm install -g bower

The -g flag means install it globally and not just for your current project.

To use bower, you need to have a bower.json config file in your project. You can create one yourself, or let Bower help you create one interactively.

bower init
[gene@rockhopper:][~/Development/javascript/ngproject]$ bower init
[?] name: ngproject
[?] version: 0.0.0
[?] description: a test angularjs project
[?] main file: app.js
[?] what types of modules does this package expose?
[?] keywords: angularjs
[?] authors: Gene De Lisa <gene@rockhoppertech.com>
[?] license: MIT
[?] homepage: http://rockhoppertech.com/blog/
[?] set currently installed components as dependencies? Yes
[?] add commonly ignored files to ignore list? Yes
[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? No
  name: 'ngproject',
  version: '0.0.0',
  authors: [
    'Gene De Lisa <gene@rockhoppertech.com>'
  description: 'a test angularjs project',
  main: 'app.js',
  keywords: [
  license: 'MIT',
  homepage: 'http://rockhoppertech.com/blog/',
  ignore: [
[?] Looks good? Yes
Now you can have Bower grab angular for you. You can check the name with lookup. Then install it with the install parameter. There are two flags for saving this dependency to your bower.json file. --save will add it to dependencies. --save-dev will add it to devDependencies. Will you need this during development only e.g. a testing library, or will you need it at deployment too?

$ bower lookup angularjs
$ angularjs git://github.com/angular/angularjs.org.git
$ bower install angular --save

Inspect bower.json and you will see this:

"dependencies": {
    "angular": "~1.2.16"

Notice that when you did the lookup that bower let you know what repository you will be getting the code from. This is important because anyone can publish code that bower can grab. See that last question from the init action that helped you from accidentally publishing to the bower repository? So OK, lesson learned. We should know where the code is coming from.

You’ve probably noticed that the code is saved in bower_components/ You can change this by creating a .bowerrc file in your home directory or current project. Here is an example that saves it in a maven friendly location:

    "directory": "src/main/webapp/app/bower_components"

Let’s add some more, this time with a testing library.

bower install sass-bootstrap --save
bower install angular-mocks --save-dev

Here is the bower.json excerpt now. (Of course, when you run this, the versions might be newer).

"devDependencies": {
    "angular-mocks": "~1.2.16"
  "dependencies": {
    "angular": "~1.2.16",
    "sass-bootstrap": "~3.0.2"

In your HTML, you reference the downloaded files like this.

<link rel="stylesheet" href="bower_components/sass-bootstrap/dist/css/bootstrap.css" />

<script src="bower_components/angular/angular.js">


You know those maven plugins that will copy files to different locations, or create a buildnumber, or that maven plugin you wrote yourself that did that thing? In the next blog post I’ll talk about a tool that will do this grunt work for you.

Posted in AngularJS, Javascript, Tooling | Tagged , | 2 Responses

JavaScript/AngularJS adventures of a Java guy Part 1

In Part 0, I wrote an introduction to this series, and decided upon an IDE; WebStorm. The IDE does not have an opinion on the project layout for a single page JavaScript/AngularJS app. That’s actually good. But we need to think about it ourselves and come up with something that is scalable, readable, and maintainable.

AngularJS/JavaScript Project Structure

One of the nice things about Maven, is that it encourages a standard project directory structure. You can open the project in Eclipse, or NetBeans, or Intellij, or even Emacs, and you’ll be able to work with it. There doesn’t seem to be a direct analog in the AngularJS world. But there are indeed various approaches out there. Since you’re probably learning AngularJS as you go, you’ve probably looked at their tutorial. If not, go ahead and take a look. It’s even fun. The Github repository for the tutorial’s phonecat app is based on another Github project named angular-seed. Hey, if they’re using it, it must be good, right? So, I looked at that first. Their directory layout; take a look. Here is a simplified version:


Some observations: This layout separates your main code (SUT) from your test code just like Maven. Is that a problem? It’s not a problem for me in the Java world. My IDE can be set up to jump between the main code and the test code with a key binding. Would there be an advantage to putting the test code in the same folder as the SUT? Unless you stick to a naming convention, e.g. ProductTest.js, you will have a hard time at deployment telling your deployment tool what is deployable code and what is test code. I can go either way with this issue. There are 2 controllers in the tutorial and they both live in app/js/controllers.js. For a simple tutorial this is not bad. For a large scale app, that could be a problem. The same goes for other files such as services.js. So here is an important question.

Do you want to organize your code by feature or by type (i.e. controllers, service, etc.)?

I’ll talk about JavaScript tools soon, but as a preview, there is a build tool that can concatenate your JavaScript (and minimize it etc.) into a single file for performance, so don’t let that be part of this decision.

I’m learning towards putting all the User code in a folder, the Product code in another folder etc. Maybe someone else agrees, so I asked The Google. Well, that was kicking a hornet’s nest. But I did find an article by Cliff Meyers that I find valuable on this topic. Cliff uses a sock drawer analogy. You can throw all your socks into a drawer, and maybe you can find the ones you are looking for. But ask someone else to find them, and good luck.

I also found a Google document by someone at Google that proposes a new layout using the phonecat app as an example. That’s an interesting read also.

OK, I’m sold. I’m going with organization by feature rather than by type.


Wait a minute. What are those JSON files in the angular seed project: bower.json and package.json? What is .jshintrc? Or travis.yml? In other projects I’ve seen GruntFile.js. Looks like there are some JavaScript tools to learn in the next blog post.


Posted in AngularJS, Javascript | Tagged , , | 1 Response

JavaScript/AngularJS adventures of a Java guy Part 0


I’ve been a developer since 1982. I’ve learned and used dozens of technologies in that time. I followed the general path of C (1982) to C++ (1987) to Objective-C (1988) to Java (1995) with many side languages (DSP asm, scripting languages etc). I pretty much abandoned C++ when I saw a demo of Java in 1995 at AT&T and have been doing it ever since. Most of Java, from Applets in the beginning, to the back end with various application servers (WAS, Weblogic, etc), to front end Swing and later JavaFX.

In the 90s I looked at LiveScript – which was renamed JavaScript when Java became the It Language du jour. I thought it was horrible. Times have changed. A very large number of developers have taken the “good parts” of JavaScript and built libraries (e.g. jQuery), frameworks (e.g. AngularJS), and tools (e.g. Grunt) that are driving the current web. You still need to watch out for stepping in the cat poop, because the litter box is still there. Luckily, there are well documented ways to write clean JavaScript. For example, the language still lets you pollute the global name space, there are things you can do yourself (e.g. IIFEs), or use frameworks such as AngularJS which provide modules.

There are many JavaScript frameworks out there. Backbone.js was one that I used last year in a Hadoop front end. It’s fairly low level, which is what I needed with searching big data rather than the standard model classes. There are others that I looked at. Right now, I’ve started an AngularJS project for a client. I like to live in “the other guy’s shoes” once in a while. As a primarily back end Java developer, it pays to learn the other technologies so you can become better at your primary technologies.


I usually create entries into a locally installed mediawiki to remind myself what I found out about a technology. My guess is that there might be other Java developers out there who are interesting in AngularJS or other new JavaScript framework and are overwhelmed by the number of new things that have popped up in this space. So, this series will be breadcrumbs for me to remember what I did, but perhaps you will find something in here that is of use to you.


So, where to start? Well, there is the Emacs of course, which does everything. But I wanted a bit of help along the way.  Besides, I like writing LISP, so the end result would be me writing that instead of getting to work on the JavaScript.

XKCD knows

I looked at the Eclipse options for New Project. Then I searched the plugins. That made me go to IntelliJ to do the same. Yeah, you can cut down a tree with a butter knife, but it’s a lot of work. So, I’ve settled on another JetBrains product, WebStorm. If you’ve used IntelliJ, you’ll feel at home. The Grunt (more on that later) integration is what sold me. It seems that JavaScript developers like Sublime Text, but I wanted something more familiar to a Java developer.

Cool. Let’s go to New Project in WebStorm! The options are various HTML projects or Node projects. No AngularJS love there. Yes there is an AngularJS plugin, but it doesn’t have an opinion on project structure. It does have nice live templates, so I installed it. I could do an empty project. Well, it looks like I need to create the project layout on my own and then import it. I have a CPA do my taxes because he knows a lot more about the IRS then I do. So, let’s see what the experts say about project structure.

Let’s talk about that issue in Part 1.


Posted in AngularJS | Leave a comment