Mastodon Josh – Page 3 – Josh Hrach

Bindings in SwiftUI

Property wrappers were one of the recent additions to Swift. SwiftUI makes use of several very useful property wrappers. One of them is @Binding. The @Binding property wrapper is really just a convenience for the Binding<T> type.

But before we go into using bindings, what are bindings?

Per Apple’s documentation, a binding connects a property to a source of truth stored elsewhere, instead of storing data directly. You would use a binding to create a two-way connection between a property that stores data, and a view that displays and changes the data.

In SwiftUI, you’ll find this to be a fairly common pattern, especially with built in input fields.

Binding Example

Let’s say we want a view with a switch for the user to interact with. How do we build it?

struct BindingExample: View {
    @State var switchIsOn = false

    var body: some View {
        Form {
            Toggle(isOn: $switchIsOn) {
                Text("Switch 1")
            }
        }
    }
}Code language: PHP (php)

First, we need a mutable variable to hold the state of our switch. In this case, we have a Bool, named switchIsOn. We have to use the @State wrapper so it can be modified in our view.

Next, when we build our Toggle, we need to provide a binding to a property that will hold the state of the toggle. When we pass in the variable, we use the dollar sign to denote that we’re binding to it. So our view holds the current state of switchIsOn, while the Toggle has a binding internally. When the state of the Toggle flips, it is propagated through $switchIsOn back into our view.

Creating a manual binding

If we again look at Apple’s description of a binding, we see that it involves a two-way connection. The binding needs to know the value to get, and where to set the new value when it is changed.

With that knowledge, we can look at how Binding workings. Let’s look at our example again. This time, we’re going to make a binding ourselves.

struct BindingExample: View {
    @State var switchIsOn = false 

    var body: some View {
        Form {  
            Toggle(isOn: Binding(get: {
                self.switchIsOn
            }, set: { newValue in
                self.switchIsOn = newValue
            }) ) {
                Text("Also Switch 1")
            }
        }
    }
}Code language: JavaScript (javascript)

Binding has an initializer that takes a couple of arguments. One, with the get label, is a closure explaining where the binding gets its value. In our example, this is our switchIsOn variable. The other, set, is given the new value for the binding. This gives us the opportunity to do something with it now that it has changed. In our case, we’re setting switchIsOn to the new value.

In its most basic form, Binding opens up a lot of interesting possibilities. In later posts, I’ll talk about when we might want to create a binding between our views, as well as some interesting problems a manual binding can solve.

Portrait Mode and the Neural Engine on iPhone SE

Apple finally announced the successor to the iPhone SE. Also called the iPhone SE (and from here, the one I am referring to with that name), it built on the expectations that many were thinking: A reuse of an existing design with newer internals. This is exactly how Apple approached the first SE model. So it made since that the new iPhone SE followed suit.

Design wise, the iPhone SE is identical to the iPhone 8 that it replaced. But internally, it contains a lot of technology found in the iPhone 11: A13 Bionic chip with the third generation Neural Engine, support for WiFi 6, Gigabit class LTE, etc. It also made some changes from the iPhone 8 in line with the iPhone 11, such as the removal of 3D Touch.

But there are some camera differences with the iPhone SE, especially when compared to the iPhone 8, that made me really think.

Portrait Mode

The iPhone SE, like the visually identical iPhone 8, has a single camera on the back side. If you compare the specs between the iPhone 8 and iPhone SE, you’ll see no difference:

  • Single 12MP Wide camera
  • Wide: ƒ/1.8 aperture
  • Optical image stabilization
  • Digital zoom up to 5x
  • True Tone flash with Slow Sync

The iPhone 8 camera was really good, so keeping that same sensor, especially to reduce cost, makes sense.

But there’s something the iPhone SE has than its predecessor didn’t have:

  • Portrait mode with advanced bokeh and Depth Control
  • Portrait Lighting with six effects (Natural, Studio, Contour, Stage, Stage Mono, High‑Key Mono)

The iPhone SE adds Portrait mode, something that initially came to the iPhone 7 Plus because of its dual cameras. Bringing this to a 4.7-inch iPhone like the iPhone SE is a first.

Now, this isn’t the first time a single camera iPhone has received Portrait Mode. The iPhone XR, released in 2018, had a single backside camera and supported Portrait Mode. If we compare the stats of the XR’s backside camera against the iPhone 8 and iPhone SE, we’ll see that it’s identical. However, there is a notable difference with the iPhone XR:

  • Portrait Lighting with three effects (Natural, Studio, Contour)

The iPhone XR only supports three effects in Portrait Mode on the single backside camera. What gives?

Apple explains in their press release:

iPhone SE features the best single-camera system ever in an iPhone with a 12-megapixel f/1.8 aperture Wide camera, and uses the image signal processor and Neural Engine of A13 Bionic to unlock even more benefits of computational photography, including Portrait mode, all six Portrait Lighting effects and Depth Control.

The neural engine in the A13 is able to do much better with the single camera sensor input to process all of the available effects for Portrait Mode. So much so that it enables all six effects on the single camera!

But it doesn’t stop there!

A First on an iPhone

Let’s compare the front camera on the iPhone 8, iPhone XR, and iPhone SE.

The iPhone 8 and iPhone SE both have identical specs:

  • FaceTime HD camera
  • 7MP photos
  • ƒ/2.2 aperture
  • Retina Flash
  • Auto HDR for photos
  • 1080p HD video recording at 30 fps

Since it doesn’t include the TrueDepth camera system, which uses a camera and additional sensors to detect facial features and depth, the iPhone SE has Touch ID instead of Face ID for unlocking the device. It also makes sense that it doesn’t have Portrait Mode on the front camera.

Except it does.

  • Portrait mode with advanced bokeh and Depth Control
  • Portrait Lighting with six effects (Natural, Studio, Contour, Stage, Stage Mono, High‑Key Mono)

Again, from Apple’s press release, just after the earlier quote:

Using machine learning and monocular depth estimation, iPhone SE also takes stunning Portraits with the front camera.

The iPhone SE is the first iPhone without the TrueDepth camera system with front facing Portrait Mode. It’s using a single camera, like the backside, to enable the same level of Portrait Mode, using the same cameras as found in the iPhone 8.

It’s amazing what the A13 Bionic chip is able to enable. It and the third generation Neural Engine definition power the most powerful smartphones in the world. In the words of Apple SVP Phil Schiller: “It’s a big deal.” And to be able to include their fastest chip in their cheapest iPhone? An easy win for Apple.

Property Wrappers in Swift

With Swift 5.1 and Xcode 11, many new features came to Swift. A lot of these were built in to help enable SwiftUI in Apple’s latest OS releases. While diving into SwiftUI is a great way to understand those features, there are some that we don’t need to go into SwiftUI to understand. One of these is property wrappers.

I like to explain property wrappers by a literal definition of the phrase: They wrap properties in functionality. While there are some built into various Apple frameworks (ie SwiftUI), it’s easy to create your own, and they can give you a lot of neat functionality.

How to create a property wrapper

To define a property wrapper, you need to have a custom type (I typically use structs) that you mark as a property wrapper with the @propertyWrapper keyword.

@propertyWrapper
struct Wrapper { 

}Code language: CSS (css)

By adding that attribute, my type now must conform to its requirements. To do so, I need to add a non-static property named wrappedValue. The type of this property will depend on the kind of wrapper you are building. Let’s look at some practical examples and see what we can build.

Easily decoding an ISO 8601 Date

Since Swift 3, we’ve had the very convenient Encodable and Decodable protocols. The type alias Codable is how we typically see it. Having our models conform to Codable allows us to easily encode and decode our models to and from JSON for API communication.

The Date type conforms to Codable. But how does it encode/decode by default?

struct DateTest: Codable {
    var date: Date = Date()
}

let dateTest = DateTest()
let data = try JSONEncoder().encode(dateTest)
let json = try JSONSerialization.jsonObject(with: data, options: [])

print(json)
// {
//    date = "608081487.019236";
// }Code language: JavaScript (javascript)

Okay. At least Date can encode automatically. I can live with that. But it looks like it’s turning a date into a number. What if we want to work with different standard, such as an ISO 8601 Date?

What is an ISO 8601 Date? It a popular date format presented as a string where a date like 10:43 pm on April 4, 2020 would appear like this:
2020-04-08T22:43:30+00:00

So can Date decode this by default? Let’s find out!

let isodateJSON = """
{
    "date": "2012-07-14T01:00:00+01:00"
}
"""

let isoDecodeTest = try JSONDecoder().decode(DateTest.self, from: isodateJSON.data(using: .utf8)!)Code language: PHP (php)

So using a multi-line statement in Swift, I’m going to try and decode an instance of our DateTest struct with this JSON object. Will this work? If you saw how Date encoded itself, then you won’t be surprised by the response we get from trying to decode this JSON.

ERROR: typeMismatch(Swift.Double, 
    Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "date", intValue: nil)], 
    debugDescription: "Expected to decode Double but found a string/data instead.", 
    underlyingError: nil))Code language: JavaScript (javascript)

While we have a valid date format, Date is expecting a Double. What can we do?

We could create a custom JSONDecoder when decoding our type. But if we want to support ISO 8601 dates across various models, then we have to create custom decoders for all of them. That could end up being a lot of manual decoding work. OR… we could create a property wrapper to automatically decode the string coming from our JSON into a valid Date.

We start by creating our property wrapper. Ultimately, we want to provide a Date. So that will be our wrapped type.

@propertyWrapper
struct ISO8601Date {
    var wrappedValue: Date
}Code language: JavaScript (javascript)

This satisfies the basic requirements of @propertyWrapper. Now we have to add our own functionality. In our case, here is where we will build our custom encoding and decoding capabilities.

We start by declaring ISO8601Date itself as conforming to Codable. This will let us wrap properties in a Codable-conforming model and not cause any errors about a model needing to manually provide such conformance.

Next, we create our custom Codable conformance within our ISO8601Date struct.

init(from decoder: Decoder) throws {
    let value = try decoder.singleValueContainer()
    let stringValue = try value.decode(String.self)
    if let date = ISO8601DateFormatter().date(from: stringValue) {
        wrappedValue = date
    } else {
        throw DecodingError.typeMismatch(Date.self, DecodingError.Context(codingPath: [], 
                        debugDescription: "Failed to decode ISO Date. Invalid string format."))
    }
}

func encode(to encoder: Encoder) throws {
    var container = encoder.singleValueContainer()
    
    let string = ISO8601DateFormatter().string(from: wrappedValue)
    try container.encode(string)
}
Code language: JavaScript (javascript)

Our initializer will try and decode a string. If this string can be used to decode a date in the ISO 8601 standard format, it will do so. Otherwise, it will throw an error. Meanwhile, our custom encode function takes the stored date and encodes it into a string using the ISO8601DateFormatter.

Lastly, we add a simple init to allow the user to set a default value with our property wrapper.

init(wrappedValue: Date) {
    self.wrappedValue = wrappedValue
}Code language: JavaScript (javascript)

Put all of this together and what do we get? If we look at our original DateTest model, we can now prepend the @ISO8601Date attribute to our date property and it will encode to and decode from an ISO 8601 string automatically.

struct DateTest: Codable {
    @ISO8601Date var date: Date = Date()
}Code language: JavaScript (javascript)

Using UserDefaults for property storage

You may be using UserDefaults in your app to store simple properties. Perhaps it’s a Bool saying if a user has enabled or disabled some feature. Or perhaps you’re storing a String of the last logged in user. You may have built convenience properties to read and write these values. In doing so, you may have ended up with a pattern like this:

class SettingManager {
    static var userViewedWhatsNew: Bool {
        get {
            UserDefaults.standard.bool(forKey: "userViewedWhatsNew")
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "userViewedWhatsNew")
        }
    }
    
    static var lastLoggedInUser: String? {
        get {
            UserDefaults.standard.string(forKey: "lastLoggedInUser")
        }
        set {
            UserDefaults.standard.set(newValue, forKey: "lastLoggedInUser")
        }
    }
}Code language: JavaScript (javascript)

There’s nothing wrong with the above code (except maybe hard-coded key values). But as you add support for more and more properties, things get to be real tedious. So if we were to abstract this out, what pattern do we see?

For each property, we are both (a) getting the value from UserDefaults and (b) setting the value to UserDefaults using the same (c) key. With this pattern in mind, let’s approach our property wrapper.

@propertyWrapper
struct UserDefault<StoredType> {
    public let key: String

    var wrappedValue: StoredType {
        get {
            UserDefaults.standard.object(forKey: key) as! StoredType
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}Code language: JavaScript (javascript)

Instead of using wrappedValue to store a value we initialize, like in our first example, we have a computed getter and setter. It is performing our reading and writing with UserDefaults. The key used for those transactions? We provide it in our property wrapper.

What data type does this work with? You’ll see that we are using a generic type (StoredType). It will inherit the type from whatever you end up wrapping.

But wait! What if you wrap an optional type, like String?. Won’t the getter crash if there is no object for key? Nope! Because it’s coming back as an optional, or Optional.none case (if you look at how Optionals work in Swift), which is a valid value for something of type String?. But if we did try to read a String that wasn’t there, then it would crash.

So how about we add a default value?

@propertyWrapper
struct UserDefault<StoredType> {
    public let key: String
    public let defaultValue: StoredType

    var wrappedValue: StoredType {
        get {
            UserDefaults.standard.object(forKey: key) as? StoredType ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }
}Code language: JavaScript (javascript)

Now we will attempt to return a value of type StoredType from UserDefaults but, in the event no such object exists, we will return our defaultValue.

How can we use this in our SettingManager above?

class SettingManager {
    @UserDefault(key: "userViewedWhatsNew", defaultValue: false) 
    static var userViewedWhatsNew: Bool    

    @UserDefault(key: "lastLoggedInUser", defaultValue: nil) 
    static var lastLoggedInUser: String?
}Code language: JavaScript (javascript)

If we were to add support for a new property stored in UserDefaults, it is super simple.

Now, notice how we are setting the key and defaultValue properties when declaring our property wrapper. This is making use of the synthesized initializer on our struct. You could make your own init function and use it when declaring the property wrapper.

Summary

Property wrappers are super helpful. They can help you take repeated code and patterns away from your properties and move them into a reusable component. As we’ve seen here, you can make various Codable edge cases easy to solve. They can also help you with code you find yourself repeating in things like a set, didSet, or other such case.

Like other Swift features, property wrappers are just another tool that are worth having in your developer tool box. I hope this post helped demystify them a little bit and make them more approachable for you.

You can find the code for this post in this Github Gist.

Swift Protocols Aren’t Scary

For people new to Swift, or coding in general, there are some keywords that might seem daunting. Optionals. Generics. Sometimes you can explain these in a simple way. Other times, exposure to them might scare learners.

Protocols might seem like a big concept for some. If you look at how many protocols exist in various frameworks, you might think, “Wow, that’s a lot to learn! When will I use all of these?”

For instance, the Int type conforms to 11 different protocols. Do you need to know them all?

What is a protocol?

A protocol is essentially a blueprint. It defines functionality. This can be requiring the existing of certain properties or functions. Something that conforms to a protocol is required to provide the required elements.

For instance, I could declare a protocol that requires the conforming object, whether a class, struct, or enum, to have a name property.

protocol Nameable {
    var name: String { get set }
}Code language: JavaScript (javascript)

If I have a struct representing, say, an instrument, I can have it conform to that protocol, which will require the presence of a name property.

struct Guitar: Nameable {
    var type: String
    
    var name: String // Required because of protocol
}Code language: JavaScript (javascript)

Protocol conformance can be declared when you create your type, or it can be done in an extension. However, extensions cannot contain stored properties, so you can only add support for required functions in extensions.

protocol DataSource {
    func numberOfItems(in section: Int) -> Int
    func dataToShow(at indexPath: IndexPath) -> Data
}

class DataManager {
    // Your class stuff here
}

extension DataManager: DataSource {
    func numberOfItems(in section: Int) -> Int {
        // Return the number of items in each section
    }
    
    func dataToShow(at indexPath: IndexPath) -> Data {
        // Return some data to show for each row/section
    }
}Code language: PHP (php)

When would I use one?

You would use a protocol when you want to require functionality for a type. When would you use this?

With the exception of very basic iOS applications, you are likely to come across a protocol and not even realize it. Consider the following example:

class MyTableViewController: UIViewController {
    
    var tableView: UITableView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        tableView.dataSource = self
        tableView.delegate = self
    }
}

Here, we have a view controller that we’ve created. It has a table view in it. To have a working table view, we need to have a data source and delegate. In simple examples, you would see what we have here: the class itself conforms as a datasource and delegate.

To do that, we’d have to have our class conform to 2 different protocols. One tells the requirements for the data source, the other tells the requirements to be a delegate.

extension MyTableViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        // Return number of rows in each section
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        // Return the table view cell for each part of the table view
    }
    
    
}

extension MyTableViewController: UITableViewDelegate {
    // Add support for the delegate methods you want to support
}
Code language: JavaScript (javascript)

So here, we’ve told our class to conform to two protocols so that it can satisfy the requirements of the table view.

They’re just blueprints

When we look at the above example of the UITableView, when it’s looking for something to be a data source, it’s wanting something that conforms to UITableViewDataSource. It’s basically saying, “If you conform to these blueprints, you, too, can be a data source!”

Don’t look at the presence of ALL protocols available to use as daunting. Remember: it’s just saying there are dozens of blueprint options out there. But they’re only used for specific reasons. UITableView wants its data source to conform to a certain protocol. UIScrollView’s delegate has certain requirements.

And what about what you might make? Would you ever want to make a protocol? Sure. If you ever want to require your types have certain properties or functions, you would create your own protocol. You’d basically be drafting your own blueprints in Swift.

That’s all protocols are. Blueprints. They’re not scary at all.

iPhone SE 2 Expectations

For months, rumors have circulated that Apple is doing another iPhone SE. But what is Apple’s goal with it? What can we reasonably expect, and why?

Is It Size?

Some think the SE 2 (or whatever it will be called; more on that later) is Apple’s chance to release another smaller phone into the line up. While it does give them the opportunity, that doesn’t seem like the role the SE 2 is to play. The reason for that is because that wasn’t the primary goal of the first SE.

Let’s look back at the original iPhone SE. It launched in the spring of 2016, roughly six months after the release of the iPhone 6S. When the iPhone 6S launched, the iPhone 5S, a then 2 year old phone, was the most affordable option, coming in around $449. The SE ended up replacing it, coming in at $399.

The SE retained the beloved 4-inch screen size that many loved. And while size made it an attractive purchase, there was another benefit. They were able to pack in the same features and specs of the iPhone 6S into the smaller design. But wait: didn’t it cost less than the iPhone 5s? How could they do that?

Reusability

Being based on the older design, it makes sense that, after having manufactured the 5-series for a couple of years, the cost of building the SE, as well as R&D, had been recouped more than a new flagship. The tooling required to build most of the SE were thus already working as a well oiled machine. Savings were easily found there, which could result in a lower price.

The rumored iPhone SE 2 will be no different. It’s likely to look like an existing design (iPhone 8, I’d say), but with improved internals. Yes, that means a newer set of internal components had to be designed to fit into that enclosure. But the cost of producing that enclosure no doubt will allow for a low price.

The one “downside” to people owning such a phone: It won’t look as flash as the iPhone 11 Pro (or whatever phones are debuted later this year). But for the audience Apple is likely targeting with this phone, I don’t think they care so much about flashy. It’s about getting the most bang for your buck.

I know I would love to see another $399 iPhone with the latest specs, and I know many others would, too. Let’s see what happens after one is announced.

Apple Arcade and multiple users

This past Spring, Apple announced a series of upcoming services. One of those is Apple Arcade, a service where users can pay every month to access a collection of exclusive games playable across Apple’s ecosystem.

There is quite a bit of excitement for this, and the games they’ve announced seem like a lot of fun. But there’s something that’s been bugging me.

Fun for the whole family

As an Apple family, it would be nice to have games that play on our Macs, iPhones, and iPads, as well as our Apple TV. But there’s an interesting question: how will that work with family members?

And yes, Apple did say this is for the whole family.

Sure, everyone can access and play these games on their device. But what about those on Apple TV? Right now, Apple TV has to be logged into an Apple ID, and its only one ID. At home, this is my ID. So it has access to my App Store history, and any games supporting iCloud sync up with my personal Apple account.

This would be all I need if I was the only one using the Apple TV. But I’m not. My son plays games on there. But if he wanted to continue playing a game on his iPad after playing on the Apple TV, we hit a snag: How does he do that?

Apple says you can jump from device to device with Apple Arcade. Is this for just ONE of the family members, though? I don’t think that seems fair OR fun.

Rather, I think this implies some other feature that must be coming: multi-user support.

Now, I don’t think iOS requires multi-user support. You typically find people having their own iOS devices in the family. But I DO think Apple TV needs to support the concept of user accounts. When it comes to Apple TV, it’s typically running on the biggest screen in the house, but it’s also likely a single device used by multiple people in a family. It’s time that it fully embraces that.

My son should be able to go from a game on his iPad to the Apple TV in the same way that I can go from my iPhone to the Apple TV. “Jump from [device] to [device]” shouldn’t be limited to specific family members.

If Apple is serious about Apple Arcade, I’d like to take it as a sign that Apple is serious about gaming in the living room. While they aren’t creating a console, the Apple TV can perform very much like one. And like modern consoles, it needs to understand the individual user using the device at the time.

That’s why I wouldn’t be surprised to see multi-user support in tvOS 13. It makes sense that it has to do that to fully embrace what Apple Arcade promises. And if Apple is serious about their Services, this is a vital component.

On iPads Replacing Laptops

The newest iPad Pro seems like a killer machine, and many reviews of it are quite positive. But a lot of reviews that I saw all came back with similar thoughts:

  • Ars Technica – “The iPad Pro raises the bar for performance, but has too many other limitations.”
  • CNET – “It’s got a big, laptop-like screen. It’s more portable than the last version. But it doesn’t solve the final few things I need to make it a true laptop.”
  • TechRadar – “(…)that would help elevate the new iPad Pro towards the level of a real laptop a little more.”

Everyone is expecting iPad to replace laptops at some point in the future. But what exactly are they wanting?

It seems the expectation is that, if iPad is the future of computing, that we’ll eventually not need laptops (and maybe desktops) because iPads will replace the PC category for what we do. Thus, iPads need to eventually do everything current PCs can do and more.

However, Apple itself gave a hint about that. And I don’t think any of that is a good expectation to have.

Here’s what Phil Schiller said in an interview with Charlie Rose for 60 Minutes:

Charlie Rose: Is there danger of one product cannibalizing the other product?

Phil Schiller: It’s not a danger, it’s almost by design. You need each of these products to try to fight for their space, their time with you. The iPhone has to become so great that you don’t know why you want an iPad. The iPad has to be so great that you don’t know why you why you want a notebook. The notebook has to be so great, you don’t know why you want a desktop. Each one’s job is to compete with the other ones.

When I read that, I see each segment in its own tier, competing with the one above it. But it doesn’t imply that one category would eventually eliminate the others. It just forces each tier to keep improving.

Let’s look at the notebook tier. You can split that into two segments: consumer and pro. How am I doing that? I’m basically looking at MacBook and MacBook Air, and comparing that against the MacBook Pro line. They all do similar things, but the Pro line has more power, more ports, and is typically used for a lot more work. But they’re all in the notebook tier.

In the iPad tier, we can simplify it between iPad and iPad Pro. iPad Pro gives you more capabilities and more features to do more work on it. The USB-C port in the new iPad Pro can enable a lot of new functionality now, and it has the potential to open up even more with iOS 13 and beyond.

Yet, it’s still an iPad. It’s on the higher end of the iPad tier, but it’s in no way meant to completely replace the MacBook Pro. Again, looking at Phil Schiller’s comments, it’s competing against the notebook. It’s not replacing the notebook.

This seems confirmed in The Verge’s review of the new iPad Pro:

(…) when pushed on the iPad’s limitations, the company insists that the iPad is still an ongoing attempt to build the future of computing, not a laptop replacement.

Sure, you can review the new iPad Pro against a laptop and see what it can and can’t do. But looking to see if it’s a complete notebook replacement is futile. Sure, it may replace a notebook for some people. If someone just used a laptop for email, news, and staying in touch with family, then an iPad can definitely do all that that person wanted a laptop for. But, implied by Phil Schiller’s own words, there will always be things that a notebook or desktop do better that can’t be done by an iPad. Always.

As the iPad Pro gains more features, I’ll continue to be interested in reviews showing how people can do work on it compared to laptops. I know there are some that live the iPad life already. But I’d also be interested in seeing things from the other angle: How improvements in devices like iPad Pro drive changes and improvements to the Mac line.

The notebook is essentially defending itself from constant attack from the iPad tier. It has been from day one. As each tier improves, so do the tiers they are competing with. iPhone and iPad eventually lead to the inclusion of multi-touch on the Mac line, which I think was a very important addition. Those kinds of influences will only continue.

But whether or not an iPad replaces the notebook tier for you has no relevance to whether or not iPads will replace laptops in general. They won’t. Apple has said it as part of their own intentions. And eight years of iPad history can prove it, too. Notebooks will remain. Desktops will remain. Their differences in UI/UX will remain.

Just like any advancement in iPhone doesn’t threaten the existence of iPad, or any Apple Watch can’t kill the iPhone market, iPad won’t kill the laptop. They’ll just keep getting better and better.

It’s all about survival.

Swift Coding Puzzles #2 – Balanced Brackets

This is part of a series of coding puzzles inspired by the #CodingPuzzle tag/puzzles originally curated and shared by Ali Spittel. This is my approach to solving those puzzles in Swift. If you have any comments or answers you want to share, post them below! I’d love to read them.


Puzzle #2

Given strings of brackets, determine whether each sequence of brackets is balanced.

When would you come across a situation like this? Well, if you ever write a source editor, you might come across something like this. As most programming languages use brackets to denote context, functions, and other divisions, we’ve probably all come across a situation where we forgot to close something off.

For the sake of this exercise, let’s define what brackets mean. In this case, it’ll be three sets of characters: ( ) , [ ] , and { } . The first character we’ll call the “open bracket” and the latter the “close bracket”.

So, what makes a balanced sequence?

  1. A matched pair of brackets.
  2. Any nested brackets must also be matched pairs.

An example of some balanced sequences include: () , [(){}] , {([()])}

An example of some imbalanced sequences include: (() , [([)]] , {([)]}

If any of those become unpaired, then the entire sequence is considered imbalanced. If you’ve ever created a class in Swift and forgot to close a brace when defining functions, you’ll know what that is.

By the above definitions, we can thus determine that in a balanced sequence:

  1. It has an even number of brackets
  2. An open bracket will never meet a close bracket of a different type
  3. A closed bracket will never appear before its corresponding open bracket

That said, here’s how I approached the problem.

First, I defined what each of the brackets were.

let brackets: [Character: Character] = [
    "[":"]",
    "(":")",
    "{":"}"
]
var openBrackets: [Character] { return Array(brackets.keys) as! [Character] }
var closeBrackets: [Character] { return Array(brackets.values) as! [Character] }

Using a dictionary, I’ll be able to easily look up the close bracket for a given open bracket.

Next, let’s look at my function to solve this.

func isBalanced(_ string: String) -> Bool {
    if string.count % 2 != 0 { return false }
    var stack: [Character] = []
    for character in string {
        if closeBrackets.contains(character) {
            if stack.isEmpty {
                return false
            } else {
                let indexOfLastCharacter = stack.endIndex - 1
                let lastCharacterOnStack = stack[indexOfLastCharacter]
                if character == brackets[lastCharacterOnStack] {
                    stack.removeLast()
                } else {
                    return false
                }
            }
        }
        if openBrackets.contains(character) {
            stack.append(character)
        }
    }
    
    return stack.isEmpty
}

First, if the string has an odd number of characters, we automatically fail it. This is in line with point number 1 above.

I then declare a variable to temporarily hold brackets. This stack will be used in the following loop.

Next, we go through each character of the string. Our first check is whether the character is a close bracket or not. If it is, and our stack is empty, it means we’re encountering a close bracket before its open bracket counterpart. We thus return false. If the stack isn’t empty, we check if the last character on the stack is an open counterpart of this close bracket. If it is, we remove the open bracket from the stack. Otherwise, we return false.

If it’s not a close bracket, we’re likely looking at an open bracket. Those automatically get added to the stack.

This loop functions for all characters in the string. Afterwards, our return is whether or not the stack is empty. If it’s not empty, it meant we had more open brackets than closed brackets, and while the earlier catches may not have produced a return, it technically is an unbalanced bracket set.

Here are a few sample calls to this function and their outputs.

isBalanced("()") // True
isBalanced("[]") // True
isBalanced("][") // False
isBalanced("[][") // False
isBalanced("()()") // True
isBalanced("[(){}]") // True
isBalanced("{{[()()][]}}") // True
isBalanced("[]()({)}") // False
isBalanced("(((") // False

I had initially hoped to tackle this with recursion, but I found the above solution to be easier to follow and much easier to manage.

Did you have a different solution? Is there something I could improve with my Swift code? I’d love to hear from you!

Paper by FiftyThree

Lauren Goode at Wired: FiftyThree, Maker of Popular Paper and Paste Apps, Gets Acquired:

BACK IN 2012, a Seattle-based startup named FiftyThree launched a drawing app designed exclusively for iPad, with a name that sounded like it was designed specifically for an Apple crowd: Paper. Despite its simplicity and also because of it, Apple crowned it the iPad App of the Year. Tech writers described it as “the next great iPad app”, “a superbly designed sketching app,” and “a fresh canvas ready and waiting for your ideas, inspiration, and art.” FiftyThree later expanded to include an iPhone app, an optional subscription called Paper Pro, and Paste, a collaboration app.

Today FiftyThree announced its apps and team have been acquired by WeTransfer, a cloud-based file transfer company with headquarters in Amsterdam and Los Angeles.

I’ve been a Paper user off and on ever since they launched in 2012. It’s been my go-to app for a lot of drawing projects, is one of our son’s favorite apps on his iPad, and is popular with my wife, who uses it for a lot of wonderful sketches. Like with any acquisition, there’s a little hesitation as to whether or not it will be a good thing for an existing company and its products. But I’m hoping we’ll see Paper stick around for years to come.

Swift Coding Puzzles #1 – Simple Pig Latin

I’ve been following Ali Spittel‘s #CodingPuzzle challenges. While I’ve read most of them, I haven’t actually done them.  But each of them would be a fun little exercise. So, I figured I’d go back and tackle each of them in Swift.

Before I start, a note: There are always multiple ways to tackle coding challenges. This is just my approach to it. If there are better/more optimal ways of doing it, please comment and share your solutions! I’d love to see them. This is as much a way for me to share what I’ve done as it is for me to learn from how others see problems and want to solve them.

That said, here is the first puzzle:

Puzzle #1 – Simple Pig Latin

“Move the first letter of each word to the end of it, then add “ay” to the end of the word. Leave punctuation marks untouched.”

If this was just taking a sentence without punctuation, this would be a lot easier. You could split the array, modify the words, and rebuild the sentence. But keeping punctuation untouched adds a bit of a challenge.

Being me, I also wanted to make sure that this would handle if someone inadvertently misses a space after some punctuation. (No, this likely doesn’t handle apostrophes properly, so contractions will probably not be correct; but I’m no native Pig Latin speaker to know.) So I may have put a little too much thought into this.

First, I wanted to make a function that modifies a word into its proper Pig Latin form.

/// Takes a word and makes it Pig Latinized
func pigLatin(word: String) -> String {
    var newWord = word
    let startIndex = newWord.startIndex
    let firstCharacter = newWord[startIndex]
    newWord.append(firstCharacter)
    newWord.append("ay")
    newWord.remove(at: startIndex)
    return newWord
}

With this function, I can now pass in parts of a sentence as needed. With that done, now I just need to get the words to pass in.

I could have split the array based on space, but that wouldn’t necessarily preserve punctuation. So I instead opted to go through the string character-by-character and parsing words that are found.

func simplePigLatin(_ text: String) -> String {
    let characterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters)
    var piggyText = ""
    var temporaryWord = ""
    for (index, character) in text.enumerated() {
        let scalar = character.unicodeScalars.first!
        if characterSet.contains(scalar) || index == text.count - 1 {
            if temporaryWord.count > 0 {
                let pigLatinWord = pigLatin(word: temporaryWord)
                piggyText.append(pigLatinWord)
                temporaryWord = ""
            }
            
            // Append any punctuation or white space
            if characterSet.contains(scalar) {
                piggyText.append(character)
            }
        } else {
            // Append letter to temporary word
            temporaryWord.append(character)
        }
    }
    
    return piggyText
}

I go through each character and, if it’s a letter, I add it to a temporary word. If I hit a non-letter (punctuation or whitespace) or the end of the string, I take that temporary word, modify it for authentic Pig Latin, and append it to the return string.

With all of that, here are some sample outputs:

let word = simplePigLatin("I wish I could speak in Pig Latin; I really do!")
print(word) // Iay ishway Iay ouldcay peaksay niay igPay atinLay; Iay eallyray oday!

let contractions = simplePigLatin("I've got day-to-day issues.")
print(contractions) // Iay'evay otgay ayday-otay-ayday ssuesiay.

let badSpacing = simplePigLatin("I forgot,this is not where commas,go")
print(badSpacing) // Iay orgotfay,histay siay otnay hereway ommascay,ogay

So that’s how you can do Pig Latin in Swift. No, it’s not perfect. There are other cases that aren’t dealt with, such as how to handle words starting with vowels or ‘qu’. But I’m no linguist.