Categories
Current Events Tampa Bay Uncategorized

What’s happening in the Tampa Bay tech scene (Week of Monday, July 10, 2017)

Every week, I compile a list of events for developers, technologists, and tech entrepreneurs in and around the Tampa Bay area. We’ve got a lot of events going on this week, and here they are!

Monday, July 10

Tuesday, July 11

Wednesday, July 12

Thursday, July 13

Friday, July 14

Saturday, July 15

Categories
Current Events Tampa Bay Uncategorized

What’s happening in the Tampa Bay tech scene (Week of Monday, July 3, 2017)

Every week, I compile a list of events for developers, technologists, and tech entrepreneurs in and around the Tampa Bay area. We’ve got a lot of events going on this week, and here they are!

Monday, July 3

Tuesday, July 4

Wednesday, July 5

Thursday, July 6

Friday, July 7

Categories
Uncategorized

It’s not the “Streisand Effect” anymore; it’s the “Zillow Effect” now!

The image of Streisand’s Malibu house that led to the naming of the Streisand Effect.
Creative Commons photo. Click to see the source.

When the humorless real estate media company Zillow sent student Kate Wagner a cease-and-desist nastygram over her hilarious architecture critic site McMansion Hell, they probably didn’t realize that they would become the new textbook example of the Streisand Effect, and that they’d lose that battle before it even began.

If you’ve noticed how tacky large suburban house architecture has become in the past couple of decades, McMansion Hell is for you. It’s a blog that features hilarious critiques and takedowns of house design and decor that are promoted as good, but in fact are so bad that they’re BAD (a concept put forth in Paul Fussell’s excellent book — and these days, a timely one too: BAD, or the Dumbing of America.

McMansion Hell used photos of terrible large suburban houses from Zillow, which is fair use, but probably embarrassing for Zillow. That’s why they sent their nastygram, in an attempt to scare Kate into shutting down her site:

Oddly enough, Zillow didn’t even own those photos, which made their claims even more bogus.

Thankfully, the EFF stepped in. Their lawyers talked to Zillow’s lawyers, and Zillow backed down, issuing these mealy-mouthed statements:

“We have decided not to pursue any legal action against Kate Wagner and McMansion Hell.We’ve had a lot of conversations about this, including with attorneys from the EFF, whose advocacy and work we respect. EFF has stated that McMansion Hell won’t use photos from Zillow moving forward. It was never our intent for McMansion Hell to shut down, or for this to appear as an attack on Kate’s freedom of expression. We acted out of an abundance of caution to protect our partners — the agents and brokers who entrust us to display photos of their clients’ homes.”

It may not have been meant to appear as an attack on Kate’s freedom of expression, but tell the truth, Zillow: it was most certainly your intent for McMansion Hell to shut down.

And now Zillow look like fools:

Remember, the Streisand Effect was the result of trying to suppress pictures of a mansion, which is pretty much what Zillow were trying to do for many more houses. We should call it the Zillow Effect now.

Categories
Uncategorized

See all the presentations and get the code from Xamarin Dev Days 2017

A couple of Saturdays ago, I helped out at the Tampa edition of Xamarin Dev Days, a full-day event where attendees found out more about Xamarin, Microsoft’s cross-platform mobile-and-more development tool. saw development demos, and participated in an afternoon workshop where they built their first Xamarin app.

If you missed the event, you haven’t missed the opportunity to learn! The presentations we gave were local versions of the official presentations, which you can check out below.

Get Xamarin

Xamarin comes built into Visual Studio, which you can get for free for either Windows or Mac. I did my demos on my Mac, while my fellow presenters did theirs on their Windows machines.

Get the demo code

The code for all the Xamarin Dev Days demos and the workshop, complete with instructions, lives on Xamarin’s GitHub account.

Building Xamarin native apps

Introduction to Xamarin

My presentation at the recent Xamarin Dev Days event in Tampa was modeled after the official “Introduction to Xamarin” one, shown below. This demo walks you through the process of building an image search app with Xamarin for both Android and iOS the “native” way, where the solution has three distinct parts:

  • A common base of application logic code that both the Android and iOS versions use,
  • Code for the Android UI, and
  • Code for the iOS UI.

This is a “best of both worlds” approach, where you get the benefits of “write once, run on all your target platforms” with the application logic part of your app, and the “native look and feel” benefits of having Android- and iOS-specific UI code. The downside of the “native” approach is that familiarity with C# and the .NET framework isn’t enough; you have to also be familiar with the libraries and techniques used in Android and iOS programming.

Xamarin’s plugins provide a way to deal with platform-specific coding through abstraction. The demo above uses a couple of plugins to provide cross-platform functions to check for network connectivity and display alert boxes, tasks that you’d normally have to perform separately for Android and iOS.

You may also find these videos useful:

Intro to Xamarin for Visual Studio: Native iOS, Android, and Windows Apps in C#

Building Your First Android App with Xamarin for Visual Studio

Building Your First Android App with Xamarin for Visual Studio

Cross-platform UI with Xamarin.Forms

Russ Fustino gave the Xamarin.Forms presentation at Xamarin Dev Days. Xamarin.Forms provides a way to write a single application that runs on all the platforms that Xamarin targets by having you code to a single UI, which then gets rendered using each platform’s native UI elements and “look and feel”:

Here’s the presentation that was the basis for the one Russ gave:

You may also find these videos useful:

Building Your First Xamarin.Forms App with Xamarin for Visual Studio

Customizing Xamarin.Forms UI

Xamarin and Azure

Greg Leonardo gave his version of this presentation, in which you build you first cloud-connected app using both Xamarin and Azure:

You may also find this video useful:

Connected Mobile Apps with Microsoft Azure

Categories
Uncategorized

The code for Tampa iOS Meetup’s “Pomodoro Timer” exercise

Last night, the Tampa iOS Meetup folks gathered to learn how to code an iPhone/iPad Pomodoro Timer in Swift, a timer that helps you use the Pomodoro technique, an ingeniously simple lifehack that many people have used to help them power past distraction, stay focused on their work, and be incredibly productive.

Pomodoro is the Italian word for “tomato” and refers to the tomato-shaped kitchen timer that the technique’s inventor, Francesco Cirillo, used in developing the technique. The technique itself is pretty simple:

  • Pick a task that you want to tackle.
  • Set a timer for 25 minutes. Because Cirillo used a tomato-shaped kitchen timer, he called this 25-minute interval a “pomodoro”.
  • Work on the task — and nothing but that task — until the timer rings.
  • After the timer rings, put a checkmark on a piece of paper.
  • If you have fewer than four checkmarks, take a five-minute break, then start another pomodoro.
  • If you have four checkmarks, take a longer break — typically 15 to 30 minutes — and reset your checkmark count back to zero. Then start a new pomodoro.

By breaking a large task or series of tasks into short, focused intervals, the Pomodoro Technique aims to have your brain to work in short sprints — which it’s evolved to do — and take regular breaks to help it recharge. The intended result is to ensure consistent productivity, motivation, and creativity.

The app we built last night is a timer for use with the Pomodoro technique that keeps track of pomodori (that’s the plural of pomodoro) and breaks.

The heart of the app

The key to writing an app like the Pomodoro timer is the Timer class. It can be used to instantiate timer objects, which:

  • Wait until a certain interval of time has passed, and when that happens,
  • Sends a message to a target object.

In the Pomodoro Timer app, we create a Timer object that fires every second. When it fires, it calls a method that:

  • Reduces the count of time remaining by one second
  • Updates a “time remaining” display
  • Checks to see if time has run out on the current interval and takes the appropriate actions

Our app has an instance variable called myTimer. To start a timer, we use this method:

myTimer = Timer.scheduledTimer(timeInterval: 1,
                               target: self,
                               selector: #selector(timerTick),
                               userInfo: nil,
                               repeats: true)

Here’s a quick explanation of the scheduledTimer method’s parameters:

  • timeInterval: The number of seconds to wait before the timer fires. We want the timer to fire every second, so we set this parameter to 1.
  • target: The object to send the message to when the timer fires. We want the message to be sent to the same object that this call lives in, so we set this parameter to self.
  • selector: The message we want to send to the target object when the timer fires. We want to call the timerTick method every time the timer fires, so we do this by setting the parameter to #selector(timerTick), which says “send a call to the timerTick method”.
  • userInfo: This is used to pass any additional information that might be needed when the timer fires. In this case, we don’t need any such additional information, so we set this parameter to nil.
  • repeats: If true, the timer is a repeating timer that fires once every timeInterval seconds. If false, the timer waits timeInterval seconds, fires once, and then stops. We want the timer in our app to be a repeating one, so we set this parameter to true.

Stopping a timer is pretty simple. If the timer instance variable is myTimer, here’s how you stop it:

myTimer.invalidate()

A couple of handy utility methods

The app keeps track of time by counting down from a given total number of seconds:

  • During a pomodoro, it starts a countdown timer of 25 minutes, which is 1500 seconds.
  • During a rest break, it starts a countdown timer of 5 minutes, which is 300 seconds.

This works just fine for internal code, but people don’t tell time in terms of hundreds of seconds, but in terms of hours, minutes, and seconds (we have the Babylonians to thank for this). So we use a method that converts a given number of seconds into minutes and seconds…

// Given a number of seconds, return it as (minutes, seconds).
func minutesAndSeconds(from seconds: Int) -> (Int, Int) {
  return (seconds / 60, seconds % 60)
}

We also need a method to display seconds (and optionally, minutes) as a two-digit number with a leading zero. That way, the app displays “one minute and three seconds” as 01:03 (or 1:03) and not 1:3. Here’s the method:

// Given a number, return it as a string of 2 digits,
// with a leading zero if necessary.
func formatMinuteOrSecond(_ number: Int) -> String {
  return String(format: "%02d", number)
}

…and we use both these methods like so:

let (minutes, seconds) = minutesAndSeconds(from: timeRemaining)
minutesLabel.text = formatMinuteOrSecond(minutes)
secondsLabel.text = formatMinuteOrSecond(seconds)

The code

This was the simplest app we’ll cover this year, and the code for the entire app lives in the view controller. Here it is:

import UIKit

class ViewController: UIViewController {

  // This app is a timer for use with the Pomodoro Technique.
  // (See https://en.wikipedia.org/wiki/Pomodoro_Technique for details.)

  // Intervals
  // ---------
  // The Pomodoro technique has two types of time intervals:
  //   1. The pomodoro, where you focus on a specific task for 25 minutes, and
  //   2. A rest break of 5 minutes.
  // These variables make it possible to tell what interval we're currently on,
  // and whether it’s a pomodoro or a rest break.

  enum IntervalType {
    case Pomodoro
    case RestBreak
  }
  let intervals: [IntervalType] = [.Pomodoro,
                                   .RestBreak,
                                   .Pomodoro,
                                   .RestBreak,
                                   .Pomodoro,
                                   .RestBreak,
                                   .Pomodoro]
  var currentInterval = 0

  // Interval lengths and time remaining
  // -----------------------------------
  // For testing purposes, I made the lengths of the pomodoro and rest break intervals
  // 20 seconds and 5 seconds, respectively. For the actual versions, use the
  // commented lengths.
  let pomodoroIntervalTime = 20 // Actual length: 25 * 60
  let restBreakIntervalTime = 5 // Actual length:  5 * 60
  var timeRemaining = 0

  // Timer
  // -----
  // The heart of the app.
  var myTimer = Timer()

  // UI controls
  // -----------
  @IBOutlet weak var minutesLabel: UILabel!
  @IBOutlet weak var secondsLabel: UILabel!
  @IBOutlet weak var intervalLabel: UILabel!
  @IBOutlet var tomatoIcons: [UIImageView]!
  @IBOutlet weak var startPauseButton: UIButton!
  @IBOutlet weak var resetButton: UIButton!


  override func viewDidLoad() {
    super.viewDidLoad()

    resetToBeginning()
  }

  override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
  }

  func resetToBeginning() {
    currentInterval = 0
    setTomatoMeter(to: 1)
    intervalLabel.text = "Ready to work!"
    startPauseButton.setTitle("Start", for: .normal)
    resetButton.isEnabled = false
    timeRemaining = pomodoroIntervalTime
    updateDisplay()
  }

  @IBAction func startPauseButtonPressed(_ sender: UIButton) {
    if myTimer.isValid {
      // If the timer is currently running:
      //   1. Change the button’s title to “Resume”
      //   2. Enable the rest button
      //   3. Pause the timer
      startPauseButton.setTitle("Resume", for: .normal)
      resetButton.isEnabled = true
      pauseTimer()
    } else {
      // If the timer is currently stopped:
      //   1. Change the button’s title to “Pause”
      //   2. Disable the Reset button
      startPauseButton.setTitle("Pause", for: .normal)
      resetButton.isEnabled = false
      if currentInterval == 0 && timeRemaining == pomodoroIntervalTime {
        // If we’re currently at the very start of a set of pomodori (plural for pomodoro),
        // begin the cycle of intervals.
        startNextInterval()
      } else {
        // If we're in the middle of a set of pomodori,
        // simply resume the timer.
        startTimer()
      }
    }
  }

  @IBAction func resetButtonPressed(_ sender: UIButton) {
    if myTimer.isValid {
      // The timer shouldn’t be running if the Reset button is enabled,
      // but let’s make sure it’s stopped, just in case.
      myTimer.invalidate()
    }
    resetToBeginning()
  }

  func startNextInterval() {
    if currentInterval < intervals.count {
      // If we haven’t done all the intervals yet,
      // do the next one.
      if intervals[currentInterval] == .Pomodoro {
        // Pomodoro interval
        timeRemaining = pomodoroIntervalTime
        intervalLabel.text = "Pomodoro!"
        let tomatoes = (currentInterval + 2) / 2
        print("\(tomatoes) tomatoes")
        setTomatoMeter(to: tomatoes)
      } else {
        // Rest break interval
        timeRemaining = restBreakIntervalTime
        intervalLabel.text = "Rest break."
      }
      updateDisplay()
      startTimer()
      currentInterval += 1
    } else {
      // If we’ve done all the intervals,
      // reset the app.
      resetToBeginning()
    }
  }

  func updateDisplay() {
    let (minutes, seconds) = minutesAndSeconds(from: timeRemaining)
    minutesLabel.text = formatMinuteOrSecond(minutes)
    secondsLabel.text = formatMinuteOrSecond(seconds)
  }

  // Start the timer, which will call the timerTick() method every second.
  func startTimer() {
    myTimer = Timer.scheduledTimer(timeInterval: 1,
                                   target: self,
                                   selector: #selector(timerTick),
                                   userInfo: nil,
                                   repeats: true)
  }

  func timerTick() {
    if timeRemaining > 0 {
      timeRemaining -= 1
      print("time: \(timeRemaining)")
      updateDisplay()
    } else {
      myTimer.invalidate()
      startNextInterval()
    }
  }

  func pauseTimer() {
    myTimer.invalidate()
    intervalLabel.text = "Paused."
  }

  func setTomatoMeter(to tomatoes: Int) {
    var currentTomato = 1
    for tomatoIcon in tomatoIcons {
      tomatoIcon.alpha = currentTomato <= tomatoes ? 1.0 : 0.2
      currentTomato += 1
    }
  }

  // Given a number of seconds, return it as (minutes, seconds).
  func minutesAndSeconds(from seconds: Int) -> (Int, Int) {
    return (seconds / 60, seconds % 60)
  }

  // Given a number, return it as a string of 2 digits,
  // with a leading zero if necessary.
  func formatMinuteOrSecond(_ number: Int) -> String {
    return String(format: "%02d", number)
  }

}

Click here to download the completed project.

So what’s Tampa iOS Meetup, anyway?

Tampa iOS Meetup is the Tampa Bay area’s meetup for beginning programmers and developers new to iOS development. We take a hands-on approach because it’s our answer to a question that I’ve been asked again and again, and it goes something like this:

“I’ve been studying iOS development for some time, and I’m still having a problem writing apps. I know how to program specific features in iOS, but I don’t know how to turn a bunch of features into an app.”

It’s one thing to go through tutorials that show you how to program a specific feature. It’s a completely different thing to take the knowledge from those tutorials and then write an app. My goal for Tampa iOS Meetup in 2017 is to show you how to make that leap by walking you through the process of making apps.

If you’re in the Tampa area and you’ve always wanted to learn iOS development but needed some help getting started, Tampa iOS Meetup is for you! It’s a regular gathering aimed at people new to iOS development or software development in general where we cover all sorts of programming topics as we build applications together in a casual, fun, hands-on setting. Find out more at the Tampa iOS Meetup page.

Categories
Uncategorized

A time traveler from just 10 years ago would have no idea what this billboard is saying

Click the photo to see it at full size.

Lan Bui, who works at mobile marketing firm Leanplum, posted the photo above on LinkedIn, which I found through our mutual friend Analise Perry. It occurred to me that there’d be some people today who’d find it cryptic, and it would be a complete mystery to a reader from a mere ten years ago.

“Lit” has had a slang meaning for over a century, “AF” would make sense to someone from a decade ago once you expanded the acronym, and a designer from that time period might call refer to the little pictures of fire as “pictograms” rather than “emoji”.

It might take longer to explain what “mobile marketing” is to someone from 2007, because it’s hard to imagine doing marketing on the devices, network and software of that era:

A still from the “Stevenote” where the iPhone was introduced.

Categories
Uncategorized

Technology itself is NOT the real disrupter/disruptor [with addendum]

Click the photo to see it at full size.

The photo above has been making the rounds on LinkedIn and Twitter, and I thought that it was worth repeating here.

Here’s the text written on the whiteboard:

  • Amazon didn’t kill the retail industry. They did it to themselves with bad customer service.
  • Netflix did not kill Blockbuster. They did it to themselves with ridiculous late fees.
  • Uber did not kill the taxi industry. They did it to themselves by limiting the number of taxis and with fare control.
  • Apple did not kill the music industry. They did it to themselves by forcing people to buy full-length albums.
  • AirBnB did not kill the hotel industry. They did it to themselves by limited availability and pricing options.

Technology itself is not the real disrupter…

Being non-customer centric is the biggest threat to any business.

In case you were wondering “Is it spelled disrupter or disruptor?”, it appears that either is an acceptable spelling.

Garrett Serack’s good counterexamples

Garrett, whom I know from my days at Microsoft, pointed out a couple of counterexamples on Twitter:

And he followed up that tweet with this: