Categories
Computers & Software

The Trials and Tribulations of a Windows Developer

Trouble ahead...
Trouble ahead…

After a very long hiatus, I am back doing software development on a Microsoft Windows machine. I decided to port EP Calipers, an app for making electrocardiographic measurements that is available on Android, iOS and OS X, to Windows. Several users had written to me and asked me to do this. Ever eager to please, I have launched into this project. And it has not been easy.

I am no stranger to Windows development, having developed a Windows database system for tracking and reporting electrophysiology procedures while at the University of Colorado in the 1990s. But it would not be overstating the matter to say that my Windows development “skillz” are rusty at this point. I have been living in the Unixy world of Apple and GNU/Linux for several years now, avoiding Windows other than when I had to, such as when I was required to use the ubiquitous Windows 7 systems running nightmarish EHR software at the various hospitals where I worked. I have not done any programming on Windows machines for many years. Transitioning back to Windows development has been, to put it mildly, difficult.

I have no complaints about Visual Studio. It is free and seems to be a very well-designed IDE, at least as good as, if not better than, Xcode and Android Studio. I like C#, which is like a cross between C and Java. Visual Studio can interface directly with GitHub. Given all this, what’s my problem with developing on Windows?

The problem originates in the command line environment of Windows, an environment that dates back to the beginnings of personal computer with the introduction of MS-DOS back in 1981, a system based on the CP/M disk operating system that dates even further back to the 1970s. Windows, which has made backward compatibility almost a religion, still uses a command line system that was written when disks were floppy and 8 inches in diameter. Of course, Unix is just as old, but Unix has always remained focused on the command line, with an incredible plethora of command line tools, whereas with Windows the command line has remained the unwanted stepchild to its GUI. Worse, the syntax of the Windows command line is incompatible with the Unix command line: backslashes instead of front slashes, drive letters instead of a root-based file system, line endings with CR-LF instead of LF, and so forth. So, in order to ease the pain of transitioning to Windows, I needed to install a Unix environment.

Even though Bash is coming to Windows, for now I downloaded MSYS2 which seems to be the preferred Unix environment for Windows nowadays. Using the pacman package management tool, I downloaded various binary packages that I needed, such as Git and Emacs. I faced the challenge of setting up my Emacs environment on Windows. My .emacs (actually ~/.emacs.d/init.el) startup file that works well on my Mac, loading various Emacs packages and customizations, didn’t do so well on Windows. I updated my .emacs using use-package so that it was easy to disable packages I didn’t want, and so that the full .emacs would load even if packages were missing. With some tweaking and downloading of various packages, I got Emacs up and running on Windows. For some reason the Emacs couldn’t find its own info (help) files, but further tweaking fixed that. With Emacs and Git working, I started a new repository on GitHub and was pretty much ready to start developing.

Except, more issues. Little things that take time to fix and can drive you crazy. An example: I had created some soft links to some files that I share on Dropbox, using the usual Unix ln -s command. The files were created, but weren’t actually linked. Apparently ln is just an alias for cp in MSYS2. There are no warnings about this when you run the command, but a Google search proved this to be correct. Fortunately Windows provides a true linking command mklink, and I was able to create the links I wanted. But all this just served to remind me how the Unix compatibility shells in Windows are just roughly pasted wallpaper over the rotten old MS-DOS walls.

Now I was ready to start developing, but I was faced with a question: what platform(s) to target? It is possible to develop a Windows Universal app, that theoretically can run on anything from a PC to a phone. This sounds ideal, but the devil is in the details. The types of controls available for developing a universal app are more limited than those available for a standard Windows Forms program. For example, the control used to display an image in a universal app (named, oddly enough, Image) is sealed, meaning it can’t be extended. I really wanted something like the PictureBox control available with Windows Forms, but this is not available in the universal API. So I have tentatively decided to develop a more traditional Windows Forms app, able to run on PCs and tablets like Microsoft Surface. The Windows phone may be fading into the sunset anyway, so it doesn’t seem worth it to jump through hoops to target a platform that is teensy-weensy compared to Android and iOS.

I should mention that I did try the bridge that Microsoft has developed to port iOS programs written in objective C over to Windows. Long story short, it didn’t work, as many parts of the iOS API haven’t been fully ported yet. Maybe someday this process will be easier.

I’m sure experienced Windows developers will read this and just chalk it up to my own inexperience as a Windows developer. I would respond that, as someone who is a cross-platform developer, it really is difficult to transition from Unix or BSD-based systems like Apple or GNU/Linux to Windows. I think Microsoft is trying to fix this as evidenced by their recent embrace of open-source code. Visual Studio is an excellent IDE. Nevertheless problems like I’ve describe do exist and will be familiar to anyone who has made the same journey I have. I’d advise anyone like this to keep on plugging away. In the immortal words of Jason Nesmith: Never give up! Never surrender!

Categories
Computers & Software

Porting an iOS Project to the Mac

I just finished porting my electronic calipers mobile iOS app, EP Calipers, to the Mac. In doing so I decided to bite the bullet and change the programming language from the original Objective C (ObjC) to Apple’s new language, Swift. Here are some observations.

The Swift programming language

I’m comfortable now with Swift. Swift is an elegant language with a modern syntax. ObjC is a very weird looking language in comparison. You get used to ObjC, but, after writing Swift for a while, your old ObjC code looks awkward. Comparing the two languages is a little like comparing algebraic notation to reverse polish notation (i.e. like comparing (1 + 3) to (1 3 +)). I’ll just give a few examples of the differences. The chapter “A Swift Tour” in Apple’s The Swift Programming Language is good resource for getting up to speed in Swift quickly.

Here’s how an object variable is declared and initialized in ObjC:

Widget *widget = [[Widget alloc] init];

Note that in ObjC objects are declared as pointers, and both the memory allocation for the object and initialization are explicitly stated. ObjC uses messaging in the style of SmallTalk. The brackets enclose these messages. So in the commonly used object construction idiom shown, the Widget class is sent the allocation message, and then the init message. A pointer to a widget results.

The same declaration in Swift:

var widget = Widget()

With Swift the syntax is much cleaner. The keyword var indicates variable initiation. Pointer syntax is not used. The type of the variable doesn’t have to be given if it can be inferred from the initiation. Swift is not a duck-typed language, like, for example, Ruby. It is strongly statically typed. It’s just that if the compiler can figure out the typing, there’s no need for you to do the typing (Sorry for the puns — couldn’t resist). Note that the constructor is just the class name followed by parentheses. If there are parameters for the constructor, they are indicated with parameter names within the parentheses. Finally, note that no semicolon is needed at the end of the line.

Swift has a lot of other niceties. Simple data types like integer, float and double are full-fledged objects in Swift (Int, Float, Double). Unlike ObjC, where only pointers can be nil, all classes in Swift, even classes like Int, can potentially be equal to nil, if the variable is defined as an Optional with the following syntax:

var count: Int? // count can be equal to nil

In order to use an Optional variable, you need to “unwrap” it, either forcibly with an exclamation point:

let y = count! + 1 // will give runtime error if count == nil

or, more safely:

if let tmp = count { // no runtime error if count == nil
     y = tmp + 1
 }

In that statement, the if statement evaluates to false if count is nil. This if statement also demonstrates more of Swift’s nice features. There are no parentheses around the conditional part of the if statement, and the options following the if statement must be enclosed in braces, even if they are only a single line long. This is the kind of syntax rule that would have prevented the Apple’s gotoFail bug and one wonders if that very bug may have led to incorporation of this rule into Swift.

Because Swift has to coexist with the ObjC API, there are conventions for using ObjC classes in Swift. Some ObjC classes, like NSString, have been converted to Swift classes (String class). Most retain their ObjC names (e.g. NSView) but their constructors and methods are changed to Swift syntax. Many methods are converted to properties. For example:

ObjC

NSView *view = [[NSView alloc] initWithFrame:rect];
 [view setEnabled:true];

Swift

let view = NSView(frame: rect)
 view.enabled = true

Properties are declared as variables inside the class. You can add setters and getters for computed properties. When properties are assigned Swift calls the getting and setting code automatically.

There are other improvements in Swift compared to ObjC, too numerous to mention. For example, no header files: wonderful! Swift is easy to learn, easy to write, and lets you do everything that you could do in ObjC, in a quicker and more legible fashion. Well named language, in my opinion.

Mac Cocoa

The other hurdle I had in porting my app was translating the app’s API. Apple iOS is not the same as Apple Cocoa. Many of the foundational classes, like NSString (just String in Swift) are the same, but the user interface in iOS uses the Cocoa Touch API (UIKit), whereas Cocoa uses a different API. The iOS classes are prefixed with UI (e.g. UIView), whereas the Cocoa classes use the NS prefix (NSView).

The naming and functionality of the classes between to two systems is very similar. Of course Cocoa has to deal with mouse and touchpad events, whereas iOS needs to interpret touches as well as deal with devices that rotate. Nevertheless much of the iOS code could be ported to Cocoa just by switching from the UI classes to their NS equivalents (of course while also switching from ObjC to Swift syntax). As expected, the most difficult part of porting was in the area of user input — converting touch gestures to mouse clicks and mouse movement. It is also important to realize that the origin point of the iOS graphics system is at the upper left corner of the screen, whereas the origin in Mac windows is at the lower left corner of the screen. This fact necessitated reversing the sign of y coordinates in the graphical parts of the app.

Although there’s no doubt the UI is different between the two platforms, there does seem to be some unnecessary duplication of classes. Why is there a NSColor class in Cocoa and a UIColor class in iOS, for example? Perhaps if Apple named the classes the same and just imported different libraries for the two platforms, the same code could compile on the two different platforms. Apple has elected to support different software libraries for computers and mobile devices. Microsoft is going in the other direction, using the same OS for both types of devices. I think Apple could get pretty close to having the same code run on both types of devices, at least on the source code (not the binary) level, with a little more effort put into their APIs. I suspect that at some point in the future the two operating systems will come together, despite Tim Cook’s denials.

IKImageView

I used IKImageView, an Apple-supplied utility class, for the image display in my app. In my app, a transparent view (a subclass of NSView) on which the calipers are drawn is overlaid on top of an ECG image (in a IKImageView). It is necessary for the overlying calipers view to know the zoomfactor of the image of the ECG so that the calibration can be adjusted to match the ECG image. In addition in the iOS version of the app I had to worry about device rotation and adjusting the views afterwards to match the new size of the image. On a Mac, there is no device rotation, but I wanted the user to be able to rotate the image if needed, since sometimes ECG images are upside down or tilted. It’s also nice to have a function to fit the image completely in the app window. But because of the way IKImageView works, it was impossible to implement rotation and zoom to fit window functionality and still have the calipers be drawn correctly to scale. With image rotation, IKImageView resizes the image, but reports no change in image size or image zoom. The same problem occurs with the IKImageView zoomToFit method. I’m not sure what is going on behind the scenes, as IKImageView is an opaque class, but this resizing without a change in the zoom factor would break my app. So zoomToFit was out. I was able to allow image rotation, but only when the calipers are not calibrated. This make sense anyway, since in most circumstances, rotating an image will mess up the calibration (unless you rotate by 360°, which seems like an edge case). Other than these problems with image sizing, the IKImageView class was a good fit for my app. It provides a number of useful if sketchily documented methods for manipulating images that are better than those provided by the standard NSImageView class.

Saving and printing

As mentioned, my app includes two superimposed views, and I had trouble figuring out how to save the resulting composite image. IKImageView can give you the full image, but then it would be necessary to redraw the calipers proportionally to the full image, instead of to the part of the image contained in the app window. I came close to implementing this functionality, but eventually decided it wasn’t worth the effort. Similarly printing is not easy in an NSView based app (as opposed to a document based app), since the First Responder can end up being either view or the enclosing view of the window controller. I wished there was a Cocoa method to save the contents of a view and its subviews. Well there is, sort of: the screencapture system call. It’s not perfect; screencapture includes the window border decoration. But it was the easiest solution to saving the composite image in the app window. The user then has the ability to further edit the image with external programs, or print it via the Preview app.

Sandboxing

Mac apps need to be “sandboxed,” meaning if the app needs access to user files, or the network, or the printer, or other capabilities you have to specifically request these permissions, or, as Apple terms it, entitlements. Since the app needed access to open user image files, I just added that specific permission.

Submission to the App Store

Submitting a Mac app to the App Store is similar to submission of an iOS app — meaning if you are not doing it every day, it can be confusing. The first problem I had was the bundle ID of my app was the same as the bundle ID of the iOS version of the app. Bundle IDs need to be unique across both the Mac and iOS versions of your apps. Then there was the usual Apple app signing process which involves certificates, provisioning profiles, identifies, teams, etc., etc. I did encounter one puzzling glitch which involved a dialog appearing asking to use a key from my keychain, and the dialog then not working when clicking the correct button. I had to manually go into the keychain program to allow access to this key. So, in summary it was the usually overly complicated Apple App Store submission process, but in the end it worked.

And so…

Because the Apple API is so similar between Cocoa and iOS, porting my app to the Mac was easier, even with the language change from ObjC to Swift, than porting between different mobile platforms. I have ported apps between iOS and Android, and it is a tougher process. As for Swift, I’m happy to say goodbye to ObjC. Don’t let the door hit you on your way out!

Categories
Computers & Software

AutoLayout Revisited

My initial experiences with Apple’s iOS AutoLayout were pretty negative. Using Interface Builder’s (IB) ability to generate AutoLayout constraints automatically based on the positioning of views turned out to be frustrating, as it would generate constraints that were incompatible with iOS 7. As iOS 8 has only been out for a few weeks, I definitely want to keep supporting iOS 7 in my app. But Xcode 6 generates these incompatible constraints anyway, even though the deployment target is iOS 7. Furthermore the automatically generated constraints don’t really do what I want, such as keeping views centered on the screen when the screen enlarges from iPhone size to iPad size. So I was forced to go back to the drawing board and really try to understand how AutoLayout works.

My impetus for all this was my desire to upgrade one of my apps from an iPhone only app to a Universal app — optimized for display on both the iPhone and iPad. The app (EP Mobile) has a big storyboard and many different views. By using AutoLayout I hoped to avoid having two different storyboards, one for iPhone and another for iPad, and just use one storyboard for both devices. I decided to check the Use Size Classes option in IB. Supposedly this allows for designing separate layouts in a single storyboard for different size devices. As it turns out, this was not helpful, as apparently this feature only works on pure iOS 8 apps. Moreover, as the compiler seems to generate code for each possible device, building your app after making a change in the storyboard takes much longer than it did before enabling this option. After a while I grew tired of this and decided to turn off Size Classes. However trying to do this resulted in a warning dialog from Xcode that stated a lot of nasty things that might happen (I hate dialogs that use the word “irreversible”) and so I decided to just live with the longer build times.

I watched some YouTube videos on AutoLayout that were helpful (here and here), but in truth the best way to learn AutoLayout is to play around with it. Take a view, clear any constraints that are there, put the subviews where you want them, and then add your own constraints manually. While doing this, ignore warning messages from Xcode about ambiguous constraints and misplaced views. Ignore the yellow and red lines that show up on the screen indicating these errors. Until you have completely specified all the constraints needed to  determine unambiguously the location of the subviews without conflicts, these warnings will show up. Prematurely asking IB to Update Frames before all the constraints are specified will make the subviews jump around or disappear. Unfortunately even when all constraints are specified and correct, the yellow warnings don’t go away. IB is not capable of automatically applying your constraints and misplaces your controls in your views whenever you change constraints. Sometimes it misplaces controls even when you are just changing the storyboard metrics from one size to another. Update All Frames then puts everything where it belongs.

One way to start is by putting constraints on heights and widths of controls that you don’t want to resize when the screen size changes or the device rotates. Note that some controls, such as buttons, have an intrinsic size based on the button label, and it is not always necessary to add specific constraints to these controls. However, it looks to me like the system will ignore the intrinsic size at times, especially if you are trying to do something fancy with constraints, and your button will grow to a ridiculous size to satisfy your constraints. So it doesn’t hurt to specify width and height constraints manually even in these controls. Of course if you want controls to expand in one direction or another, don’t specify a constraint in that direction.

Next step is to align controls that are lined up horizontally. You can select multiple controls and then align their vertical centers using Editor | Align | Vertical Centers on the menu. If there are rows of controls like this you can take the leftmost control and working from the top to bottom, pin each row to the row above (or to the superview for the first row) to make sure there is vertical separation between the rows. Finally, usually you want your controls to be centered on the screen, even when using different screen sizes and with rotation. If you have one wide control, such as a segmented control or a large text field, you can horizontally center that control. You can then pin the leading edge of that control to the leading edge of the superview (i.e. the window) and that view will grow as the screen width increases. Aligning the leading edges and trailing edges of the other controls to this view will allow the whole set of controls and expand and contract with the width of the screen. If you have rows of controls, you may still need to put constraints between individual controls to control the horizontal distance between them.

One issue I noticed was that, while it’s nice to have controls expand to fill the screen of the iPhone when going from the small iPhone 4s to the 5.5 inch iPhone 6, sometimes the controls get too wide when viewed in landscape mode or on the iPad if they are just pinned to the superview leading edge.  For example, this segmented control is centered horizontally and vertically in the superview, and the leading edge is pinned to the superview leading edge.

Screen Shot 2014-10-01 at 10.03.52 AM
Segmented control has centering constraints and is pinned to the leading edge of the superview.

On the iPhone 4s, with rotation the view remains centered and enlarges when the device is rotated.

iPhone 4s portrait
iPhone 4s portrait
iPhone 4s landscape.  Control remains centered and expands to fill screen.
iPhone 4s landscape. Control remains centered and expands to fill screen.

To show the flexibility of AutoLayout, we can limit the expansion of the segmented control to a maximum we select, by making the width less than or equal to a value (in this case 350) and lowering the priority of the pinning of the leading edge to the superview. This achieves the desired effect.

Width of control constrained to ≤ 350 and priority of pinning to left margin decreased to 750
Portrait view is unchanged, but landscape view (shown here) limits the width of the control to a max of 350
Portrait view is unchanged, but landscape view (shown here) limits the width of the control to a max of 350

You can do a lot with AutoLayout just using IB if you are patient and try out various effects. You can do more by attaching your constraints to outlets and manipulating them in code. It is unfortunate that some glitches in the implementation of AutoLayout in Xcode 6 Interface Builder make using AutoLayout more frustrating than it needs to be.  To those who are discouraged like I was by AutoLayout, I urge you to keep experimenting with it.  The a-ha moment will come and it will be worth it.

Categories
Computers & Software

Spaces in Paths are Evil!

This comes as no surprise to anyone who has wrestled with this problem. While trying to make sure that the EP Simulator program builds and runs on Windows, I ran into trouble using absolute paths in my qmake *.pro files. Qmake is a tool from Qt Software that generates a Makefile that builds the program. Everything worked fine in Linux, where the path to the program directory is /home/mannd/dev/git/epsimulator. In Windows (XP) I had set up the following path in my “home” directory: L:\Documents and Settings\mannd\dev\git\epsimulator. This path though breaks the Makefile, since the path gets split into a “L:Documents”, an “and” and a “Settings\…” path, which totally screws everything up. Of course I tried putting quotes around the path, but this does not correct the problem; somewhere inside qmake the quotes get stripped and the path goes the Makefile which mangles it like before. I tried various sed-like scripts to escape the spaces, substitute forward for backslashes, but to no avail. The problem seems to be infinitely compounded by the fact that Windows uses backslashes instead of forward slashes as a path delimiter, and so the escape characters are reversed from unix usage, and the whole thing is a big mess. Yes I know you can use forward slashes in Windows instead of backslashes, but Windows also uses forward slashes to set off command options (e.g. copy /x) so what’s a poor unix-based make and gcc to do? Get messed up, that’s what. I also know spaces are allowed in Linux paths, but there are no spaces in critical file paths like “\Program Files” and “\Documents and Settings” in Windows. And yes finally I am aware that Microsoft has changed these file names in Vista to eliminate spaces. Thank you! In the meantime, I gave up on using absolute paths in my qmake files (using relative paths like “../../lib” instead) and so continue to move on.