WWDC 17 Packing List

WWDC17

It’s almost time for WWDC17, this year in San Jose, CA instead of San Francisco. I haven’t been in several years because my lottery skills are obviously lacking. I managed to score one this year and I’m pretty excited. What will I take this year?

Well, to be honest, taking a laptop to sessions in the past, for me, has been serious overkill. I might jot some notes down in a Moleskine or other notebook – for transcription later. Taking notes on a laptop brings distraction to the game for me. Am I plugged in and charging? I need to get close to a plug. Oh, I can check Facebook during this session. Email. Pretend to be coding something amazing in Xcode. Taking notes on a laptop makes me feel like a court stenographer and I want to relax a bit and take the information in. I can always watch the session again later in its stored video format (to catch some details that might have gone by too quickly).

I will still take my trusty MacBook Pro R, but that will be for when I’m back in my room at night – coding, emailing, sorting photos, watching session videos again, etc. It will probably stay in my room locked up. Charged and ready for my welcome return at the close of each day. That will save me a bunch of weight to lug around too. I can use my laptop bag for other things, including hoofing multiple Odwalla juices around (if they supply those big cans of it around the venue).

For travel:

  • Bose QuietControl 30
  • Bose QuietComfort 35
  • Casio ProTrek PRW 3100y-1B (all black) watch

For the sessions:

  • iPhone 6 Plus. The most important bit of gear I have.
  • At least two portable battery chargers. Anker Astro Pro Series(20000mAH) and a RavPower (16750mAH)one I had before the Anker. Both charged up and ready to fly. The Anker needs plugged into an outlet, the RavPower does microUSB for charging.
  • Two point-and-shoot cameras – both Sony. An older one which is my favorite, and a newer model which I don’t like as much.
  • Timbuk2 messenger laptop bag.
  • Moleskine, fountain pen, EDC pens, etc.
  • Zojirushi Coffee Thermos 20oz. Über awesome and über important.
  • Various cables, USB wall plugs, etc.
  • Takeya ThermoFlask Insulated Stainless Steel Water Bottle, 40 oz, Asphalt. Huge. Perfect. Might leave at the hotel as it could be overkill.
  • Apple Watch series 2. I’m hoping Apple updates the WWDC iOS app and it gets watch support – for keeping us on our selected schedules, getting important updates, map support, etc.
  • Sunglasses. For me, very important.

Hotel:

  • Takeya ThermoFlask Insulated Stainless Steel Water Bottle, 40 oz, Asphalt. Huge. Perfect. No water bottles.
  • Couple of polos/shirts (a few older WWDC ones, a Swift logo one, etc.)
  • Shorts/jeans/Scarpa shoes. Scarpa Margherita rock.
  • A spring jacket in case it gets cool at night (Patagonia fold up for laptop bag) – not wearing a WWDC17 one if Apple swags those out.
  • Livionex tooth gel. Its amazing.
  • Wet shaving and beard gear. Very important to clean up daily.
  • Packing cubes. Although I’m not bringing a ton of clothes, they really, really, really help keep things tidy. I know I’ll be bringing back a few additional shirts and a sweatshirt at least.
  • Snooz white noise generator (I got from Kickstarter)
Tagged : / / /

watchOS: Adding a complication to an existing project?

Apple Watch Complications

Much of Xcode is wonderful to use in my opinion. You can dig and dig and dig and still find new things in there. However, there are aspects that induce sphincter tightening just thinking about it (without version control). Anything you do to a project file is scary stuff.

Recently I had a project that I attempted to add a watchOS complication to. You can’t undo that. I ended up with a bunch of extra targets, including a new watch extension itself. I wasn’t sure what I was doing and things started producing warnings and errors. I deleted a bunch of stuff  and cleaned the build folder. Whew.

Turns out, it’s quite easy to create a new project with complications present before anything. Most of the project’s code was able to be ported quite easily. Some other things took some time (plist files, user interfaces, etc.)

I have yet to find out how to add a complication to an existing watchOS application. I spent too much time searching the internet for answers. I was actually able to mostly figure out complication templates too – after a lot of trial and error. So much so that I had to use the simulator because I was building so much to get it working.

Tagged : /

Sending and playing an audio file from Apple Watch Extension to iOS

Watch Image

I recently played around recording audio on my Apple Watch. Doing that was easy enough, and I wanted to send the recorded file from the watch extension to the iOS application – and play it. I started messing around and used sendFile as the communication protocol since it can work in the background (without the iOS app needing to be open). I banged on it a ton and it worked – but I couldn’t figure out a way to actually play it. The file.fileURL.path had some dynamic stuff in the URL which prevented me from successfully creating a valid AVAudioPlayer.

Here is my solution in sending a file (wav) to the iPhone and playing it upon receipt.

//Watch Extension code

override func awake(withContext context: Any?) {
    super.awake(withContext: context)
        
    if WCSession.isSupported() {
        WCSession.default().delegate = self
        WCSession.default().activate()
    }
        
    let fileManager = FileManager.default
    let container = fileManager.containerURL(forSecurityApplicationGroupIdentifier: "group.net.ericd.WatchRecord")
    let fileName = "audioFile.wav"
    // class variable
    saveURL = container?.appendingPathComponent(fileName) as NSURL?    
}

@IBAction func sendAudio() {
    let data = NSData(contentsOf: saveURL as! URL)
    sendAudioFile(file: data!) // Quicker.
}

func sendAudioFile(file: NSData) {
    WCSession.default().sendMessageData(file as Data, replyHandler: { (data) -> Void in
        // handle the response from the device
    }) { (error) -> Void in
        print("error: \(error.localizedDescription)")
    }
}

The presentAudioRecorderController, when successfully saving the audio file recorded, enables a button that calls the sendAudio function. That code is here.

@IBAction func recordAudio() {
    let duration = TimeInterval(10)
    let recordingOptions = [WKAudioRecorderControllerOptionsMaximumDurationKey: duration]
    print("record:", saveURL as! URL)
    presentAudioRecorderController(withOutputURL: saveURL as! URL,
                                                    preset: .narrowBandSpeech,
                                                    options: recordingOptions,
                                                    completion: { saved, error in
                                                        
                                                    if let err = error {
                                                        print(err.localizedDescription)
                                                    }
                                                        
                                                    if saved {
                                                        print("saved file.")
                                                        self.playButton.setAlpha(1.0)
                                                        self.sendButton.setAlpha(1.0)
                                                        self.playButton.setEnabled(true)
                                                        self.sendButton.setEnabled(true)
                                                    }
    })
}

Now, in the iOS application, I handle the receipt of the sendMessageData method.

func session(_ session: WCSession, didReceiveMessageData messageData: Data, replyHandler: @escaping (Data) -> Void)
{
    DispatchQueue.main.async
    {
        self.someLabel.text = "We got an audio file: \(messageData)" //Show bytes
        self.versionLabel.textColor = UIColor.blue
            
        do {
            self.player = try AVAudioPlayer(data: messageData)
            guard self.player != nil else { return }
            self.player?.prepareToPlay()
            self.player?.play()
                
        } catch let error as NSError {
            print(error.localizedDescription)
        }
    }
}

And there the audio file is rendered upon receipt. Not using a file transfer (which I would prefer since it’s a background task that doesn’t require the iOS application to be running in the foreground).

Tagged :

watchOS3 communication to iOS (speed)

Speed with caution

Recently I was working with watchOS 3 and iOS – transferring data back and forth (not using reply callbacks). I was using .sendMessage, and messages from the iPhone to the watch were very quick. However, messages from the watch to the phone seemed very slow, or so I thought.

This is what my receive delegate looked like on the iPhone side of the BLE fence.

// Message received from the Apple Watch Extension.
func session(_ session: WCSession, didReceiveMessage message: [String : Any])
{
    let val = message["ancValue"] as! String
    messageFromWatchLabel.text = "ANC: \(val)"
}

That took anywhere from 1 to 25 seconds to populate the UILabel. Now, I miraculously remembered reading somewhere that on the iPhone, the delegate call comes back on a background thread. My eyes lit up, and I tried updating my UI on the main thread (where it needs to happen).

// Message received from the Apple Watch Extension.
func session(_ session: WCSession, didReceiveMessage message: [String : Any])
{
    let val = message["ancValue"] as! String
    // Main thread - super snappy.
    DispatchQueue.main.async {
        self.messageFromWatchLabel.text = "ANC: \(val)"
        self.slider.value = Float(val)!
        self.sliderValue.text = "\(Int(val)!)"
    }
}

Shazaam. Snappy as can be. So try not to forget about this. I didn’t need to do this on the watch side because I assume there is no threading on the watch (that we have access to)? It’s very snappy in response to the same delegate on the watch side, so I’m not sure what’s going on there. But that main thread for iOS saved my bacon.

Tagged :

Swift: When you have an Optional String you need to remove

Screen Shot 2016-07-21 at 10.09.57 AM

Recently I have been developing an iOS app that comes with a companion Apple Watch extension. I wanted to play with the communication between iOS and WatchOS using

func session(session:WCSession, didReceiveMessage message:[String:AnyObject])

via

WatchConnectivity

It was working well and I thought I had it nailed down. Nope.

At one point my watch extension makes a request to the iOS app for a preset index. That’s so I can display the preset number in an alert on the watch at another point. I was setting the title of the alert to display something like “Preset 2”. Instead I was getting “Preset Optional(2)”. Not good.

I thought the problem was in unwrapping an Optional on the watch extension side. I tried all sorts of things that did nothing. How can you unwrap a String since they are not Optional?

Finally I printed the full watch extension message and found the Optional was already in the String when it arrives. So I had to look at the iOS ViewController that created the message to send to the watch extension.

Here is a breakdown (simplified) of what it looks like for the iOS application.

func session(session: WCSession, didReceiveMessage message: [String : AnyObject])
    {   
        if let info = message as? Dictionary<String,String>
        {
            if let s = info["needPresetData"]{
                let index = Int(s)
                let preset:Preset = self.presetObjectArray[useIndex]
                let p1 = preset.name
                let p2 = preset.source
 
                let window = UIApplication.sharedApplication().keyWindow
                let vc = window?.rootViewController as! ViewController
                let dict:[String:String] = ["description":"\(index)_\(p1)\n\n(p2)"]
                vc.session.sendMessage(dict, replyHandler: {(_: [String:Anyobject])-> Void in
                    //
                }, errorHandler: {(error ) -> Void in
                    //
                })
            }
                
        }
}

I think the braces balance (I typed this by hand). They should anyway. Do you see anything obvious in there? I didn’t for a while until I posted to StackOverflow and the comments came pouring in. There was an optional being injected into the constructed string prior to being sent (as I discovered later).

The solution was an explicit unwrapping of something I didn’t think was an Optional to begin with.

let dict:[String:String] = ["description":"\(index!)_\(p1)\n\n(p2)"]

Look at that. index!

This solved the problem on the other end where I was splitting the index off the String (into an array and used the number for the alert title). I was going mad, but the answer should have been more obvious.

Thanks to those on StackOverflow that helped me go through it. I don’t have code reviews for prototypes I usually build at work and that one sailed right by me for a while.

Near Lock is really nice.

nearlockThe idea of having a BLE-enabled proximity lock & unlock for a work computer is a really good one. I generally have quite confidential information displaying on my monitors at any given time throughout the day. If I run to a meeting, the cafeteria, or down the hall to refill a water tumbler, my screens lay exposed for wandering eyes both internal employee and the rare external visiting guest.

Continue reading “Near Lock is really nice.”

Tagged :

Apple Watch after WatchOS 2.0.1

IMG_0949Based on my previous post about my Apple Watch, I was hoping for Apple to do something with an update release for WatchOS. I still wasn’t getting close to a whole day on a charge but it seemed a bit better than before.

One thing I noted, and I offer this suggestion. If you charge your watch near or close to a large table fan at night, the magnets in the fan might be fooling your watch into thinking it’s fully charged when it’s not. I have since moved my charger and I’ve noticed much better performance.

Anyway, I am now putting the watch on around 8:00 AM and by around midnight, I am still around 65% battery. This is after 2.0.1 was installed. I don’t know what bugs or internal features Apple has made, but they have significantly stretched battery life for me. I don’t keep the battery complication on any watch face any more because I don’t have to worry about battery at all. It’s pretty liberating. I know my experience shouldn’t have included worrying about battery life to begin with, but now that it’s been addressed, I am quite happy.

Thank you Apple.