On the missing Xcode

Update: We got a seed of an Xcode 10.2 today (January 24, 2018) – so no conspiracy. Perhaps a holiday break? It was fun to write all this down at least.

As an iOS developer I’m used to change. We get new apis every June, new betas of iOS and Xcode every few weeks. Our environment is always changing.

But something recently stopped changing. Xcode. If you go to Xcode Releases and look, the last release iOS developers got was Xcode 10.1, released on October 30, 2018. 84 days ago.

Scroll back through previous releases, and I can’t see any other time where releases have paused this long.

I have a few guesses/crazy theories about this, enough that I’ve decided to write them down.

Mac With Arm Chips

There’s been more and more rumors recently about Macs coming with ARM chips. I can imagine Apple pausing releases of Xcode to integrate and test support for them. I can also imagine an Apple keynote where they announce them, shipping in a month, and a beta of Xcode drops with that support. When? March or April perhaps? Beyond that I feel it’s too close to WWDC 2018 in June.

iOS 13

There’s been lots of speculation that iOS 13 is going to add support for iPad features. Given that could require massive Xcode changes, Apple pauses releases to add support and test. When? It would have to be at WWDC 2018 in June, given previous history.

Marzipan

At WWDC 2018, Apple announced they will be bringing forth a way for iOS apps to run on macOS. This will necessitate massive changes to Xcode to support, hence the current pause. When? Apple said 2019. The question in my mind is do they seed a release to developers earlier than WWDC 2019 in June.

Seed Marzipan in March, get developer feedback for a few months until June, when iOS 13 drops with better iPad support, and hand in hand better macOS support for Marzipan apps.

Conclusion

The pause in Xcode releases is odd. Odd enough that I felt compelled to write this down. But I have no little birdie to tell me any secrets. Yes, I have friends that work in various parts of Apple. But I learned long ago not to ask them any questions, knowing I didn’t want to put them in any awkward spots.

What would be really funny is if we get a new Xcode seed tomorrow morning.

Swift By Sundell – Model Controllers

This is a great post about Model controllers. Upon reading it I realize that I’ve done this for many years, to try and keep things encapsulated.¬†

https://www.swiftbysundell.com/posts/model-controllers-in-swift

I’ve been slowly rewriting my Birthdays app in Swift, for no real reason except to see how little code I could write now to reproduce when I wrote 9 years ago in Objective C.

I had been putting a lot of logic in the “Person” class I made. Looking at my old code I had a Person class, and a PeopleController. Not sure why I made that distinction. ūüôā

 

Birthdays: View on App Store

Apple’s December 2 Springboard Crash Bug

It’s been interesting to watch this pop up tonight. It looks like a pretty serious bug in iOS 11:

https://www.macrumors.com/2017/12/02/ios-11-1-2-date-bug-crash-loop/

I can imagine what’s happen at Apple right now for the past few hours, engineers in there working and finding and solving the problem. Other folks discussing how to push it out to customers.

I wonder if they are also discussing tonight about pushing the button and releasing 11.2 tonight. I don’t think that’s in the cards if only for the size of that download, vs a super small patch for this one.

Today at work I spent most of the day doing a small firefight around how Touch ID / Face ID works in an app. I’ve come to the conclusion that’s a combination of a subtle bug, and customers expectations regarding how the feature is supposed to work. It felt like a small firefight to me Рit was exciting and a little hairy at the same time.

But nothing on the scale of what’s happening at Apple tonight. Good luck Apple folk!

Intuit vs eBay Settlement

Today I received a $150 check from the settlement of this class action lawsuit: http://www.agtechemploymentsettlement.com/frequently-asked-questions.aspx.

As much as I am not a fan of class action lawsuits, two companies that colluded to not poach each others employees seems wrong, it doesn’t allow for people to move freely between companies based upon their skills and future challenges. 

This FAQ link is interesting: http://www.agtechemploymentsettlement.com/frequently-asked-questions.aspx#a6. Рtalking about the different settlements amounts, and the numbers of people in them. 

Halt And Catch Fire

I’ve really enjoyed¬†Halt And Catch Fire¬†over the past three years. Being part of the tech industry now for over 20 years, I’ve found myself really empathizing with these characters, and growing to love them.

The final season was great and heart wrenching at the same time. But the writers and actors did a fantastic job in my opinion. I shed more than one tear over the last 4 episodes.

If you haven’t seen the show, I highly recommend it. It’s a bit techie, but the show is really about relationships and people.

XOXO to Halt and Catch Fire.

Test Drove a Chevy Bolt EV today

A few days ago I got an email from Chevy regarding a new program they started in the past few weeks where one can sign up for a Chevy Bolt EV test drive. (It’s currently offered in San Francisco/San Jose, San Diego and Los Angeles)

But unlike a normal test drive, they drive the Bolt EV to the location you choose and you drive from there for about 30 minutes.

So I signed up for April 1 at 11am. I got an email confirmation, and when the driver was on his way I got a text with a link to a page that had a map showing the Bolt’s current location.

 

Sam arrived with the blue Bolt, and we hopped in, he showed me around a bit, and then I ended up driving to work and driving to my local grocery store, on the roads that I travel every day.

Sam wasn’t from a particular dealership in the area, but works for Chevrolet doing this only, what he calls a product specialist. So no hard sells, just a nice test drive on roads I drive every day.

Thoughts on the car

I was very impressed. The Bolt has some really good pickup, that great linear acceleration that people have talked about with electric cars. The technology was impressive too, two nice large screens, one for your speedo, etc, and one in the radio/nav area of the car.

Will I get one? I don’t know. I’m a little sad that it doesn’t have adaptive cruise control (my current Mazda has it). And it’s a good $14,000 more than my current car. But I would probably end up leasing it, as I have to think in 3 years the cost/technology in electric cars will have advanced quite a bit.

A few last pictures

 

 

 

Long after WKWebView Hello World – Part 1 – Cookie Handling

First of all, I want to thank my friend and colleague Lucien Dupont for inviting  me to contribute.

Recently our project transitioned web views written in UIWebView to WKWebView and Lucien suggested that I share some of my experiences with the larger community.  The intent here is not a tutorial on how to write web views using WKWebView, (there are tons of great, and free material out there) but suggest solutions on problems that we struggled with and hopefully help those with similar issues.

Before I begin, I am greatly indebted to the ton of postings in Stack Overflow and other web sites.  In particular please bookmark the following links whose authors really took the time to document the many tips they collected over the years:

https://github.com/ShingoFukuyama/WKWebViewTips

iOS WKWebView Tips

Finally my discussions assumes iOS 9 and above, Xcode 8.2.1, Swift 3, basic WKWebView API

Cookie Handling

Adding Cookies

Quick take: Cookies are added using Javascript and only after the document is loaded

Adding cookies to a WKWebView from the app is different from UIWebView.  For UIWebView, one can take advantage of the APIs in HTTPCookieStorage as follow:

let cookieStorage = HTTPCookieStorage.shared
let cookieProperties: [AnyHashable: Any] =
[HTTPCookiePropertyKey.domain: "mydomain",
HTTPCookiePropertyKey.path: "/",
HTTPCookiePropertyKey.name: "mycookie",
HTTPCookiePropertyKey.value: ",abc"]
let cookie = HTTPCookie(properties: cookieProperties as! [HTTPCookiePropertyKey : Any])

cookieStorage.setCookie(cookie!)
UserDefaults.standard.synchronize()

Add a cookie to the HTTPCookieStorage and viola, the cookie is added.

WKWebView, cannot take advantage of HTTPCookieStorage APIs to add the cookie.  Instead one has to use javascript evaluation to add the same cookie

let webConfiguration = WKWebViewConfiguration() 
myWKWebView = WKWebView(frame: .zero, configuration: webConfiguration)

let addCookieScript = "document.cookie = \'mycookie=abc;domain=mydomain;path=/\';"

...

// Call this only after document is loaded
myWKWebView.evaluateJavaScript(addCookieScript,completionHandler: { (result, error) in
            if completionHandler != nil {
               completionHandler(result, error)
            }
        })

Additionally, the above javascript evaluation will not be successful until a document is loaded!

Sharing Cookies

Quick Take: Use the same WKProcessPool to share cookies with other WKWebView

Since WKWebView is not able to take advantage of HTTPCookieStorage, you would need to create a common WKProcessPool to be used by WKWebViews that need to share cookies

let commonProcessPool : WKProcessPool = WKProcessPool()

let configuration1 = WKWebViewConfiguration()
configuration1.processPool = commonProcessPool
let webView1 :WKWebView = WKWebView(frame: CGRectZero, configuration: configuration1)

let configuration2 = WKWebViewConfiguration()
configuration2.processPool = commonProcessPool
let webView2 :WKWebView = WKWebView(frame: CGRectZero, configuration: configuration2)

Instruct WKWebView to add cookies once document is loaded

Quick Take: A javascript to add cookie can be set when the WKWebView is first initialized and can be instructed to run when the document is loaded

WKWebView can be instructed to add cookies once the document load and before any other scripts and logic are processed within the document.  This is especially helpful when some of the scripts are looking for certain cookies.  We can do this using the WKUserScript class and specify the injectionTime to .atDocumentStart (and if for some reason you need the script to be run at the end, then use .atDocumentEnd)

let addCookieScript="var cookieNames = document.cookie.split(\'; \').map(function(cookie) { return cookie.split(\'=\')[0] } );\nif (cookieNames.indexOf(\'mycookie\') == -1) { document.cookie=\'mycookie=abc;domain=mydomain;path=/\'; };\n"

let script = WKUserScript(source: addCookieScript, injectionTime: .atDocumentStart, forMainFrameOnly: false)

let config = WKWebViewConfiguration()
config.processPool = processPool

config.userContentController.addUserScript(script)

webView = WKWebView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(50), height: CGFloat(50)), configuration: wkconf)

What about time sensitive cookie?

Quick Take:  Use a message handler to add time sensitive cookie

The above example works well if the value of your cookie stays the same.  Eg if the same web view is used to load another document which also needs the same cookies, then your work is done.  However, what if the value of the cookies changes eg if they are time sensitive, then using the above example could result in stale data.  Fortunately we can modify the example to callback to use a message handler to set the cookie with the latest value when the document loads

class MyWKWebViewController: NSObject, WKNavigationDelegate,  WKScriptMessageHandler {

override init() {
  // this script callback when the document load
  let script = WKUserScript(source: "window.webkit.messageHandlers.MyListener.postMessage(\"setCookiesForInitialLoad\");", injectionTime: .atDocumentStart, forMainFrameOnly: false)

  let config = WKWebViewConfiguration()
  config.processPool = processPool

// Note make sure the name of the message handler, in this case 'MyListener' is the same the one specified in the script
  config.userContentController.addUserScript(script)      conf.userContentController.add(self, name: "MyListener")

  webView = WKWebView(frame: CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(50), height: CGFloat(50)), configuration: config)

}

// MARK: Message Handler methods
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

  let messageValue = message.body as! String;
  switch (messageValue) {
     case "setCookiesForInitialLoad":
          self.webView.evaluateJavaScript(self.addTimeSensitiveCookieScript, completionHandler: nil)
                break;
            default:
                break
   }
}

func addTimeSensitiveCookieScript() -> String! {
   let newValue : String = self.getSomeTimeSensitiveValue()
   let addCookieScript="var cookieNames = document.cookie.split(\'; \').map(function(cookie) { return cookie.split(\'=\')[0] } );\nif (cookieNames.indexOf(\'mycookie\') == -1) { document.cookie=\'mycookie=\(newValue);domain=mydomain;path=/\'; };\n"

    return addCookieScript
 }

Deleting Cookies

Cookies are not automatically deleted when the WKWebView is deleted, have to manually delete them

As with UIWebView, cookies are not deleted when they are deleted, in the case of WKWebView, once again, you are not able to delete cookies using HTTPCookieStorage APIs.  You can use the following (which will not only delete cookies but any cache data as well.  Note some cookies are needed for signing out etc, so do not delete cookies until they are used for the necessary clean up.

func deleteWebViewData(completion: @escaping () -> Void) {
  let dateFrom = NSDate(timeIntervalSince1970: 0)
  let websiteDataTypes = WKWebsiteDataStore.allWebsiteDataTypes()

  WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes, modifiedSince: dateFrom as Date, completionHandler: {() -> Void in
  UserDefaults.standard.synchronize()
  completion()
  })
}

That’s all for now, hopefully its enough to chew for the time being.

I would like to leave you with a link to a slide show that Mobify put together about their experience with WKWebView: