tag:blog.corywiles.com,2013:/posts Cory Wiles Blog 2021-06-15T00:42:06Z tag:blog.corywiles.com,2013:Post/1094341 2016-09-28T17:00:44Z 2021-06-15T00:42:06Z Moving to Medium

Over the next few months I'll be migrating the blog over to Medium. I was an early Posterous adopter and really enjoyed the service. I was happy to move over to Posthaven as simple easy to use alternative.

However, my needs have grown while the features and updates to the platform. This is not a dig on the owners and developers of Posthaven. I think what they have committed to is admirable and we need more founders like them.

If you arrive here and are looking for my latest contributions please head over to Medium.

]]>
tag:blog.corywiles.com,2013:Post/1085145 2016-08-30T14:28:36Z 2016-08-30T14:28:36Z Objective-C Developer in a Swift World/ Map and Filter

Old Dog Swift Tricks

Over the past year and half I’ve spent as much time as possible learning Swift. I don’t care how senior of an iOS developer you are, Swift is a shift in language and paradigms. I hit the reset button in my development career and wanted to get up to speed with the new language changes in a short amount of time.

I’m Loving It

I’ve been reading, coding, cursing and embracing Swift since 1.0 (including Swift 3) and I love the language. Before I taught myself Objective-C I programmed in Java, PHP and occassionally Python. All of the later languages where extremely fun, but didn’t hold a candle to Objective-C or Swift.

Below are some examples of the new language features.

New Paradigms

  • Map
  • Reduce
  • Filter
  • Generics
  • ErrorType
  • Functional
  • Protocols
  • Value Types

Adjustments

Adjusting to these paradigms hasn’t been easy, but all been worth it. Swift is the future and the latest XCode is my Delorian.

Full Steam Ahead

Apple and the community have spoken…Swift is the future. With that said I’m not letting my Objective-C (fu) go to waste. I, like many others, have a TON of code written (and needs to be maintained) in Objective-C. The language and features are amazing and if you are starting out in iOS development then I would strongly suggest boning up on you Objective-C (fu).

Lessons Learned

Embrace the Swift change swiftly…and by change I mean high order functions mapand filter.

Map

Definition

The map method solves the problem of transforming the elements of an array using a function. Let’s look at some examples.

Example

Let’s say you want to display a list of names from a Person object in a UITableView. In the “old” way I would have a list of Person objects and then in my UITableViewCell I would have grabbed the instance of the Person object from the list and set the textLabel.text to the name property value.

In the Swift “way” map would simplify this.

struct Person {
    let name: String
    let age: Int
}

let names: [String] = people.map { return $0.name }

You can also apply the same principles to dictionaries.

Uppercase the values

let payload: Dictionary <String, String> = ["hello": "world", "steve": "jobs"]
let result = payload.map { (key, value) in (key, value.uppercased()) }

/// Output

[("hello", "WORLD"), ("steve", "JOBS")]

Filter

Definition

The filter method solves the problem of selecting the elements of an array that pass a certain condition

Example (name filter)

Filter names from a pre-defined list

Extending our map example from above let’s say you have a list of Person objects that you want to filter from another list. Somewhat like a search filter.

struct Person {
    let name: String
    let age: Int
}

let people = [
    Person(name: "Katie",  age: 23),
    Person(name: "Bob",    age: 21),
    Person(name: "Rachel", age: 33),
    Person(name: "John",   age: 27),
    Person(name: "Megan",  age: 15)
]

let matchingNames = ["Katie", "Rachel", "Megan"]
let names: [String] = people.map { return $0.name }
let filteredNames = names.filter({matchingNames.contains($0)})

/// Output

["Katie", "Rachel", "Megan"]

Example (character filter)

let badCharacters: [Character] = [".", "$", "#", "[", "]"]
let badChannelName: String = "thisis my string[.$#"

let cleanName = String(badChannelName.characters.filter{!badCharacters.contains($0)})

/// Output

"thisis my string"

Example (filter dictionary)

let data: [String: String] = ["hello": "world", "steve": "jobs"]
let filtered = data.filter { $0.1 == "world" }
// let filtered = data.filter { $1 == "jobs" }

Real World

I have a list of objects with three properties: name, imageName and selected. You can toggle the selection from the list display. If user selected one then I would toggle the “selected” flag then I would add it to a local Array of toggled items. Didn’t care there were dupes. When the user was finished with workflow then I need to pass the selected options to another view controller with only names.

Obvious question is “How do you filter an array of duplicates?”. With a a combination of map, filter and a computed property the answer is out there.

Let’s assume I have two lists of sports that can be selected or unselected and those datasource for those lists. What is the optimal solution for returning a final list of objects who has a selected value of true?

Let’s start with the lists

let SportPickerTypesRight: Array<SportPickerType> = [SportPickerType.create("NFL", imageName: "nfl_background"),
                                                     SportPickerType.create("NCAA Football", imageName: "football_background"),
                                                     SportPickerType.create("NCAA Basketball", imageName: "basketball_background"),
                                                     SportPickerType.create("NBA", imageName: "nfl_background")]

let SportPickerTypesLeft: Array<SportPickerType> = [SportPickerType.create("NHL", imageName: "nhl_background"),
                                                    SportPickerType.create("Soccer", imageName: "soccer_background"),
                                                    SportPickerType.create("MLB", imageName: "mlb_background"),
                                                    SportPickerType.create("Other", imageName: "other_background")]
var SportPickerTypes: Array<SportPickerType> {

    let combined = [SportPickerTypesRight, SportPickerTypesLeft]
    
    return combined.flatMap { $0 }
}

struct SportPickerType {

    let name: String
    
    let imageName: String
    
    var selected: Bool
    
    static func create(name: String, imageName: String) -> SportPickerType {
        return SportPickerType(name: name, imageName: imageName, selected: false)
    }
}

extension SportPickerType: Equatable {}

extension SportPickerType: Hashable {
    
    var hashValue: Int {
        return (name + imageName).hashValue
    }
}

func ==(lhs: SportPickerType, rhs: SportPickerType) -> Bool {
    return lhs.name == rhs.name
}

/// When you want to return a list of unique selected sports
///
/// What the property does is return a set (unique values)
/// and the filters that set for only values with selected == true
/// and returns that array of names

   var selectedSportTypes: Array<String> {
        
        let filteredSet: Set<SportPickerType> = Set(self.unfilteredSportTypes)
        let mappedNames: Array<String> = filteredSet.filter({$0.selected == true}).map{return $0.name}
 
        return mappedNames
    }

Understanding these higher level function provide great functionality and clarity to your coding. Swift is the future and learning / understanding these higher level functions will provide much more elegance to your code.

]]>
tag:blog.corywiles.com,2013:Post/1040745 2016-04-25T19:33:48Z 2016-04-25T19:36:05Z Handling Min Character Requirements in iOS with Swift

Bytes Not Length

While working on a little side project I was testing out some for validation on a textview. The business rule was that the user has to enter at least three characters before they can save the form. However, using an emoji should pass as well and wasn’t because I was checking character length only.

One string character == 1 byte, but one emjoi == 4 bytes.

I just had to change my validator it look for string bytes greater or equal to 3. Not a sexy solution, but it reminded me to a) not make assumptions b) to really think through usecases when dealing with form inputs.

Check out the playground.

]]>
tag:blog.corywiles.com,2013:Post/996254 2016-02-18T15:42:08Z 2016-02-18T15:42:09Z 2.0.1 More Than Meets the Eye

More Than Meets the Eye

2.0 was released back in mid December. The release was a 6 week effort to completely overall the UI. Today’s release was focused more on UI tweaks, bug fixes and universal app linking.

This Feedback is BAD

What seemed like over night, analytics showed that 2/3rds of iOS users are now on iPhone 6(s) phones. Two bug reports that came in on the same day made me panic. Users who were trying to do mobile broadcasts on a 6 or 6s were getting horrible feedback from the phone. I’m not sure if this hardware specific or iOS update that introduced regression.

In previous versions of the app, and lesser hardware, when a user wanted to started a broadcast I used the following configuration for the audio session:

[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryRecord error:nil];
[[AVAudioSession sharedInstance] setActive:YES error:nil];
[[AVAudioSession sharedInstance] setMode:AVAudioSessionModeDefault error:nil];

In order to fix the broadcasting feedback issue I had to override the audio output port like so:

    NSError *error;
    
    if (![[AVAudioSession sharedInstance] overrideOutputAudioPort:AVAudioSessionPortOverrideNone error:&error]) {
        RTVLog(@"AVAudioSession error overrideOutputAudioPort to Reciever:%@",error);
    }

Realtime Play Counts

Stats are not really very useful in a “snapshot” context. This release added in functionality that will update a broadcast’s play count in real time.

Up and Down

It seemed awkward for many users in the early days of iOS to “swipe” for actions, but over the past 3+ years Apple has been including these mechanisms in interacting with the OS and native apps (pull to refresh and mail controls). I added in swipe gestures on the broadcast header for up / down vote. The gestures free up real estate and feels more natural.

Custom Filter Control

I’m a huge advocate for using 3rd party libraries as little as possible in my apps, unless it absolutely necessary. There are tremendous number of truly amazing controls and libraries written by people smarter than myself, but I learn more my doing than copying. We wanted to move the “Live” and “Upcoming” filter from the drop down to above the lists and the user would toggle that way.

There are a million ways to skin that cat. I’m not the biggest fan the UIButton class due to all the setup, but using an interactive UIView didn’t seem the right approach either. I settled on just creating my own custom UIControl which was a fun little task that only took a few hours. Though it is simple to look at and interact, which was the goal, IMHO, the subtle animations and control states are what make it shine.

There is always room for improvment. If you have an comments or questions about features or bugs please email me at cory {@} rabble (.) tv

]]>
tag:blog.corywiles.com,2013:Post/916829 2015-10-14T21:12:42Z 2015-10-14T21:12:42Z Rabble 1.3 Released - It's a BFD

In the Beginning

The two main functions of RabbleTV are listening and broadcasting. Since I first met with RabbleTV to talk about what they wanted one of the most talked about features has always been broadcasting from the app. We decided it was best to leave it out of the first few versions of the app because of the huge development effort that is involved with this one feature.

Once version 1.2 was safe and sound in the App Store I dove into the deep end of live broadcasting from the app. Ben (CTO) and I spent a few days just going through what was on the web and what needed to be re-thought to provide the best experience on the phone.

A BFD

On the desktop version you can create a broadast for Sports, TV / Movies or a RabbleCast. The first 2 have quite a bit of workflow that is doable on the phone, but would require an enormous effort on the client side to accomplish the ease of use that is found on the web. We wireframed the solution, but decided for this release we would stick with RabbleCasts. With workflow set out it was time to create the 1.3 feature branch and get to work.

Build, Buy or Borrow

AVPlayer provides a wonderful API for streaming audio and video. Unfortunately, there isn’t such for sending out audio and video. You really need to drop down to lower level API’s. When I set down to start writing the broadcasting functionality I had 3 choices:

  1. Write my own
  2. Use an open source solution
  3. Purchase and off the shelf solution

I’m a huge proponent of writing my own libraries (if possible)…especially if it centers around a core piece of functionality in the app. In this case, due to the feature timeline, it made sense to go with something stable that someone else had written. I just didn’t have the time to write the library. YET!

There aren’t many open source solutions, but the one I decided to use is MediaLib. After a few configuration settings made I was up and running fairly quickly. I felt a little uneasy about using the library because most of the source code is compiled into static libraries and since this was such an important piece to the app going forward I didn’t want to be caught in a situation where an iOS upgrade broke the library.

Due to the fact I still wasn’t in a place to write my own library we decided it would be worth the money to purchase a license (compiled version) from RealTimeLibs. Being the cautioned person that I am I got in contact with their product support to ask specifically about their iOS 9 support and development, upgrades, licensing and some custom development that we needed to add in. They were very quick in responding and gave me all the information that I need and a license was purchased.

I was assured that the custom development was only going to take 4 hours and would cost X amount extra. No problem. Weeks went by and no word on when the custom development was going to be done. This was very annoying, but still not a deal breaker since I had a stop gap put in place. What was a deal breaker the lack of background support.

When a user was broadcasting and puts the app in the background the stream was disconnect.

RTMP sends / receives audio and video. Even though I was doing audio broadcasts only their was still a video capture session initialized and you can’t broadcast video with the app in the background. I contact their support about this and was told…well not what I wanted to hear and knew better.

It is a good thing I didn’t delete my old manager. It was easy to revert the project back to using my open source based solution.

Having been reaffirmed in my belief of being in control of my app’s libraries I’ll be writing my own over the next few months when time allows.

Testing is Hard

Unlike Bill O’Rilley, I don’t just assume that something is going to work live. Any sort of live media publishing on a mobile device is going to get interrupted by FaceTime, phone calls, phone restarts, etc. A lot of time was spent testing various application states and system interruptions to make sure that listeners were notified, as well as, giving the broadcaster the ability to restart a broadcast if necessary.

I even found a bug with iOS 9 regarding FaceTime.

Additional Features

In addition to broadcasting these additions features and enhancements were added: * iOS 9 support * Spotlight search * Background fetch * Better list caching * New default broadasting image * Comment links now have better touch handling

Don’t wait download or update now!!!!

]]>
tag:blog.corywiles.com,2013:Post/907403 2015-09-21T17:00:00Z 2017-03-28T08:22:04Z Broken FaceTime Audio Interruptions in iOS 9

Constant Interruptions

I’ve been working on a new feature for RabbleTV the past 3 months. Now that the functionality is pretty close to shipping I’m going through all the various testing scenarios of making sure that if/when the user gets an audio interruption (phone call, FaceTime, etc) that I handle the app state appropriately.

Apple has pretty straight forward guidelines of how to handle these types of interruptions. When one occurs the AVAudioSessionInterruptionNotification is sent out and the developer can inspect whether the AVAudioSessionInterruptionTypeKey is equal to AVAudioSessionInterruptionTypeBegan or AVAudioSessionInterruptionTypeEnded and handle their app state and UI appropriately.

HOWEVER, looking closer at the documentation there is one very important sentence:

There is no guarantee that a begin interruption will have an end interruption. Your app needs to be aware of switching to a foreground running state or the user pressing a play button. In either case, determine whether your app should reactivate its audio session.

Cause for Panic?

Receiving FaceTime calls on iOS 8 works “as expected”. The appropriate notifications are fired off by iOS and I’m able to pause and continue my use of the microphone. The development was moving right along until I started testing under iOS 9.

Going through the same scenario using iOS 9 the AVAudioSessionInterruptionTypeEnded type is never available. The AVAudioSessionInterruptionNotification is called when the interruption begins and ends, but the only type that is set is AVAudioSessionInterruptionTypeBegan.

iOS 9 Log

2015–09–17 09:40:32.098 RabbleTV[6541:2258301] _66-[RTVBroadcastingManager addAudioInterruptionNotificationListener]block_invoke(271): notification for interruption NSConcreteNotification 0x14e8a6ea0 {name = AVAudioSessionInterruptionNotification; object = <AVAudioSession: 0x14e813220>; userInfo = { AVAudioSessionInterruptionTypeKey = 1;}}

iOS 8 Log

2015–09–17 09:34:35.405 RabbleTV[471:106341] _66-[RTVBroadcastingManager addAudioInterruptionNotificationListener]block_invoke(271): notification for interruption NSConcreteNotification 0x17f31ab0 {name = AVAudioSessionInterruptionNotification; object = <AVAudioSession: 0x17ee4860>; userInfo = { AVAudioSessionInterruptionTypeKey = 1;}} 2015–09–17 09:34:52.715 RabbleTV[471:106341] _66-[RTVBroadcastingManager addAudioInterruptionNotificationListener]block_invoke(271): notification for interruption NSConcreteNotification 0x17dae300 {name = AVAudioSessionInterruptionNotification; object = <AVAudioSession: 0x17ee4860>; userInfo = { AVAudioSessionInterruptionOptionKey = 1; AVAudioSessionInterruptionTypeKey = 0; }}

I have some business rules that add a bit more complexity to work around this issue, but if your only concern is to know when it the interruption began and ended then you can set a property to toggle between the states.

I’ve filed a radar with Apple.

]]>
tag:blog.corywiles.com,2013:Post/894287 2015-08-16T17:32:41Z 2015-08-17T17:00:05Z Detecting When UIGravityBehavior is “Off Screen"

A Simple Implementation

Yesterday I was working on some UI enhancements to a new feature that is coming for RabbleTV. One of the pieces to the UI envolved using UIDynamics…specifically UIGravityBehavior. This was going to be a pretty straightforward implementation considering I didn’t need to use the UIGravityBehavior with any other types as I had done with previous apps.

Assumptions Are Bad

During some testing I noticed that the CPU would spike during the animation, but never go back down after I assumed the animation was complete…in my case the “fall” passed the referced view’s bounds. I didn’t think too much of it at the time because I still needed to add in my completionHandler. I kicked the can down the road for a few hours until I could profile it. I assumed it must have been a coincidence since I’m also making network calls during this animation as well.

Upon the first run of my now completed UI animation the completionHandler wasn’t called. I checked and doubled checked my code and all the appropriate delegates and properties were set. The next part of my debugging strategy was to see when exactly the behavior was stopped. Perhaps I was trying to perform an action before everything had been completed. This is where my assumption bit me.

I had assumed that UIGravityBehavior was completing, but in reality it wasn’t. I was able to verify this by logging the current point in the reference view the item was at using linearVelocityForItem.

The fall was infinite. After I stopped and thought about it it made sense. If the UIGravityBehavior is supposed to represent gravity on an object and space is infinite then why would it ever stop. I had never run into this before because in all my other experiences of using UIDynamics I used UIGravityBehavior inconjunction with other behaviors.

Choose Your Solution

As I saw it I had two possible soultions to implement to fix my issue.

First

Use UICollisionBehavior. There really isn’t much more to say there. You can setTranslatesReferenceBoundsIntoBoundaryWithInsets to setup the area where you want the items to “stop”.

Second

Add a UIDynamicBehavior that checks for the Y coordinate as the items are falling (specifically the last item). Once it is past the height of the reference view then remove the behaviors.

And the winner is…

I opted for the second approach because it gave me more control over when to stop the animation. Once I updated my animation controller all of the delegate and completionHandlers were properly called.

Code Snippet

// MARK: Public

- (void)animateItemsWithCompletionBlock:(RTVStandardCompletionBlock)block {

    if (block) {
        self.animationCompletionBlock = [block copy];
    }

    self.animator.delegate = self;

    NSArray *referenceItems = self.itemReferences.allObjects;
    
    /**
     * Gravity Behavior
     */

    UIGravityBehavior *gravityBehavior = [[UIGravityBehavior alloc] initWithItems:referenceItems];

    [self.animator addBehavior:gravityBehavior];
    
    /**
     * Dynamic Behavior
     *
     * @note
     * I'm adding the dynamic behavior so that I can tell when the last item
     * has fallen past the bottom of the screen. Once it has then I remove all
     * the behaviors. This will trigger the animator delegate method, which will
     * call the completionBlock.
     *
     * Without doing this the view continues to "fall" and eats up the CPU.
     * Another possible solution is to setup a collision barrier which should
     * trigger it as well.
     */
    
    UIDynamicItemBehavior *dynamicItemBehavior = [[UIDynamicItemBehavior alloc] initWithItems:referenceItems];
    
    __weak UIDynamicItemBehavior *weakBehavior = dynamicItemBehavior;
    __weak typeof(self) weakSelf = self;

    dynamicItemBehavior.action = ^{

        /**
         * @note
         * You only need to wait for the last object to finish (drop below) as 
         * opposed to iterating over all the item.
         */

        CGFloat currentY = [weakBehavior linearVelocityForItem:referenceItems.lastObject].y;
        
        if (currentY > CGRectGetMaxY(self.animator.referenceView.frame)) {
            [weakSelf.animator removeAllBehaviors];
        }
    };

    [self.animator addBehavior:dynamicItemBehavior];
    
    /**
     * Implict Animation of Alpha
     */
    
    [referenceItems enumerateObjectsUsingBlock:^(UIView *item, NSUInteger idx, BOOL *stop){

        [UIView animateWithDuration:0.65
                              delay:0
                            options:kNilOptions
                         animations:^{
                             item.alpha = 0.0f;
                         }
                         completion:nil];
    }];
}

Lessons (Re)Learned

  1. Space is inifinite
  2. Never assume anything
]]>
tag:blog.corywiles.com,2013:Post/879404 2015-07-10T13:57:20Z 2015-07-10T13:57:20Z Rabble 1.2 Released

Today version 1.2 launched. Launching 1.1 was pretty hectic and I missed detailing some of the more technical changes from 1.0. Each iteration brings important lessions learned.

Version 1.1

Your version 1.0 is always the hardest. This is because you want to get to market as quickly as you can, but not at the expense of quality and user experience. Before 1.0 was started 1.1 was already roadmapped and I began basic development a few weeks before 1.0 was released.

Features, Fixesa and Improvements

  • Added ability to open broadcast detail link from the web directly into the app. I wrote my own router classes for this which I am able to use for more deep linking functionality and simplified the code for the OAuth dance that must be performed for social media authentication
  • Fixed bug where an archived broadcast would start playing if it was paused, the app was put into the background and then you brought the app back in the foreground. This was caused by the state of the AVPlayer and KVO calls automatically calling play on the instance
  • Fixed display issues with Rabblecasts.
  • Fixed crash that would occur when trying to load more “featured” broadcasts.
  • Fixed issue where sometimes broadcasts for PLU would be co-mingled.
  • Added loading feature for play/pause. AVPlayer has to do some setup work before the HLS stream will start playing and depending on the network connection this could take a bit of time so I overlay a UIActivityIndicatorView.
  • Wrote my own custom label with link tap support so comments are much better. This was one my favorite improvements. I ended up migrating most of the other labels to this new subclass.
  • Caching lists. I consider to caching to be a shield and sword. On one hand it allows for a much better use experience and the conversvation of resources, it also introduces lots of potential bugs and in a multithreaded environment race conditions. Further down on the roadmap I will be integrating CoreData, but as “step 1” I’m using NSCache. I created a fairly nice cache manager and protocol that sits on top of it so that I can swap out NSCache or bolt CoreData on top of it when the time comes.
  • Reminders. This was the biggest feature to the app. When a user schedules a broadcast there isn’t a 100% guarantee that it will start or start on time. This is not a fault of Rabble’s service, but rather the broadcaster has failed to start it and/or is running late. On each upcoming broadcast detail screen you can set a reminder. If/when the broadcaster starts you’ll be notified via push notification. This is big improvement for user engagement and retention.
  • Fixed issue with certain sports broadcasts were showing (null).

Version 1.2

The goal of 1.2 was geared to more polish, speed enhancements and putting the app’s UI/UX in the direction for some big features coming over the next two to three months.

  • Simplified the app navigation which will allow for adding in additional features (hint hint).
  • Custom transitions.
  • Fixed nasty keychain bug. Fortunately this didn’t effect, but one user. The issue came about during some UAT. The bug occured if you deleted the app without logging out first, reinstalling the app and trying to login. The old keychain value wasn’t being deleted so you would find yourself in “authentication” loop.
  • Custom font.
  • Moved to swipe actions for comments and role action. The comments’ cell looked pretty crowed and the touch areas for up and down voting weren’t optimum. I moved everything over to a swipe to reveal control similar to the native mail application. This also allowed me to add in more role based actions for deleting and muting comments.
  • Fixed overlay bug that occured on the main broadcast list screens.
  • Tweaked caching.
  • Migrated my RabbleKit static library over to a framework
]]>
tag:blog.corywiles.com,2013:Post/859576 2015-05-22T02:40:27Z 2015-05-22T02:41:54Z Intro to iOS Class Teaching Experience

File -> New -> Teaching Class

It is no secret that there has been a growing, and necesary, push to bring more females into tech. I was asked a few months ago if I would be interested in teaching an intro to iOS class for woman at Lamp Post Group. It would be a six week course that met two hours every Wednesday. I jumped at this opprotunity. I’ve given lectures and pitches before, but never taught a class so I figured this would be an amazing opprotunity to:

a) contribute something positivie to the local femal tech community

b) help myself grow in an area that I’m interested in

c) promote iOS development.

When I talked with the program director at LPG I learned of my most exciting challenge yet…the diverse background of my students. This included not only age, but the level of prior experience in any development. I couldn’t wait to get started.

Develop and Teach with a Purpose

The most memorable classes I had in college were always the one’s where my professors had passion for the subject(s) they were teaching. It shouldn’t be a surprise to anyone that those were the classes that I excelled in. My computer science and M.I.S. classes I took I was average in because of the lack of enthusiasm from my teachers.

One of my primary goals was to make sure that my students knew that I was passionate about iOS development and that they had my full attention. I was there for them.

The next step was to outline a curriculum that I could present to the class. This was tougher than I had anticipated. Most of the intro classes that I have seen or been in always position themselves as “by the end of the class or workshop you will have built an app”. I personally think this does a disservice to the attendees. My reasoning for feeling this way is that if you break down the various components it takes to actually create an app, the coding portion is but a fraction. What about the actual submission process, Xcode, design patterns, language features, etc? These are all very important concepts that are too often skipped.

This would not be the case in my class. At the end of the class I wanted the women to have the necessary foundation to carry on with their future careers.

Pencil vs Pen

At the beginning of the first class I told everyone that our curriculum wasn’t going to be written in stone. Granted, I had curriculum outlined, but it was important that we move at the pace of the class, thus the topics covered could / would be adapted.

In retrospect this was the best decision I made.

Classroom Supplies

PowerPoint was always the bane of my existence in college and early in my career. Keynote was a much welcome alternative, but I probably use less than 1% of its power. I don’t use animated transitions or fancy slide layouts. I keep each slide to a topic or sub topic and engage my students. There is nothing worse than having a presenter read verbatim from their presentation.

Deckset was the perfection solution. Creating presentations using markup was right up my alley. I can’t endorse this product enough.

if let humbled

I’ve been writing software for the past 18 years and iOS apps since the initial SDK. Teaching this class has been without a doubt my greatest challenge and reward. Truth be told, I was always nervous each passing week if people would return or was scaring away future developers, but each week the classroom was full and the questions were more intriguing.

Over the six weeks I think I learned just as much as my students did.

During our last class I asked if anyone had any final thoughts or questions before we signed off. The question shared amongst the majority of the class was, “Where do we go if we want to learn more?”. A huge smile came over my face and I felt that I was successful in teaching the class. I knew that I wasn’t going to be able to cover every topic that I wanted to nor would everyone “get it” at the end, but my goal was to inspire them to keep learning.

Code of Wisdom

The advice I gave them to becoming successful developers…

Presentations

All of my desks and samples are on Github.

]]>
tag:blog.corywiles.com,2013:Post/834752 2015-04-02T14:53:30Z 2015-04-03T03:36:04Z Mute.Mic.Rabble 1.0

Receipe for Success

I’ve never quite understood why television broadcasting, especially sports, has continued to decline in quality over the past 20 years. I speculate that the networks would rather spend money on “personalities” that actually distract from the event than enhance it. In addition, there are so many rules and regulations imposed by the FCC, networks and sponsors that I always felt watered down the experience.

Other mediums, such as, podcasts, forums, neighborhood bars or a friend’s basement; you have individuals, fans and super fans who provide much better commentatry, stats and experiences than you would ever find on the networks. These individuals aren’t shackeled by duldrums plaguing corporate broadcasting and are free to really “call it like they see it”. Unfortunately, there hasn’t been a platform for these loquacious verablizers to sound off…that allowed for not just one way communication, but also given the ability to interact with their listeners in real time.

Introducing Rabble.tv

Serendipty

I knew my contract was ending last December with Life360, so around November I started to look around for the “next thing”. I got a call from Ben (CTO of Rabble and best friend) asking if I would come to Nashville and chat with the other two founders about what it would take, or even possible, to build their iOS app. I don’t need much of an excuse to go hang out with Ben and other cool people in Nashville. After spending about half a day hearing what they needed and when they needed it I headed back to Chattanooga with the green light to build the first RabbleTV app.

File -> New Workspace/Project/Design/EcoSystem, etc.

~35 days to develop…that’s what I had.

When I sat down to start cranking on the app it really began with a todo list of tasks that needed to be completed before I could actually start writing code. This included setting up the developer account, certs, github repos, workflows for testing and general design/wireframe elements. Since there was no time to waste I jumped right on my list and starting knocking things out.

Because fate loves to provide us with challenges, a few where thrown my way. These were in the form of getting the flu for almost 5 days, as well as, my wife and kids. Nothing brings development to a hault like a house full of sick people. Despite this set back I was able to make up the time. Ironcially, what caught me up was going on vacation with my wife to Tulum. We had our trip planned for almost a year and right before we left I was defintiely feeling the pressure. I had many late nights so completely disconnecting for 4 days was exactly what I needed. When I got back the saw was sharpened and was cutting faster and better than ever.

Development

Writing Swiftly with Objective-C

The entire app was written in Objective-C. Yep, that’s right…not the first line of Swift. My reasoning for this is due to the very limited time for development and right now I’m much faster with Objective-C. Future releases will incorporate Swift. I did setup the project to support Swift from the beginning knowing that I wanted to use it in the near future.

Hard Feature Decisions

The features on the web far out number the features on version 1 of the app. This was both a neccessity and a choice. The end goal of my development time was to go from version none to version one. Trying to fit all that Ben had developed for the web would have easily been 6 to 8 months of development. In reality the usage of on the phone is going to be much different than that on the web. Shocker I know!!!!

The Rabble guys were great to work with in regards to these decisions. As such, I believe and stand by the features that are supported by the iOS app. Additional features will be coming shortly.

TestFlight and Crashes

For beta testing I opted for TestFlight for beta distrubutions and Flurry for reporting and crash reporting. This was both a blessing and a curse. By curse I mean all the cursing I did when iTunes Connect had its meltdowns and a blessing when it worked. My experience was 50/50.

What’s Next

Discussions for what was going to be included also included what was going to be in 1.0.1. These are secondary, but still important features, as well as, bug fixes. I’ve also laid out the roadmap for 1.0.2, 1.1 and 1.2.

Zen and the Art of iOS Development

This has been the BEST app I’ve ever worked on. The app is far from perfect and will continue to improve and evolve (like software is supposed to), but the experiences couldn’t have been better. The work/life balance has been phenonmueal and working 1:1 with Rabble couldn’t be better.

My good friend, Jason Jardim, tweeted an article called Looking back at where it all began, the basement startup. As I look back on building the Rabble iOS app, I completely understand what the author is describing. Though I actually work in my home office, I have lots of fresh air, open light, etc…it is pretty humble and I wouldn’t trade it for anything.

Mute.Mic.Rabble

Promo Video

Download Now

Sign up now

]]>
tag:blog.corywiles.com,2013:Post/827736 2015-03-20T19:36:46Z 2015-03-23T13:49:14Z Objective-C Runtime with Swift

One thing that I really miss when using Swift is Objective-C’s runtime. I don’t go crazy with it, but its power, when used wisely, is highly effective.

I had an idea a few weeks ago for a fun little app and thought this would be a great time to build an app from the ground up using Swift. I have a lot of convenience classes and logic written in Objective-C that needed to be ported over. One the first, and most important categories, is on UIView. I extend the class to include the ability to add a tap gesture.

Over the years I’ve moved over to use Oliver Drobnik’s technique. It is cleaner than my original approach.

When Swift was announced, Apple assured developers that you could mix and match Objective-C and Swift. Most things you can. One of the skeptisms I originally had concerned the runtime. Mattt Thompson posted a great piece on NSHipster that gives some great examples on how to do it. I used that article as the main reference when starting to port my category over. When I went to compile the project I got the following error.

error: type ‘() -> Void’ does not conform to protocol ‘AnyObject’

var tapAction: (() -> Void)? {

    get {
        objc_getAssociatedObject(self, &AssociatedKeys.SNGLSActionHandlerTapBlockKey)
    }

    set {

        objc_setAssociatedObject(
            self,
            &AssociatedKeys.SNGLSActionHandlerTapBlockKey,
            newValue,
            UInt(OBJC_ASSOCIATION_COPY_NONATOMIC)
        )
    }
}

I wrestled with this for quite sometime. I understood what it was saying, but didn’t have an elegant solution for it or hell an unelegant solution. Part of my problem was that I was thinking in terms of Objective-C and not Swift. After posting to StackOverflow Greg Heo pointed me in the right direction. How do make pass in a Void type into something that must conform to AnyObject…you have to wrap it in a class.

Well that is simple enough:

class ClosureWrapper {
  var closure: (() -> Void)?

  init(closure: (() -> Void)?) {
    self.closure = closure
  }
}

var tapAction: (() -> Void)? {
  get {
    if let cl = objc_getAssociatedObject(self, "key") as ClosureWrapper {
      return cl.closure
    }
    return nil
  }

  set {
    objc_setAssociatedObject(
      self,
      "key",
      ClosureWrapper(newValue),
      UInt(OBJC_ASSOCIATION_COPY_NONATOMIC)
    )
  }
}

Not so fast. That is the right concept, but there is some additional work that you have to do. The ClosureWrapper needs to inherit from NSObject and conform NSCopying.

Now I’m able to add a tap gesture to any UIView subclass.

]]>
tag:blog.corywiles.com,2013:Post/804440 2015-01-29T17:29:28Z 2015-01-29T17:29:28Z Personal Project Preferences for IOS

It’s Personal

How developers setup their projects is a very personal subject and can invoke extreme emotion if ever critized. While I’m pretty set in my ways as to how I like to setup my iOS projects, I know that I’ve gotten to my preferred setup because of others so I would like to share mine.

Workspaces and Projects

When starting a new iOS project I always create a new workspace. Most of my projects are not just a singular project and workspaces provide the best relationship management, whether it is a static library, framework or related.

Filesystem

Since I edit the classes in the Xcode and do not access them via the file system I don’t create any additional folders or hierarchy inside the project(s). It doesn’t make seems to me. Any class organization I handle with groups.

With the addition of asset folders in Xcode, image assets are now kept within the project as well. The file system project folders are split between the various “source” projects, configurations, external libraries and misc dependencies.

Configurations

It is a huge pet peeve when I see code like this:

NSString *serverURL = nil;
#if DEBUG
serverURL = @"https://path.to.dev/";
#else
serverURL = @"https://path.to.prod/";
#endif

These type of environment key/values shouldn’t be determined by preprocessor macros inside of your app. Even logging can/should be handled by a dedicated class. Since my projects usually have three build configurations, Debug, Adhoc and Release, I’ll create three plists: ConfigDevelopment, ConfigAdhoc and ConfigRelease.

You can create as many or few as you want depending on your setup

I don’t need all three of these plists in the app’s bundle, only the appropriate one which corresponds to my current build configuration. This is handled by a build script

#!/bin/sh

PATH_TO_CONFIGURATIONS="$SRCROOT/../../Configurations"
ConfigDevelopment="$PATH_TO_CONFIGURATIONS/ConfigDevelopment.plist"
ConfigRelease="$PATH_TO_CONFIGURATIONS/ConfigRelease.plist"
ConfigAdhoc="$PATH_TO_CONFIGURATIONS/ConfigAdhoc.plist"

echo "Checking for file $ConfigDevelopment, $ConfigRelease, $ConfigAdhoc..."

if [ $CONFIGURATION == "Debug" ]; then
echo "Processing $CONFIGURATION"
if [ -f "$ConfigDevelopment" ]; then
cp "$ConfigDevelopment" "$PATH_TO_CONFIGURATIONS/Config.plist"
else
echo "$ConfigDevelopment not found"
exit 1
fi
fi

if [ $CONFIGURATION == "Adhoc" ]; then
echo "Processing $CONFIGURATION"
if [ -f "$ConfigAdhoc" ]; then
cp "$ConfigAdhoc" "$PATH_TO_CONFIGURATIONS/Config.plist"
else
echo "$ConfigAdhoc not found"
exit 1
fi
fi

if [ $CONFIGURATION == "Release" ]; then
echo "Processing $CONFIGURATION"
if [ -f "$ConfigRelease" ]; then
cp "$ConfigRelease" "$PATH_TO_CONFIGURATIONS/Config.plist"
else
echo "$ConfigRelease not found"
exit 1
fi
fi

if [ -f "$PATH_TO_CONFIGURATIONS/Config.plist" ]; then
echo "Processing $CONFIGURATION"
echo "Found Config.plist, and copying to $TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH"
cp "$PATH_TO_CONFIGURATIONS/Config.plist" "$TARGET_BUILD_DIR/$EXECUTABLE_FOLDER_PATH/"
else
echo "Did not find Config.plist"
exit 1
fi

In my projects I create a simple NSObject subclass, AppConfig which loads the Config.plist from the bundle and sets the values from the plist’s dictionary. Though I haven’t done it yet, you could “automate” this process further by using some Objective-C runtime to dynamically create and set the properties.

WIth this setup whatever setting, URL, path, etc. I can grab by calling [AppConfig defaultConfig] without having to maintain or deal with unnecessary runtime settings.

Subclassing

I spend a lot of time and energy evaluating DRY in my code. This isn’t only applied to abstraction, but inheritenance. View controllers and views (standard UIView subclasses, UILabels, UIButtons, etc) will share some basic default attributes that I only want to set once, and by design, modify once to be applied to all. You can accomplish a lot on the UI side of things by using UIAppearance, which I do utilize, but anything outside of that supported by the protocol I have a base class.

For example, this is what my base UIViewController would look like.

- (instancetype)init {
    
    self = [super init];
    
    if (self) {

        self.edgesForExtendedLayout           = UIRectEdgeNone;
        self.extendedLayoutIncludesOpaqueBars = YES;
    }
    
    return self;
}

// MARK: View Life Cycle

- (void)loadView {
    
    self.view = [[UIView alloc] initWithFrame:[UIScreen mainScreen].bounds];
    self.view.backgroundColor = [UIColor whiteColor];
}

There isn’t much to look at, but in a fairly large application having type/copy/paste that becomes tedious and unnecessary. Now I just have to subclass this base controller.

In the case of UIView and UIControl subclasses, I almost exclusively use AutoLayout and I don’t use Storyboards or XIBs. I create a base AutoLayout view and button class and set the AutoLayout “flag”. Labels are given some extra love by adding an additional property so that I can handle multiline and edgeInsets.

// MARK: Initializers

- (instancetype)initWithFrame:(CGRect)frame {
    
    self = [super initWithFrame:frame];
    
    if (self) {
        
        _edgeInsets = UIEdgeInsetsZero;
        
        self.translatesAutoresizingMaskIntoConstraints = NO;
        self.textColor       = [UIColor whiteColor];
        self.backgroundColor = [UIColor clearColor];
        self.font            = [UIFont preferredFontForTextStyle:UIFontTextStyleBody];
    }
    
    return self;
}

- (instancetype)init {
    return [self initWithFrame:CGRectZero];
}

/**
 * @discussion
 * In order for autolayout (or sizeThatFits) to correctly calculate the size of the
 * label when using edgeinsets then we must adjust the rect accordingly.
 *
 * If not then you can run into the situation where multiline text is cut off.
 * This happens when calculating dynamic size height cells in UICollectionView's.
 */

- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {

    UIEdgeInsets insets = self.edgeInsets;
    CGRect rect = [super textRectForBounds:UIEdgeInsetsInsetRect(bounds, insets)
                    limitedToNumberOfLines:numberOfLines];
    
    rect.origin.x    -= insets.left;
    rect.origin.y    -= insets.top;
    rect.size.width  += (insets.left + insets.right);
    rect.size.height += (insets.top + insets.bottom);
    
    return rect;
}

- (void)drawTextInRect:(CGRect)rect
{
    [super drawTextInRect:UIEdgeInsetsInsetRect(rect, self.edgeInsets)];
}

MVCUC,e (Models, Views, Controllers, Utilies, Categories, etc)

I like to take the abstraction of responsibility a bit further using my own Foundation framework project added to the workspace that holds all the models, UIView parent classes, utilies and categories. The app project itself is mainly view controllers, static assets and config files. This type of setup wasn’t as necessary early on in iOS development, but as devices and functionality (Apple Watch, separate iPad and/or Mac apps and extensions) are added it has been extremely beneficial to keep these separate.

Daniel Kennet’s article talks about the benefits of taking a similar approach.

Basically, if it doesn’t involve UI or application state, it goes in a separate framework. My iPod-scanning app Music Rescue contained a complete framework for reading an iPod’s database. My pet record-keeping app Clarus contained one to work with the application’s document format.

Dependencies

This is probably the most debatible preference I have. I do not like CocoaPods. I prefer to use git submodules. I admire and respect the work that has gone into CocoaPods and there are certainly benefits to using it, but it has caused more headaches and is one more external dependency that I have to work with. Ruby (I know it is installed by default), messing with pod files, the XCode project modifications to name a few. Git submodules aren’t without their issues, but you have complete visbility into what is going on. I don’t feel that way about CocoaPods. I’ve used it on a few projects at different points over the past year and it just annoys me.

Networking

My network stack is homegrown solution, which consists of a Client, Request and Session class. The client has paginator property.


------------------------------------------------- =====================
Client                                              paginator
------------------------------------------------- =====================
------------------------------------------------
Request
------------------------------------------------
------------------------------------------------
Session
------------------------------------------------

The only class and methods that are accessed directly by the app is the Client instance which calls down the Request class (handles get, put, post and delete verbs) which in turns calls down the Session (wrapper around NSURLSessions).

The client has a property called paginator which allows the Client instance to call nextPage or previousPage for request pagination.

@property (nonatomic, weak) RTVPaginator *paginator;

XIB’s, Storyboards and Views

Unless it is a requirement I DO NOT use XIB’s or Storyboards. Over the course my iOS career, when I did use them, it became yet another asset to manage and I found myself, more times than not, doing all of my customizations programmatically and never really touched the XIB’s after their initial creation so I just quite using them all together. With the addition of AutoLayout in iOS6 all of the initial tutorials and examples used XIB’s so I gave it another try. To me it was MORE frustrating dealing with AutoLayout using IB so I started doing all of the setup programmatically and haven’t had any issues.

XCode Build Settings

Before I add/modify in any project I’ll create target specific xcconfig files. One shared and specific ones for the various build settings. Tweaking specific settings becomes much more manageable that way, in addition, to see change sets via diff tools. Viewing the project files in any diff viewer is a horrible experience.

The Way

The Right Way, The Wrong Way and Your Way

There is no really wrong to setup or maintain your project. If you are developing and shipping apps then your setup is correct. Over the past 7 years certain trends have emerged as common expectations, but I have yet to see an official recommendations or guidelines from Apple so I’ll (and you) should keep doing it your way.

I would love to hear more of how you manage your project setup.

]]>
tag:blog.corywiles.com,2013:Post/750127 2014-10-02T16:27:39Z 2014-10-02T16:27:39Z Theo - An Open Source Neo4j Framework

Who?

Thomas Anderson is a computer programmer who maintains a double life as “Neo” the hacker. - Combination of Neo and Thomas

What?

Theo is an open-source framework written in Swift that provides an interface for interacting with Neo4j.

Why?

The why comes from multiple reasons.

1. Swift

Over the past few months I’ve been reading and writing some small utilities and getting my feet weight with Swift. I’ve found myself really enjoying the language and wanted to do something on a larger scale. This seemed like the next logical step for me to get a better grasp on the language and it’s possibilities. It didn’t disappoint in its challenges and rewards.

2. Neo4j

Neo4j is a highly scalable, robust (fully ACID) native graph database. Neo4j is used in mission-critical apps by thousands of leading startups, enterprises, and governments around the world. - http://neo4j.com/guides/about-neo4j/

The most common usage for graph’s is social data sets, but it should never be assumed that only social apps/services can use Neo4j. Business intelligence, content management, recommendation engines, and geo are just a few of areas of it superiority over traditional RDBMs. Customers and case studies.

My introduction to graph databases was from my good friends over at FatFractal. I saw the power and flexibility that leveraging this technology had on the variety of data that one could present. I had read a few tutorials about Neo4j, but never had the opprotunity to really dig deeper. As the need to show complex relationships in a straightforward and performant way for me grew I kept coming back to Neo4j and what it provided.

3. Challenges

Everything about the Theo was a challenge. Using a new language that has been officially 1.0 for ~month, using new Xcode features and wrapping that around Neo4j…a graph database that I’m a novice in. The rewards and excitement of using these technologies has been better than I could imagine. In all honesty I could have written the current version with Objective-C in about a quarter of the time, but I feel the greatest rewards come from the greatest challenges. Swift has a huge part in the future of iOS development and this was the perfect playground, if you will, to use it. The Neo4j API is interesting. In all honesty I don’t believe it is the most consistent API that I’ve used, but there is a ton of flexiblity in it.

  • If you look at the current version there is a lot of logic and modeling to deal with meta information. This was because in 2.1.4 the API kept a lot of it separate. However, yesterday, a maintenance release was pushed out, 2.1.5…the API now includes:

Adds additional metadata (label and ID) to node and relationship representations in JSON responses from the REST API.

KKHHHHHHAAAANNNNNN

I haven’t thrown out the “meta” related structs because of backwards compatibility, but please note that in future releases I will rely more on what the API returns from the nodes themselves as opposed to bridging the two.

4. I Like Working with Friends

Greg

programmer, usability hunter, potential lunch winner - love graph databases. I SAID LOVE THEM

When I worked at St. Jude Greg was my boss for 5 years. Over that time, and ever since, we’ve been good friends. For the past few years, between raising 3 kids with his wife and getting his Ph.D., he has been jetting setting (coach unfortuantely) across the country evagenlising Neo4j. As of a few months ago he started GraphStory and asked if I would like to write a Neo4j client for them. I couldn’t jump on that project fast enough.

Jeremy

PHP dev, motorcyclist, and amateur photographer, CTO/Lead Developer at http://GraphStory.com

When I first started at St. Jude the two main languages of use where Java 1.3 and ASP (shoot me now). Needless to say the Java version need a slight bump, which it got, but the .asp had to go. Our team ended up moving over to a LAMP stack, but that also meant a ton of migration of applications and random forms to PHP. I was working on a custom CMS so we hired Jeremy as a contractor. He was solid with “function” based PHP, but had only a little experience with OO. Over the next few months I showed him a few fundamentals and settled on Zend Framework (1.0) and the next thing I knew he was extending the framework and writing the basis for our in house library that is still in use today. After he left St. Jude he went on the found the MemphisPHP user group and speaking all over the country about PHP. Say what you will about PHP, but the community is pretty awesome and Jeremy has been a big part of that. Now that he is the CTO of GraphStory…I would listen to whatever that man has to say. He has earned his place.

Ready for Production?

The code that is on Github is very stable. I’ve written tests for all the functionalty up to this point. There are still some pieces of functionality that I want to add in, as well as, make the Client object a bit more user friendly. If you are interested in contributing, find a bug or ask a question please view the README for the best way to do that. I’ll take the library as version 1.0 in the next few weeks.

]]>
tag:blog.corywiles.com,2013:Post/737462 2014-09-06T20:44:16Z 2014-09-06T20:44:16Z Initial Experience with Swift

Is Swift worth it? What is it like to go to/from Objective-C and Swift? I wrote about my experience and thoughts regarding Apple’s new and exciting language over at Macspots.

]]>
tag:blog.corywiles.com,2013:Post/698210 2014-05-30T19:40:09Z 2014-05-30T19:40:10Z Crowdplace, MVP and Working on 1.1

Crowdplace

For the past few months I’ve been working on the iPhone app for [Crowdplace][http://www.crowdplace.com/]. In a nutshell what Crowdplace does is let you save your favorite content into a customized stream of useful information. Whether it is an RSS feed, Facebook posts, pictures, links, etc. is inconsequential…everything is considered content.

You can read more here

I was interested in working with the team for numerous reasons, but two of the biggest ones where:

  1. I got to work with my friend, and former boss from St. Jude, Greg Jordan
  2. Greg used Neo4j for backend storage.

MVP

When I took on the project the desktop browser client was much much further along than any iOS efforts. Though there wasn’t a hard “due date” to launch the app, there was a general timeframe. Unfortunately I didn’t have the time to get the app up to a 1:1 feature set with the desktop client during that time. I spent about a week going to through and make some pretty hard descisions about what would stay and what would have to wait. Those on the chopping block included features, as well as, some under the cover solutions. For example, there isn’t really robust caching happening outside of NSURLCache and on disk/memory caching for profile images.

Right now version 1.0 is undergoing testing from a variety of users. What I was able to deliever might be “missing” features, but what it does do it does well. I was able to squeeze in background fetch I spent as much time and effort as I could to make sure that the app was smooth and stable. I would rather release an initial version of an app that is feature limited than something that is clunky and crashes.

1.1

While version 1.0 is being tested I’ve already started the 1.1 branch and development is well underway. The first big sprint is syncing and caching using CoreData. Since CoreData will be the caching mechanism I’m having to do quite a bit of refactoring models, callbacks, etc., but I can already see the benefits of these efforts. While my focus for version 1.0 is to provide a stable and easy to use app, I’m even more excited about version 1.1 and the foundation that it will be built on.

]]>
tag:blog.corywiles.com,2013:Post/666736 2014-03-24T13:30:00Z 2014-03-23T00:30:08Z ZEMCircleProgressView

ZEMCircle Time

The inspiration for ZEMCircleProgressView was FFCircularProgressView I really liked it’s simplicity and was going to use it in a project but there were some design pattern decisions that bothered me, as well as, some features I needed like cancel and manual start.

My intention was to issue a pull request but by the time I got around to it the original project had changed (for the better) but had grown in complexity that I didn’t want. Huge thanks to Cory Hymel for taking the time to debug a timer issue for me!

Check it out and let me know if there are any bugs or features you would like to see.

]]>
tag:blog.corywiles.com,2013:Post/665893 2014-03-20T13:40:09Z 2014-03-20T13:40:09Z Asynchronous Image Loading for UITableViewCells

Simmons, Brent Simmons

Unless you do everything you possibly could to stay away from anything related to iOS development, then you’ve at a bare minimum stumpled upon at least one of Brent Simmons articles on the continued development of Vesper. I try to blog somewhat regularly, but the frequency tends decrease the more involved I am with a project. However, with Mr. Simmons, it seems to be the opposite and I’m inspired to do more of the same. Being a devout reader of his chronicals it has been comforting to know that a developer, such as himself, goes through many of the conumdrums of development decisions that myself and I’m sure many others do. In addition, I think that the fact a) Brent is willing to disclose so much information regarding the development b) The great responses from other developers on his approaches are exemplary of how great the iOS/Mac dev commnuity is. The fact is that iOS development is hard and no matter how good you are the answers aren’t always straightforward and there is always something new and interesting to learn.

Asynchronous Image Loading - Hasn’t This Already Been Solved

How many guitarist does it take to play Stairway to Heaven? 100…1 to actually change it and the other 99 to say, Not bad, but I could do it better

I’ve seen many different ways to handle effective asynchronous image dowloading, specifically in the context of UITableView’s. Most of the time this involves some variation of utilizing Grand Central Dispatch, which in this context isn’t very effecient for few reasons.

  1. Not a good way to manage the active queues
  2. You end putting in way too many references to the cell instance in UIScrollView delegate methods
  3. Your controller code becomes too large for it’s own good.

My Version

I’m not including any caching logic because that is a subject for another blog (debate)

Benefits

  1. Utilizes NSOperation and NSOperationQueues
    • You can have as many operations as you want in your cell subclass
  2. Reference to the cell instance via block param
  3. Using UITableViewDelegate methods to cancel any download operation for cells that aren’t onscreen

PhotoViewerCell.h

@class PhotoViewerCell;
@class Photo;

extern NSString * const  PhotoViewerCellIdentifier;

typedef PhotoViewerCell* (^PhotoViewerCellBlock)(void);

@interface PhotoViewerCell : UITableViewCell

@property (nonatomic, strong) Photo *photo;

- (void)loadPhotoDataForCell:(PhotoViewerCellBlock)currentCell
                   withPhotoInstance:(Photo *)aPhoto
                               queue:(NSOperationQueue *)queue;
- (void)cancelDownload;

PhotoViewerCell.m

#import "PhotoViewerCell.h"
#import "Photo.h"

NSString * const  PhotoViewerCellIdentifier = @"PhotoViewerCellIdentifier";

@interface PhotoViewerCell() 

@property (nonatomic, strong) UIImageView *imageView;
@property (nonatomic, weak) NSOperation *fullImageRequestOperation;

@end

@implementation PhotoViewerCell

- (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
  
  self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
  
  if (self) {
    
    _imageView = [[UIImageView alloc] initWithFrame:frame];
    
    _imageView.contentMode            = UIViewContentModeScaleAspectFit;
    _imageView.clipsToBounds          = YES;

    [self.contentView addSubview:_imageView];
  }

  return self;
}


#pragma mark - Public Methods

- (void)loadPhotoDataForCell:(PhotoViewerCellBlock)currentCell
                   withPhotoInstance:(Photo *)aPhoto
                               queue:(NSOperationQueue *)queue {
  
  NSOperation *fullImageBlockOperation = [NSBlockOperation blockOperationWithBlock:^{
    
    NSString *fullsizeImageUrl = [NSString stringWithFormat:@"%@%@/iPhoneFullsizeImage",
                                                 [FFManager sharedManager].ff.baseUrl, [aPhoto ms_ffUrl]];
    
    NSURL *downloadURL = [NSURL URLWithString:fullsizeImageUrl];
    NSData *imageData      = [NSData dataWithContentsOfURL:downloadURL];
    UIImage *scaledImage = [UIImage imageWithData:imageData scale:[UIScreen mainScreen].scale];
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
      
      PhotoViewerCell *originalCell = (id) currentCell();
      
      originalCell.imageView.image = scaledImage;
    }];
  }];

  self.fullImageRequestOperation = fullImageBlockOperation;
  
  [queue addOperation:self.fullImageRequestOperation];
  
  self.imageView.image = [UIImage imageNamed:@"placeholder"];
}

- (void)cancelDownload {

  if (self.fullImageRequestOperation) {
    MSLog(@"cancelled");
    [self.fullImageRequestOperation cancel];
  }
}

@end

ViewController Snippet


- (void)viewWillDisappear:(BOOL)animated {
  
  [super viewWillDisappear:animated];
  
  [self.imageDownloadQueue cancelAllOperations];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {

  PhotoViewerCell *cell = [tableView dequeueReusableCellWithIdentifier:PhotoViewerCellIdentifier
                                                           forIndexPath:indexPath];

  PhotoViewerCell * (^currentCellForBlock)(void) = ^PhotoViewerCell*{
    return (id) [tableView cellForRowAtIndexPath:indexPath];
  };
  
  Photo *currentPhoto = (Photo *)self.photos[indexPath.row];
  
  [cell loadPhotoDataForCell:currentCellForBlock
                        withPhoto:currentPhoto
                            queue:self.imageDownloadQueue];
  
  return cell;
}

- (void)tableView:(UITableView *)tableView didEndDisplayingCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
  
  PhotoViewerCell *currentCell = (PhotoViewerCell *)[tableView cellForRowAtIndexPath:indexPath];
  
  [currentCell cancelDownloads];
}

]]>
tag:blog.corywiles.com,2013:Post/663112 2014-03-12T18:16:43Z 2014-03-12T18:16:44Z Macspots - Progammatic Dynamic Cell Height Using AutoLayout


As a matter of practice I don't use Interface Builder or Storyboards. Notice I said practice and notprinciple. I find nothing morally wrong with using them, but I find myself doing so much UI customization that they get in the way ESPECIALLY when using AutoLayout.

I wrote a few words regarding creating dynamic cell heights using auto layout over at the MacSpots Blog.

]]>
tag:blog.corywiles.com,2013:Post/641592 2014-01-14T02:02:31Z 2014-01-14T02:02:31Z Macspots - The Intersection Between Science and Magic

Macspots

Over the past 6 months I’ve been working with my good friend Jason on revamping Gifts, as well as, collaborating on a some crazy ideas that we’ve talked about over the past few years. To provide more visibility and credenance to the work that we’re doing we’ve created a blog. Our innagural post was published today.

If you have a love/hate relationship with AutoLayout it might be of interest.

Stayed tuned for further snippets of code, lessons learned, and most importantly, amazing apps (at least we think so) over at Macspots…the intersection of science and magic.

]]>
tag:blog.corywiles.com,2013:Post/638495 2014-01-06T17:31:54Z 2015-11-19T21:28:30Z Using Dispatch Groups for Multiple Image Resizing

Three Queues are Better Than One

There are three use cases where I see that image resizing should happen on your phone.

  1. The resized version is the only only version that will be displayed in the app, i.e. Instragram.
  2. You want to do the user a favor and not try and send a full resolution image over the wire to your server. For example, lets say a user has or takes an image that is 2448 × 3264. You would have to have a compelling case for using the bandwhich needed for uploading that size image to resize it.
  3. Use case dictates the need for full resolution images.

Why You Should Have Multiple Sizes

Bandwidth, like death, is the debt that all people must pay for and there is a LOT of competition for this bandwidth (Netflix, Facebook, YouTube, etc). The strategy that I employee for dealing with photos in my apps are as follows.

Assuming their is functionality for some sort of a gallery

iPhone Only App

As stated previously there are rare exceptions where your app should be sending up full resolution images, so I generally resize the image to max of 1024 x 768. This will reduce the file size by more than half while still leaving plenty of pixels left over for proper resizing. On the server side of things the each photo object will have three, sometimes four, different sizes:

  1. Original (the 1024 x 768) version that was uploaded
  2. Fullsize display
  3. Thumbnail
  4. Preview (optional - depends on the UI)

Just as you should be concerned with bandwidth going out, you should be more concerned about content being downloaded. By requesting the size that you need, only when you need it, saves you CPU, GPU, memory, battery, etc. while providing your user with a much better experience. Throw in a sprinkle of FastImageCache and you’ll have happy users. These are not revolutionary concepts, but still basic principles that aren’t too many times overlooked.

iPad Only App

I follow the same basic approach as dealing with an iPhone only application, but I do generally send up the raw image, not because I’m lazy, but because more times than not iPads are used with Wifi so bandwidth, while still a consideration due to horrible public wifi connections, isn’t as much a concern. The one addition that I do for the iPad is make sure that there is some sort of offiline queue that will handle stop/starts of network connectivity and poor connections. NSURLSession makes this so much easier.

Universal App

There isn’t a hard and fast rule to go by, like the “iOnly” applications, but if the goal is to provide a seamless experience with interacting with photos, I do upload the full resolution image from both devices and let the server resize the images to the following sizes:

  1. Original version
  2. Fullsize for iPhone display
  3. Fullsize retina and non-retina for iPad
  4. Fullsize retina and non-retina for iPad
  5. Thumbnail for iPhone
  6. Thumbnail retina and non-retina for iPad

Universal apps take the most tweaking because of all the different use cases and business rules.

Best Laid Plans of Mice and Developers

It is an unfortuante reality for developers that best practices end up becoming good suggestions. For a new feature in GiftsHD2 we are adding in some photo features. Unfortunately, the resizing and resampling that I need to perform can’t be done on the server so my hand is forced. The tasks that need to happen in parallel are creating an “original image”, gallery display image and thumbnail image.

This is iPhone only for now

Group Dispatch

In order to provide the paralleism for the resizing and then a final push of all the images to the server I went with using dispatch_group_async.

Workflow

dispatch_group_async(__imageResizeDispatchGroup, __imageResizeDispatchQueue, ^{...resizedOriginal...});

dispatch_group_async(__imageResizeDispatchGroup, __imageResizeDispatchQueue, ^{...galleryDisplay...});

dispatch_group_async(__imageResizeDispatchGroup, __imageResizeDispatchQueue, ^{...thumbnail...});

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
dispatch_group_notify(__imageResizeDispatchGroup, globalQueue, ^{..save to backend});

IMPORTANT At first glance you might tempted to resize like this:

UIImage * (^resizePhotoForSize)(UIImage *, CGSize) = ^ UIImage* (UIImage *originalImage, CGSize newSize) {

  UIImage *resizedPhoto  = nil;

  UIGraphicsBeginImageContext(newSize);

  [originalImage drawInRect:CGRectMake(0, 0, newSize.width, newSize.height)];

  resizedCoverPhoto = UIGraphicsGetImageFromCurrentImageContext();

  UIGraphicsEndImageContext();

  return resizedCoverPhoto;
};

Don’t do that. Use the ImageIO framework. If you aren’t sure why then read Bill Dudney’s book on the subject. You’ll be happy you did.

Controller Snippet

Pros and Cons

The solution works great, but there are still some pitfalls that would probably best overcome by using NSOperations, but this does provide an example what can be done using dispatch groups.

]]>
tag:blog.corywiles.com,2013:Post/627433 2013-12-06T16:58:00Z 2013-12-09T14:38:15Z Migrating Data to FatFractal with Node.js

One of the long term goals that I have for an app I’m working on is to move the backend over to FatFractal. This was not a consideration that I made lightly. Moving from one backend system, no matter if it is homegrown or using BaaS, is a daunting task. Schemas, permissions, number of records, order of operations, migration scripts, etc. Is the juice worth the squeeze? In my case it is. Even after careful evaluation of the pros and cons of staying on the current provider or moving to FatFractal I threw together a proof of concept with sample data. Because the data structure paradigms are so different between the two platforms I wanted to see what the migration effort would be. How would I accommodate for exceptions in the model? What I mean by this is that even if the PoC was successful I knew that we wouldn’t do the migration for several months and that the current provider’s schema would be changed and I didn’t want to have to go back and make large scale changes to my import scripts. We are talking about >50,000 users and ~300,000 object records including static files.

Most of the BaaS providers that I’ve used allow for dynamic schema creation, but generally in the context origination not in migration. Parse does allow for [file importation of data]((http://blog.parse.com/2012/05/22/import-your-csv-data-to-parse/) . Stackmob has pretty cool python script for importing data directly from Parse, but the limitations are important to take note, especially the level of subobjects and large datasets.

Though FatFractal doesn’t have an official migration tool to/from a specific provider, setting up a migration is extremely easy using node.js and their server side js lib. An added benefit was being able to test everything locally, including a production migration run before I ran it against cloud migration.

Mystique is the sample project which has two components. First is the actual FatFractal application and second is the importer. Inside of the Importer directory there are two json files which would represent your data. Obviously you have a lot more when doing a migration of any significant size, but in this sample project I wanted to highlight the functionality that makes migration with FatFractal so easy. The big “aha!” in making all this work is in FatFractal’s Definition Language (FFDL) which allows for objects, collections and properties to be automatically determined at runtime. I don’t have to explicitly define any objects, properties or collections in the FFDL.

Post-migration you can explicitly add object definitions in the FFDL for what has been auto-generated at runtime during the import process… but it’s not required. The app will work perfectly well without your intervention.

You can find the project on GitHub.

]]>
tag:blog.corywiles.com,2013:Post/602989 2013-09-20T14:01:20Z 2013-10-08T17:30:19Z Open Source - The Ultimate Community Service

Open Source - A Discussion

Wednesday I spoke at the Chattanooga Technology Council’s luncheon on open source. It was geared toward the “culture” of open source and why it should be considered for larger companies that have traditionally avoided or ignored these as options.

OPEN SOURCE DEVELOPMENT IS ONE OF CHATTANOOGA’S FASTEST GROWING SKILL SETS. PARTLY BECAUSE CHATTANOOGA IS BECOMING A HOT BED FOR START-UP COMPANIES WHO CAN TAKE ADVANTAGE OF THESE FREE PROGRAMMING LANGUAGES, BUT LARGER COMPANIES ARE SEEING THE BENEFIT TOO. THE AUDIENCE WILL BE INTRODUCED TO OPEN SOURCE DEVELOPERS IN THE CHATTANOOGA AREA. THE DEVELOPERS WILL DISCUSS HOW THEY USE OPEN SOURCE TECHNOLOGIES, WHY THEY USE OPEN SOURCE. IF YOUR COMPANY IS USING OPEN SOURCE TECHNOLOGIES WE INVITE YOU TO COME AND MEET THESE DEVELOPERS IN PERSON.

Source: https://www.chattanoogatechnologycouncil.org/event/

I really enjoy these forums because of the town hall type enviornment that allows individuals to provide different insight to questions concerning a topic. My fellow panelists provide amazing thoughts, reflections and advice that is at the core of most open source communities.

The What

I was asked what I defined “open source” as. In the simpliest terms I could think of I defined it as both innovation and progress. I got a few laughs in the crowd, but I further elaborated that “open source” as a concept has been around for 1,000’s of years and without it we would be reinventing the wheel every week. Open source is knowledge. While many of the members in the crowd had never really used or contributed to what we know as open source projects I reminded them that libraries are filled with “open source” projects. Cookbooks, IMHO, are one of the earliest examples of open source repositories. Someone came up with an awesome receipe, published it, you bought, traded it, checked it out from the library, learned from it and more times than not tweaked the receipe for your needs and passed that on.

The follow up question was around how open source projects are benefitting apps or companies outside of startups. I love these types of questions. While 2 out of the 5 examples that I gave are startups that have HUGE user base. I first asked members of the audience who had heard or used Mailchimp. 80% raised their hands….I informed them that the Mailchimp iPad app uses DTCoreText/DTRichTextEditor written by Oliver Dropnick. Next example, who uses Pinterest apps, almost 100% raised their hands…informed them that AFNetworking, (written by Mattt Thompson), powers that app. Finally I pointed out that if you browse the web, buy anything off Amazon or stream any Netflix movies then you should thank the open source community. All of those products/services are built on open source software, and in most cases those companies open source many of their services or they contribute to existing projects.

So basically if you use a computer or browse the internet you have benefited from open source technology.

The Why

One of the final questions was, “Why open source?”. That question always makes smile a bit because it reminds me of the question, “What is the meaning of life?”. It depends, but it is generally ends up with something centered around happiness as it relates to that individual. Why someone should use or contribute to open source varies as much, but at it’s core (IMHO) it is the best example of community service devs participate in by evangelizing and contributing in projects and communities. Open source community participants are passionate about whatever project they are involved in. We don’t care that we aren’t paid. It is one of the last remaining aspects in any career were you do it purely for the “love of the game”. It is the perfect balance between intrensic and externsic reward.

My closing and unapologic remark on the why was because frankly these projects and solutions are better than many of the paid for vendor delievered solutions.

Hypocrite?

One of the best questions, and I expected it, was “Are you being a bit hyprocritiacal regarding open source considering you make your living off developing/supporting a closed system?” There is no question that iOS and MacOS are closed systems, but at their core is open source based technology, the language used for Cocoa/CocoaTouch IS open source and the that surrounds and supports these platforms is one of the largest and thriving that I’ve been apart of, especially considering that iOS has only been in extistance ~5 years.

Community Service

As stated by one of my fellow panelist, open source is the highest form of community service that anyone can participate in if you are in technology, but I would venture that you can apply that same attitude and opprotunity to any field. It is our obligation as technologist to set that example.

Go and play your small part in something big.

]]>
tag:blog.corywiles.com,2013:Post/587995 2013-07-10T04:26:17Z 2013-10-08T17:27:10Z Test Drive Evaluation

Test Driven Evaluation

To better explain what Test Drive Evaluation (TDE) is let’s first look at what the principle is based upon…Test Drive Development (TDD)

Source:

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or 'rediscovered' the technique, stated in 2003 that TDD encourages simple designs and inspires confidence.[1]

Test-driven development is related to the test-first programming concepts of extreme programming, begun in 1999,[2] but more recently has created more general interest in its own right.[3]

Programmers also apply the concept to improving and debugging legacy code developed with older techniques.[4]

Emphasis mine

Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle: first the developer writes an (initially failing) automated test case that defines a desired improvement or new function, then produces the minimum amount of code to pass that test, and finally refactors the new code to acceptable standards. Kent Beck, who is credited with having developed or ‘rediscovered’ the technique, stated in 2003 that TDD encourages simple designs and inspires confidence.

With the growth, popularity and increased acquisitions of BaaS providers it becomes harder and harder for newcomers to properly evaluate which one will provide the best value added to their application. Please note that I didn’t specify mobile application. While I personally subscribe to the “mobile first” approach…native desktop applications and traditional web applications shouldn’t/can’t be seen as second class citizens, thus BaaS should provide support for those as well.

Myself and many others have written on the pro’s / con’s of the top BaaS players. Unforunately, there hasn’t been a set of standard tests that could be run and give a definitive “pass or fail” result for the features that the providers offered. The KitchenSync project aims to provide the tests for these features.

Everything and the KitchenSync

It doesn’t matter if you’re a developer, project manager or analyst there is never enough time to implement every feature, RTFMs or write POC’s (when evaluating different vendors, technology, etc) for an application. We tend to cut corners, take for face value what is on the brochure or go with a solution just because it was featured as the latest/greatest startup on Techcruch.

For example:

  • What does it take implement object level security?
  • Does the BaaS provide static file / webhosting?
  • Which providers will allow full text searching?
  • How many API calls to it take perform request abc

What is the Best Option?

Antonio Martinez does a great job of summarizing:

Source: http://www.raywenderlich.com/20482/how-to-choose-the-best-backend-provider-for-your-ios-app-parse-vs-stackmob-vs-appcelerator-cloud-and-more

The most important factor here is to evaluate each service’s features and their usefulness to you — and your app! Preconfigured objects might seem attractive, unless your idea of what those objects should be differs from those of the service provider.

Services that offer just “users” along with general purpose objects might initially seem more difficult, until you see that substantial flexibility comes with the user-based approach.

Will you need a support agreement; and should you be paying for phone support? How accessible are the engineers if you need them, and what kind of user community support can you expect?

Take a close look at the documentation. This is one time you should read it thoroughly, even though the sample projects have given you enough to get started! :] The quality, coverage and currency of the documentation will give you a good idea of how committed each company is to your success.

Make yourself aware of the costs you will incur in using the service. This will require you to project your future usage for best case and worst case scenarios. Should you find yourself with 100 million users, will your app generate enough income to pay for the level of service you will require?

If your app is going to require a paid plan from the start but it flops, can you afford that? Some services require you to contact sales before you know what these costs will be; don’t skip this step!

Doing a search for the name of your service on discussion boards might expose interesting bits of information you might not have found otherwise. If you get no hits at all, how popular is the service — and what are the chances it will be around in six months? If all the hits say “it’s awesome” that might be a good indication – but pay attention to the negative comments as well!

You can arrive to most of the answers just by reading the documenation, but where the 1’s and 0’s meet the compiler is at implementation. Enter the KitchenSync project.

Even with my own evaluations and recommendations I wanted to give developers, analysts and just the curious, a set of common unit tests to run against each of the providers so that informed decisions could be made with the utmost transparency, while providiing a forum for feedback, additions and bug fixes.

All the tests are open for anyone to use for your own evaluations - just download and run them. In addition, as mentioned above, anyone is welcome to join the party - all enhancements, corrections and suggestions are welcome. There are a number of additional vendors as well - and I invite them, or any developers familiar with them to contribute implementations for them as well and I will be happy to add them to the report as time permits or I can get to them.

You Forgot About Feature ‘X’ and Provider ‘CDW’

No, I didn’t. There had to be a version 1.0 so I chose the providers that I’ve used (Parse, Stackmob, Kinvey and FatFractal) and a basic set of tests. I will be adding more tests just as quickly as I can write them, but I’m hoping for as many pull-requests as I can get. Specifically for new providers.

Disclaimer

I am not employed by any of the providers nor paid, bribed or coerced into writing this article or performing the tests nor is this an endorsement of my employer. I wrote it because…well…no one else had.

]]>
tag:blog.corywiles.com,2013:Post/583097 2013-06-07T18:44:54Z 2013-10-08T17:26:11Z Asynchronous Unit Tests for iOS

Handling Asynchronous Unit Tests

I’ve been following through on my goal of writing more unit tests for my iOS projects. Though there is definitely an upfront cost, the pay off is priceless. I’m by no means an expert on the subject, but with each test_ that is written I become that much more proficient.

If you are new to the subject checkout these articles

What I’m not a fan of is the repeated logic to handle asynchronous logic for my unit tests. Asynchronous code is much more common now due to GCD and blocks; unforunately it isn’t clear as to how to handle it via unit tests. My testing framework of choice is SenTest. There are many others, but I like you stock components and you get the setup for free when starting an Xcode project, but SenTest “assumes” that all the tests return synchronously. What this essentially means is that your tests will finish before your background executed code completes. #notgood

In order to synchronize your asynchronous code you need to interact with the currentRunLoop. By taking this approach you’ll essentially “pause” any other code execution until your block has finished and returned to the main thread.

DRY

With this block I can interact with the currentRunLoop and properly execute my asynchrous tests.

]]>
tag:blog.corywiles.com,2013:Post/579112 2013-05-15T13:28:28Z 2013-10-08T17:25:23Z Universal App Template Rewrite

Almost 3 Years Ago

Just when I thought I had a somewhat decent grasp on developing and deploying iOS apps Apple came out with the iPad and threw a whole new element in the mix when it comes to developing.

It was almost three years ago that I published the first blog about my Universal App Template. I was inspired by, at the time, a little tutorial by a guy you might have heard of…Ray Wenderlich). His tutorial really helped me with a project I was working on and get through stumbling blocks. I wanted to provide a set of examples that any iOS developer could refer to that was straightforward and get up and running with creating a universal app in as short of time as possible.

Over the following months I added additional features such as iAd support, tabbar controller and iAd with tabbar support. Since then it is still the most visited article and starred/forked repo that I have.

Bueller, Bueller…

Over the years that followed I got busy and wasn’t able to keep up with the iOS updates, new features requested or bugs. The project was quickly becoming outdated.

I’m happy to say that I’ve carved out sometime to update this project. The plan is to start with the master branch, which is the most basic example, and update it from the ground up. I’ll then move on to the “feature” branches. I’m dropping support for iOS 3.2, 4.x and 5.x. Due to the fact that the iOS upgrades happen so rapidly, thanks to OTA, and iOS 7 is being released in less than a month, this is the perfect time to hit the reset button. The project, as well as, feature branches, will support ARC. I’ve also spent some time cleaning up the code base and removed a lot of duplicated code.

If you still need access to the “old version” I’ve tagged it.

No developer is perfect and I’m sure there are tons of things to improve so please drop me a line or file a bug and I’ll fix it ASAP.

]]>
tag:blog.corywiles.com,2013:Post/564266 2013-04-30T17:36:49Z 2013-10-08T17:22:19Z NSValueTransformers: More Than Meets the Eye

Speed Dating

No matter the language dealing with calendars, dates and dateformatters are cumbersome. Even beginners with Cocoa/CocoaTouch learn very quickly that NSDateFormatters are expensive to create. There are various techniques that have been used over the years to improve the speed and efficiency of using NSDateFormatters.

  • Lazy loading
  • Retain the date formatter instance in your class as an attribute
  • Using NSThreadDictionary

No matter which one of these techniques I used I was never really satisified with the overall performance or their ease of use. I needed to be able to go back and forth between dates and their respective string representations in a non repeated manner. writing the same code/logic repeatedly

ISO Love NSValueTransformer

If you aren’t reading NSHipster then you are missing out on some great Objective-C gems. One of my favorites is @mattt’s post about [NSValueTransformer](http://nshipster.com/nsvaluetransformer/).

Of all the Foundation classes, NSValueTransformer is perhaps the one that fared the worst in the shift from OS X to iOS.

But you know what? NSValueTransformer is ripe for a comeback. With a little bit of re-tooling and some recontextualization, this blast from the past could be the next big thing in your application.

After reading through the TransformerKit and the examples, it dawned on me that I could solve my “date” problems using NSValueTransformer. Since TransformerKit already provides an interface that removed all the boilerplate setup for the subclasses I forked the project and got to work.

The 3 features that I wanted to provide were:

  1. The ability to set a default date formatter.
  2. The ability to cache all custom date formatters
  3. Provide thread safety

After a few hours of work I submitted a pull request.

Kiss the Date Goodbye

Request denied.

This is one of the main reasons why I love open source software. Matt not only took the time to point out where my logic was flawed, but offered a better abstract approach and then provided a better technical solution.

I was trying to be all things to all people with what the transformer would handle for you, but in the end transformers should be used to solve a particular issue.

Exposing them as class variables is a good API decision on that count, but introduces shared state to the point of not being particularly useful (e.g. wanting more than one at one time).

Though the pull request was denied, and rightfully so, a new addition was added that provides the functionality that I needed.

Your patch did remind me of one of the reasons I made this library to begin with: converting between ISO 8601 timestamps and NSDates. I’ve added these in c064303.

]]>
tag:blog.corywiles.com,2013:Post/357241 2013-04-08T17:53:16Z 2013-10-08T16:38:12Z Now Playing with Spotify and iTunes

When I started to finalize the features for the WhoNote agent app I knew I wanted to give users the ability to choose multiple music sources. Out of the gate the first two choices I wanted to support were iTunes and Spotify. Arguably the most common sources.

Coming from iOS development I had the naive assumption that there would an Cocoa equivelant of the MediaPlayer Framework. There isn’t. I became one sad panda. I saw quite a number of people “get around the problem” by writing AppleScripts and running them from the command line or including the AppleScript(s) in their apps and calling them directly. That would have worked, but there had a be a native way of interacting with iTunes. My use case is quite simple: Listen for track change notifications and upload meta data.

RTFM

When in doubt one should read the manual. Apple has provided an excellent example of how to natively interact with iTunes. Though the subject of ScriptBridging is much broader than just iTunes, the example couldn’t be better.

Needless to say incorporating iTunes support was a breeze.

Playtime with Spotify

Now that I had a native methodolgy to go by adding in Spotify support was just as easy. First I had to generate the header file:

sdef /Applications/Spotify.app | sdp -fh --basename SpotifyClient

Note

I did get the following error message when I ran the above command:

sdp: warning: class “SpotifyClientApplication” redeclared; use instead.

I’m not sure yet if this is bug in the sdp or if I didn’t pass in a correct param, but basically in your generated header file you’ll have a class forwarding delcaration twice.

@class SpotifyClientApplication, SpotifyClientTrack, SpotifyClientApplication;

Just open the file, remove one and you are good to go.

Being a Good Listener

Though I could interact with both services, the functionality that I needed was to listen for state and song changes. Both services “broadcast” this information through NSDistributedNotificationCenter.

iTunes Notification

com.apple.iTunes.playerInfo

2013-04-08 11:12:13.652 WhoNoteMac[7939:303] notification payload: {
Album = "Collide With the Sky";
"Album Artist" = "Pierce the Veil";
"Album Rating" = 0;
"Album Rating Computed" = 1;
Artist = "Pierce the Veil";
"Artwork Count" = 1;
"Disc Count" = 1;
"Disc Number" = 1;
Genre = Alternative;
"Library PersistentID" = 162261630986489880;
Location = "file://localhost/Users/cwiles/Music/iTunes/iTunes%20Media/Music/Pierce%20the%20Veil/Collide%20With%20the%20Sky/07%20Tangled%20In%20the%20Great%20Escape%20(feat.%20Jason%20Butler).m4a";
Name = "Tangled In the Great Escape (feat. Jason Butler)";
PersistentID = "-7678479651277138133";
"Play Count" = 1;
"Play Date" = "2013-04-08 14:24:50 +0000";
"Player State" = Playing;
"Playlist PersistentID" = 1335587615931680054;
"Rating Computed" = 1;
"Skip Count" = 0;
"Store URL" = "itms://itunes.com/album?p=536038964&i=536039373";
"Total Time" = 356893;
"Track Count" = 13;
"Track Number" = 7;
Year = 2012;
}

Spotify Notification

com.spotify.client.PlaybackStateChanged

2013-04-08 11:18:52.679 WhoNoteMac[8053:303] notification payload: {
Album = "In Search Of Solid Ground";
"Album Artist" = Saosin;
Artist = Saosin;
"Disc Number" = 1;
Duration = 215;
"Has Artwork" = 1;
Location = "/Users/cwiles/Music/iTunes/iTunes Media/Music/Saosin/In Search Of Solid Ground/08 The Worst Of Me.m4a";
Name = "The Worst Of Me";
"Play Count" = 0;
"Playback Position" = 0;
"Player State" = Playing;
Popularity = 38;
Starred = 0;
"Track ID" = "spotify:track:1wiIhmcIU1hJWqEKhcaXcp";
"Track Number" = 8;

}

Finally with all the pieces in places I was able to get all the meta information I need to send to the WhoNote backend. While I do wish there was an official Cocoa framework to interact with iTunes; utilizing ScriptBridging does provide a blessed mechanism for interacting with other installed applications.

Example Implementation

Gist of how it looks

]]>
tag:blog.corywiles.com,2013:Post/192896 2013-03-20T20:08:00Z 2016-08-27T17:37:34Z Photo Gallery with UICollectionView

UICollectionView of Awesomeness

I’m just going to say it. UICollectionView is the coolest, most useful addition that shipped with iOS6. The obvious solution to use this for is a grid layout, but that is just the tip of the iceberg. You can transform your views to use a circular layout or cover flow style layout, etc. They are extremely configurable, flexible and extensible.

UICollectionView and UICollectionViewController are the perfect balance between UITableView, UITableViewController, UIScrollView and UIPageController.

There are just as many “photo gallery” libraries out there as there apps that use them. Unforunately, I never found one that I was really happy with. I’ve developed a few of my own, but I wasn’t helped with them either. At times they were too complicated and others they weren’t extensive enough.

UICollectionView of awesomeness to the rescue.

For an great introduction to UICollectionView go and checkout this tutorial on Ray Wenderlich’s site.

Recreating iPhone Photo App

The Introducing Collection Views session at last year’s WWDC was absolutely amazing and really showed the infinite possibilities of using UICollectionView to showcase data in fascinating ways. In my opinion, there is no better way to learn something than to actual use it. So I set out to recreate the native iPhone photos app.

Aside from dealing with some of the headaches with the AssetsLibrary library, laying out a grid and the pagination of the large images was elementary. Though this a very simplicstic example I think it demonstrates how quick and efficient these new classes provide. I’ve used this same project as the basis for loading in remote photo datasources and works just as beautifully.

Bryan Hansen has a great tutorial a custom gallery as well.

CWPhotoViewer on Github

]]>
tag:blog.corywiles.com,2013:Post/192897 2013-02-21T19:41:00Z 2013-10-08T16:02:31Z Managing Urban Airship Configuration

Pre-flight Checklist

It has been awhile since I’ve used Urban Airship. The last time was before their SimpleGeo acquistiation. However, I’ve been following their progress over the years and admired the hardwork and features that are offered. I’m in the final development stages for EPB’s iOS app and need to incorporate push notifications. UAS was the clear choice.

When I was going through their getting started tutorials there was one aspect that bothered me. There’s an assumption, really a requirement, that you put your applications key/master credentials into a file called AirshipConfig.plist. I didn’t pay too much attention to this when I was using their example, but when it came time to incorporate the library in the app I didn’t like the setup.

Reworking the Flight Plan

I decided that it made better since to break out the sampled AirshipConfig.plist into a Debug and Release versions. I ended up modifying their checkConfig.sh script to look at the current Xcode configuration and copy the appropriate plist as AirshipConfig.plist and add it as Build Phase for the project: `“$SRCROOT/Scripts/checkUASConfig.sh”

Now the appriopate settings are provide to the library without having to keep everything in one config that has to have runtime checks and extract the necessary values.

Final Approach

I keep my plist in a Configurations directory at the toplevel of the $SRCROOT

https://gist.github.com/kwylez/5007475

]]>
tag:blog.corywiles.com,2013:Post/192898 2013-02-07T00:07:00Z 2013-10-08T16:02:31Z 11995 Minutes of Development

5 Mins is the New 2 Weeks

When I first started my professional corporate development career it seemed that when I sat down with a project manager to go over my estimates, any sprint or milestone that was over two weeks was too long. For reasons, still unbeknownst to me, managers/executives thought that any small project shouldn’t take more than 2 weeks and large projects should only have milestones that didn’t exceed two weeks.

I have longed since moved on from places like that and refuse to be apart of the madness ever again

The previous articles I’ve written about PaaS/BaaS providers highlight that you can get up and running in as little as 5 mins. Most developers will think to themselves:

WOW! I have this really great app idea and can’t wait to get started, but developing will take me weeks if not months. I’ll use provider ABC and be in immersed in my UITableViewControllers by minute 6.

This statement is pretty close to true. 5 mins has become the new standard for the maximum amount of time it should take to get “up and running” with a PaaS/BaaS.

The Other 11995 Minutes

One of the apps I released last year I spent ~200hrs coding (12,000 mins). Getting setup with one of the providers took the advertised 5 mins, but what about the other 11995 minutes…well that was different story. It wasn’t a complete disaster, but as I got deeper into project I hit the maxed out threshold of the features offered. Object relationships, querying, persistance etc. The support I got was extremely responsive. Their developers and I worked on various solutions to my issues, but they couldn’t change the nature of the platform. I had two choices:

  1. Create a hacky workaround
  2. Move off the service

Moving off service wasn’t a realistic option because of the time and effort. It wasn’t like I was using my own service layer classes or model objects. I was locked in to using their object-types. I ended up coming with a nasty hack to get part of what I needed done.

Considerations

Does the backend have the power/flexibility that allows you do the things that makes your app really cool? This is what deserves the considerations.

The Frontend Shouldn’t Drive Your Backend

Convienence is a funny thing. The instant gratification comes a price. Sometimes that price is worth paying, but there are no shortcuts with development. Sooner or later the “coding gods” must have their sacrifice. Having Login/SignupViewControllers is extremely convenient, but that shouldn’t be a deciding factor in selecting which BaaS provider that you go with. The amount of time saved by not having to write a login/signup viewcontroller is lost exponetially by having to write mounds and mounds of client-side code to manage your actual objects.

It Goes on the Server

WIth every product release cycle, it doesn’t matter the company, mobile devices get more RAM, CPU and disk space, but these resources are still limited and have to be shared across, not only your apps, but the system itself. There has been an unforunate trend that has been accepted as “best practice” in that the client (your phone) is executing tasks that should be done on the server.

Source: https://parse.com/tutorials/anypic#model

- (BOOL)shouldUploadImage:(UIImage *)anImage { 
  // Resize the image to be square (what is shown in the preview)  
  UIImage *resizedImage = [anImage resizedImageWithContentMode:UIViewContentModeScaleAspectFit 
                                            bounds:CGSizeMake(560.0f, 560.0f) 
                            interpolationQuality:kCGInterpolationHigh];
  // Create a thumbnail and add a corner radius for use in table views
  UIImage *thumbnailImage = [anImage thumbnailImage:86.0f
                                  transparentBorder:0.0f
                                       cornerRadius:10.0f             
                               interpolationQuality:kCGInterpolationDefault];

  // Get an NSData representation of our images. We use JPEG for the larger image
  // for better compression and PNG for the thumbnail to keep the corner radius transparency
  NSData *imageData = UIImageJPEGRepresentation(resizedImage, 0.8f);
  NSData *thumbnailImageData = UIImageJPEGRepresentation(thumbnailImage, 0.8f);

  if (!imageData || !thumbnailImageData) {    
      return NO;
  }
}

A common use case in illustrating this point is handling image resizing after a user takes a picture with the camera. Let’s take a look at how Parse handles this scenario

This is to improve performance in the activity feed table view where several thumbnail images are displayed simultaneously. As we’ll see in Section 3, the resizing is done on the client side.

While the amount of code written isn’t too much, the device resources that are utilized are done so unecessarily. Most image processing tasks are for the server. By deferring the image manipulation to server you allow yourself for greater flexibility in time to process, future enhancements/tweaks to the processing itself, and you have a much greater pool of resources at your disposal. Finally, there is the tanglible financial savings of not having repeated calls to the API itself.

Client code should be small, fast and focused on UI. Frequently the libraries required for this kind of operation can bloat your app ridiculous amounts.

Let’s look at how this is done with FatFractal:

Server Side Code

var ff  = require('ffef/FatFractal');
var io  = require ('io'); // standard CommonJS module
var fs  = require ('fs'); // standard CommonJS module
var bin = require ('binary'); // standard CommonJS module
var hc  = require ('ringo/httpclient'); // not standardised by CommonJS yet, hence ringo prefix. see http://ringojs.org

var Scalr   = Packages.org.imgscalr.Scalr; // import the Scalr Java package
var ImageIO = Packages.javax.imageio.ImageIO; // import the imageIo Java packages

function WNTimelinePhoto() {

  this.clazz = "WNTimelinePhoto";

  this.countForUser  = 0;
  this.photoInstance = null;

  return this;
}

function resizeImage(picBytes, width, height) {

  var croppedBytes = null;

  /**
   * We need a BufferedImage for the Scalr processing
   * There are ImageIO read methods for InputStream, File and URL. We've got a
   * ByteArray. So let's create a ByteArrayInputStream.
   */

  try {

    var bais = new java.io.ByteArrayInputStream(picBytes);
    var img  = ImageIO.read(bais);

    /**
     * Crop the picture
     */

    var cropped = Scalr.crop(img, width, height);
    var baos    = new java.io.ByteArrayOutputStream();

    ImageIO.write (cropped, 'JPG', baos);

    /**
     * Get the bytes from the ByteArrayOutputStream
     */

    croppedBytes = new bin.ByteArray(baos.toByteArray());

  } catch (e) {}

  return croppedBytes;
}

exports.resizeImages = function () {

  var data                  = ff.getExtensionRequestData();
  var r                        = ff.response();
  var evnt                  = null;
  var finalResponse = '';
  var usr                    = null;
  var photoInstance = null;

  if (data.httpMethod.toLowerCase() !== 'post') {

    r.responseCode  = "405";
    r.statusMessage = "You must use POST method";
    r.mimeType      = "application/json";
    r.result        = JSON.stringify("REJECTED");
    return;
  }

  photoInstance = ff.getObjFromUri(data.httpContent.photoresource);

  if (photoInstance === null) {

    r.responseCode  = "400";
    r.statusMessage = "Photo Instance Error";
    r.mimeType      = "application/json";
    r.result        = JSON.stringify("The photo instance can't be null: (photo ffurl) " + photoInstance.ffUrl);

    return;
  }

  /**
   * Check and make sure the current user owns the image
   */

  if (data.ffUser !== photoInstance.createdBy) {

    r.responseCode  = "403";
    r.statusMessage = "Don't Think So";
    r.mimeType         = "application/json";
    r.result                  = JSON.stringify("Not Authorized");

    return;
  }

  originalImage  =  ff.getBlob("image", photoInstance);

  if (originalImage === null) {

    r.responseCode  = "400";
    r.statusMessage = "Image Request Failure";
    r.mimeType      = "application/json";
    r.result        = JSON.stringify("Image information can't be null: (photo ffurl) " + photoInstance.ffUrl);

    return;
  }

  previewImage   = resizeImage(originalImage, 280, 75);

  if (previewImage === null) {

    r.responseCode  = "400";
    r.statusMessage = "Preview Resize Failed";
    r.mimeType      = "application/json";
    r.result        = JSON.stringify("Preview information can't be null: (photo ffurl) " + photoInstance.ffUrl);

    return;
  }

  thumbnailImage = resizeImage(originalImage, 100, 100);

  if (thumbnailImage === null) {

    r.responseCode  = "400";
    r.statusMessage = "Thumbnail Resize Failed";
    r.mimeType      = "application/json";
    r.result        = JSON.stringify("Thumbnail information can't be null: (photo ffurl) " + photoInstance.ffUrl);

    return;
  }

  ff.saveBlob(photoInstance, 'previewImage', previewImage, 'image/jpeg');
  ff.saveBlob(photoInstance, 'thumbnailImage', thumbnailImage, 'image/jpeg');

  finalResponse = {"instance" : photoInstance, "raw" : data.ffUser};

  r.responseCode  = "200";
  r.statusMessage = "Meta Information";
  r.mimeType      = "application/json";
  r.result        = JSON.stringify(finalResponse);
};

Client

I defined an POST event handler in my FatFractal FFDL file to be executed after the object is created

- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {

  [self dismissViewControllerAnimated:YES completion:nil];

  [MBProgressHUD showHUDAddedTo:self.view animated:YES];

  UIImage *anImage = [info valueForKey:UIImagePickerControllerEditedImage];

  if (anImage) {

    NSData *imageData = UIImageJPEGRepresentation(anImage, 0.2);

    WNPhoto *photo = [[WNPhoto alloc] init];

    photo.event = self.currentEvent;
    photo.image = imageData;

    [self.ffMgr.ff createObj:photo
                       atUri:userPhotosUri
                  onComplete:^(NSError *err, id obj, NSHTTPURLResponse *httpResponse) {

                    if (err) {

                      WNLog(@"error saving: %@", err);

                      [ADVAlertView showStandardAlert:@"Error Posting Image"
                                          withMessage:[err localizedDescription]];
                    } else {
                     [ADVAlertView showStandardAlert:@“Success”
                                          withMessage:@“Image upload is complete”];
                    }
                  }];
  } else {

    [MBProgressHUD hideHUDForView:self.view animated:YES];

    [ADVAlertView showStandardAlert:NSLocalizedString(@"Error Saving Image", nil)
                        withMessage:NSLocalizedString(@"There was an error saving your picture", nil)];
  }
}

With FatFractal, I’m able to create as many sizes/formats as I want based upon my model without changing any client code. I’m able to set the permissions, specify the types of images I want (PNG, JPEG, etc) in a consistant manner that is scaled and in theory unlimited resources. I have ensured that my data is consistent and complete because it is handled on the backend.

Regardless of the client: desktop browser, mobile browser, Windows mobile, iOS or Android the client code should stay the same, take a picture and upload, and all get consistent image processing results. Often implementing such features is very different on the various devices, writing once saves time and probably a lot of headaches.

Do You Speak Other Languages (Idiomas, Sprog, Lingue)

With any new technology there is always a learning curve. What is best for the developer is when that technology provides you with lots of options. Most developers tend to work on more than one project at time and require more than one language for those projects. This becomes a real pain when you have to have separate accounts to manage from various BaaS providers (because most only offer one, maybe two languages) or worse you have to setup yourself different development enviornments that support each of the various languages. Why shouldn’t you be able to mix and match code from different languages using the same provider.

FatFractal supports all of this out of the box. No additional modules to request or add-on. They are available to through the engine itself. You can mix/match server-side js with Ruby or Rails or Servlets without the headaches of setting up gems, bundler or java class paths. If Ruby is the better choice for your “Movie” model event handlers and server-side js is better for the “Theater” model event handlers/extensions then you make the choice….not the backend provider. A few releases later if you want to write everything with servlets then go ahead. The backend is agnostic and your client code doesn’t change one bit.

FatFractal – Picking the Lock(in)

In my experiences and writing about FatFractal I have championed some of their unique features:

but the one that I haven’t talked about is the fact that you aren’t locked in so tight to their services that you can’t leave it or use it in pieces and parts. For BaaS to become mainstream, it is imperative that the players make it really easy to move to another service. Dalton Caldwell, from App.net, illustrates…though indirectly, what others have done wrong and what FatFractal has been doing from day one.

Imagine a world in which your social data (e.g. messages, photos, videos) was easier to work with. For instance, imagine you could try out a new photo sharing service without having to move all of your photos and social graph.

In this world, your photos are held in a data store controlled by you. If you want to try out a new service, you can seamlessly login and choose to give permission to that service, and the photos that you have granted access to would be immediately available.

This is one benefit of an “unbundled” social service. Unbundling gives the user power to pick the software that best suits their needs, rather than being forced to use the software made by the company that manages their data.

Source: http://blog.app.net/2013/01/28/announcing-the-app-net-file-api/

FatFractal doesn’t lock you into any particular client side technology for your models (HashMaps, Dictionaries, CoreData) nor server side language or technology. You can use their service as a full stack or you could use it, for example, as an extension to your Heroku app.

Using the image resizing example above, let’s assume you have an existing Rails app that you are happy with, but ImageMagick isn’t performing as well as you want and you want to offload the resizing functionality asynchronously, but be notified when it is complete. You could create a simple server extension with FatFractal and accomplish all of that without having to worry about an extened amount of time coding the service layer, security or scalling…no properitary libraries or syntax to write except for the serverside js FF library, but that is for fetching/saving collections from your backend. If you would rather write it in Ruby/Python/Java, etc. well that is your choice. It is “unbundled”.

Parse has done a great job with their 3rd party integrations, but developers rarely use only the functionality out of the box. They want to extend or write something custom. FatFractal is the only BaaS/PaaS that provides what you need to accomplish everything that you need. It is the balance between OOTB features while leveraging custom code, all the well, trying to not reinvent the wheel with every application.

FatFractal: providing an unbundle platform for the other 11995 minutes of development.

]]>