Fireside 2.1 (https://fireside.fm) Fireside Swift Blog https://firesideswift.fireside.fm/articles Thu, 07 May 2020 00:00:00 -0700 Fireside Swift Blog af Using GeometryReader to Swap Between Vertical and Horizontal Stack Views in SwiftUI https://firesideswift.fireside.fm/articles/geometry-reader Thu, 07 May 2020 00:00:00 -0700 [email protected] eecc12f4-7ab6-4f02-acc3-b0b6fd297f52 I’m not going to spend too much talking about this here, because we talked about it on the show. Here is the awesome example code that Matt Waller from Weird Swift meetup sent to us to show how we could use GeometryReader to easily change a stack orientation in SwiftUI.

import SwiftUI
import PlaygroundSupport

struct ContentView: View {
    var objects = [1, 2, 3, 4, 5, 6]
    @State var horizontal = true
    func switchOrientation() 


    var body: some View {
        VStack {
            Button(action: switchOrientation) {
                Text("Switch orientation")
            }
            ZStack {
                GeometryReader { proxy in
                    ForEach(self.objects, id: \.self) { index in
                        Text("\(index)")
                            .offset(self.offsetCalculator(size: proxy.size, index: index))
                    }
                }
            }
            .animation(.default)
            .frame(maxWidth: .infinity, maxHeight: .infinity)
        }
    }

    func offsetCalculator(size: CGSize, index: Int) -> CGSize {
        if horizontal {
            let screenDivision = CGFloat(size.width / CGFloat(objects.count + 1))
            let height: CGFloat = 0
            let width = CGFloat(index) * screenDivision
            return CGSize(width: width, height: height)
        } else {
            let screenDivision = CGFloat(size.height / CGFloat(objects.count + 1))
            let height = CGFloat(index) * screenDivision
            let width: CGFloat = 0
            return CGSize(width: width, height: height)
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View 


}

PlaygroundPage.current.setLiveView(ContentView())
]]>
Basic Reading and Writing to Tags using CoreNFC https://firesideswift.fireside.fm/articles/core-nfc Tue, 29 Oct 2019 00:00:00 -0700 [email protected] 9b9143c0-fbfe-4bd1-94f9-8d486b745c54 Someday I’ll get back to Part 2 of my UIDynamicAnimator tutorial, but today is not that day. I was super excited when Apple announced at WWDC this year that iOS 13 would let us not just read NFC tag payloads, they would let us write to them too! Shortly after this announcement, however, my company was acquired, we moved offices, and my job responsibilities changed significantly. It’s rather amazing how much our jobs can affect our motivations, because almost all my excitement to explore this gift from Apple disappeared while I figured out where I fit in my new role.

My motivation returned when I was asked to give a guest lecture for Lambda School on CoreNFC. I’ve been working with CoreNFC from the day it came out, and it has literally changed my life and career. It was time to dive back into the joys and mysteries of NFC. To my dismay, there was very little information out there on using CoreNFC to successfully write to tags. After several hours of hacking I was able to piece together writing a basic english formatted string to a chip using what I could find and what I already knew of reading tags. Here is what I learned.

My project can be found here. It’s possible it may have improved since the time I had written this blog post. This post is based on the first commit dated October 29, 2019.

There are a couple ways to start the NFC scanner to write. In this project I use this method:

readSession = NFCNDEFReaderSession(delegate: self, queue: .main, invalidateAfterFirstRead: false)

Notice how “invalidateAfterFirstRead” is set to false. This is key. We need to keep the reader open after we initially read with it, so we can connect to the tag and write to it.

The next major difference is that we will use

func readerSession(_ session: NFCNDEFReaderSession, didDetect tags: [NFCNDEFTag])

instead of

func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage])

like most tutorials will have you do. The reason is that we need access to the NFCNDEFTag object. You need this object to write, but you can also read the tag as well, although, it requires a few more steps than just using the NFCNDEFMessage method.

Here is my entire write code:

tag.queryNDEFStatus { (status, capacity, error) in
            guard error == nil else {
                print(error!)
                session.invalidate()
                return
            }

            print("Capacity: \(capacity) Bytes")

            switch status {
            case .readWrite:
                print("This tag is read/write compatible!  :D")

                // The characters "\u", "e", and "n" are prefixed on strings that are formatted for english
                // There are other prefixes for other language formats.
                guard let payload = "\uen\(self.writeToTagTextField.text ?? "")".data(using: .utf8) else {
                    session.invalidate()
                    return
                }

                let record = NFCNDEFPayload(format: .nfcWellKnown, type: Data(), identifier: Data(), payload: payload)

                // Not sure why NFCNDEFPayload.wellKnownTypeTextPayload() doesn't work...
//                guard let record = NFCNDEFPayload.wellKnownTypeTextPayload(string: "This is a test", locale: .autoupdatingCurrent) else {
//                    session.invalidate()
//                    return
//                }

                let myMessage = NFCNDEFMessage.init(records: [record])

                tag.writeNDEF(myMessage) { (error) in
                    if let error = error {
                        session.alertMessage = "Write NDEF message fail: \(error)"
                    }
                    else {
                        session.alertMessage = "Write NDEF message successful!"
                    }

                    session.invalidate()
                }
            case .readOnly:
                print("This tag can only be read.  :)")
                session.invalidate()
            case .notSupported:
                print("This tag is not supported.  :(")
                session.invalidate()
            @unknown default:
                print("I have no idea what just happened. 8O")
                session.invalidate()
            }
        }

The first thing you must do is check if the tag can even be written to. (I guess you are free to let it error out, but I like to make sure we know why it failed too.) Getting the status will also get you the capacity of the tag in bytes. I currently do not have any checks around this, but you can figure out how to add them pretty easily.

The key is to determine how to build the NFCNDEFMessage to be written. I am disappointed that Apple’s convenience function,

let record = NFCNDEFPayload.wellKnownTypeTextPayload(string: "This is a test", locale: .autoupdatingCurrent)

doesn’t seem to work as expected. I’m really not sure what it’s doing, but it definitely isn’t formatting the payload they way a normal NFC reader would understand. So instead I was able to achieve success by creating a NFCNDEFPayload object with Data and using that to initialize a NFCNDEFMessage. (I just now realize I used the long form, .init(), for NFCNDEFMessage. 🤦‍♂️)

// The characters "\u", "e", and "n" are prefixed on strings that are formatted for english
// There are other prefixes for other language formats.
guard let payload = "\uen\(self.writeToTagTextField.text ?? "")".data(using: .utf8) else {
    session.invalidate()
    return
}

let record = NFCNDEFPayload(format: .nfcWellKnown, type: Data(), identifier: Data(), payload: payload)

let myMessage = NFCNDEFMessage.init(records: [record])

Notice how you don’t need to set anything but empty data objects for “type” and “identifier”. I’m not sure if this will cause problems with other readers, but I was able to read the payload just fine after it gets written to the tag.

Once I have my NFCNDEFMessage I can then just call the write method.

tag.writeNDEF(myMessage) { (error) in
    if let error = error {
        session.alertMessage = "Write NDEF message fail: \(error)"
    }
    else {
        session.alertMessage = "Write NDEF message successful!"
    }

    session.invalidate()
}

All in all, it’s not that complex, however, it did cause me a little more grief than I expected it would, so I figured it would help you out. Let me know if there are improvements to be made, and like I said earlier, I will hopefully improve this project as I learn more.

Thanks!

Steve

]]>
My first post for Better Programming on Medium.com! https://firesideswift.fireside.fm/articles/better-programming Sat, 18 May 2019 00:00:00 -0700 [email protected] 84f2ecaf-8861-4af9-a373-97d08957b9e7 Hey everyone!

It looks like it’s been about a year since anything happened with this blog….ooops. Good news though! I joined a publication on Medium.com called Better Programming. It is exactly what it says on the tin. Daily posts about programming covering a range of topics. I wrote my first post them recently and I would love for you to check it out. Hit up the friend link below so you can get full access to what’s normally behind a pay wall!

https://medium.com/p/dealing-with-burnout-or-how-i-learned-to-stop-worrying-and-appreciate-life-c1fbf96c0b9a?source=email-43dcea5e74bc--writer.postDistributed&sk=d3a28fb775a13ffb9c0a55c9722443e0

Thanks for your continued support!

Zack

]]>
Generics https://firesideswift.fireside.fm/articles/generics Tue, 15 May 2018 00:00:00 -0700 [email protected] 7b1e3180-dd4d-4d7e-ae98-0c48b194c338 Swift Generics

Generics in Swift… Seem intimidating? I know they seemed that way to me…at first. But think back, wasn’t there a time in your programming career where variables, loops, and functions seemed intimidating? Now, they most likely feel second nature. This is true for plenty programming concepts. At first, they can be scary, but once you dive in that doubt and confusion will quickly dissipate. So hold tight, in this post I am going to walk you through the basics of generics and some of the different ways they can be used.

The Basics

So…what are generics? First off, I am certainly not the first person to write about generics. There are plenty of resources out there. Just search “Swift Generics” and tons of resources will appear. But that is kind of a beautiful thing in itself. Some people can learn just fine from Apple Documentation, others from a nice tutorial, some from videos or screen casts, and still others from blogs. Though DRY is a great principle for programming, it does not apply as nicely to pedagogy. You never know what style, analogy, example, medium, etc will finally give you that oh-so-sweet “aha” moment - that beautiful rush of understanding that comes when you realize you finally “get it”. So, with any luck, perhaps this post will provide you your “aha” moment or, at the very least, perhaps it will give you a good idea.

Now, back to the technical concepts…what are generics? Well, its really in the name. Generics are a feature for taking classes, structs, enums, typealiases, protocols, etc, and making them…well…generic. All of our functions and objects reference other and objects such as Strings, Ints, Doubles, etc. But sometimes you don’t care about a specific type, you want to write your code for ALL types, or at least a subset of those types. To show an example of this, lets make our own generic data structure. Many times, when a developer wants to write an asynchronous function, they will use closures like this:

internal func fetchStringAsyncWithNormalClosure(arg: String, completion: (String?, Error?) -> Void) {
    if arg.isEmpty {
        completion(nil, ExampleError.emptyString)
    } else {
        completion(arg, nil)
    }
}

But this has a few issues:

  1. Verbosity - (String?, Error?) -> Void) is both ugly and long
  2. Developer Error:

Because String and Error are both optional, one could actually include both or neither in the result.

completion(nil, nil)
completion("Result", .someError)

Or, the consumer could forget to check one or the other.

fetchStringAsyncWithNormalClosure(arg: "someString") { str, error in
    guard let s = str else { return }
}

Really, our completion result should be exclusive. Either the result was successful or there was an error. Most of the time there is no middle ground. So, lets model that in an enum.

 public enum Result {
    case success(String)
    case error(Error)
 }

But there is one problem here…what if we want this result to be an Int? Or a Bool? Or really any other type? Here is where generics come in…instead of tying this enum down to just a single type, we can simply use generic type T. Like this:

public enum Result<T> {
    case success(T)
    case error(Error)
}

Now, Result can take be defined as any type T at compile time. Here is what our fetch function can look like now:

 internal func fetchStringAsyncWithResultClosure(arg: String, completion: (Result<String>) -> Void) {
    if arg.isEmpty {
        completion(.error(ExampleError.emptyString))
    } else {
        completion(.success(arg))
    }
 }

This is looking better already. Shorter syntax, no optionals, and exclusive results. The best part is we are adhering to the DRY principle by not repeating our Result enum for each type we might want to use it with. We simply write once and reuse.

But…we can take this one step further with the help of generics and typealiases. Though (Result String) -> Void) is certainly better than (String?, Error?) -> Void what if we could could get a little more concise and take out the ugliness of closure syntax? This is definitely a job for typealiases. But did you know that they can be generic as well?

public typealias ResultClosure 'T' = (Result 'T' ) -> Void

Now we can simply write our fetch function like this:

internal func fetchStringAsync(arg: String, completion: ResultClosure<String>) {
    if arg.isEmpty {
        completion(.error(ExampleError.emptyString))
    } else {
        completion(.success(arg))
    }
}

Nice! And we can now call it like this:

fetchStringAsync(arg: "someString") { result in
    switch result {
    case .success(let str):
        print(str)
    case .error(let error):
        print(error)
    }
}

As you can see, generics can really help eliminate duplication of code as well as improve syntax.

But generics don’t need to simply be restricted to data structures. They can be used in functions as well! For instance, lets say I need to write a function that takes in an array of Ints, and returns true if all the Ints are equal, and false if they are not. This function might look something like this:

func areAllElementsEqual(_ elems: [Int]) -> Bool {
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        if elem != lastElem {
            return false
        } else {
            lastElem = elem
        }
    }
    return true
}

This code will certainly work, which is great. We have accomplished our task and can now move on. But…what if we now need the code to check if all Strings in an array are equal? Well…our first thought might be to simply copy and paste the previous function but modify the signature to accept an array of Strings instead of Ints. But that seems like an awful waste. We could also rewrite our function to accept and array of Any. Lets look at that:

func areAllElementsEqual(_ elems: [Any]) -> Bool {
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        switch elem {
        case is Int:
            if (elem as? Int) != (lastElem as? Int) {
                return false
            } else {
                lastElem = elem
            }
        case is String:
            if (elem as? String) != (lastElem as? Int) {
                return false
            } else {
                lastElem = elem
            }
        default:
            fatalError("This type cannot handle String(describing: elem)")
        }
    }
    return true
}

Notice, we need to switch on elem here to check the type. The operator == cannot be called on type Any for practical reasons, so we need to try to cast to any potential types we are interested in. If we haven’t matched that type, then we have to throw some sort of error, because…well what else would you do? Now, if you want to add any more types to this you simply add to the switch statement. Double? Give it another case. Float? Give it another case. Dog? Give it another case. This certainly is not very DRY. We are rewriting the same code over and over again! Not to mention, by using Any we are opening ourselves up to all sorts of runtime bugs that the compiler cannot catch. Wouldn’t it be nice if instead of declaring Any as our type of array, we could instead declare some arbitrary type? Well…generics to the rescue.

Lets rewrite this function with generics in mind.

func areAllElementsEqual<T>(_ elems: [T]) -> Bool {
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        if elem != lastElem { // compiler error - Binary operator '!=' cannot be applied to two 'T' operands
            return false
        } else {
            lastElem = elem
        }
    }
    return true
}

Now, this code won’t compile, but we will get to that later. Lets take a look at that T. Sitting between the angle brackets is our generic type. It doesn’t have to be called T. It could be anything we want. T is just a convention, but we could be more descriptive and call it Element. Names aside, what we are doing here is declaring that this function will be making use of a generic type. We must utilize type T somewhere in our function signature, so we will use it to declare our Array type. Now, we don’t need to worry whether our input is an array of Strings, Ints, or Doubles, as long as it is specified at compile time. But, there is something wrong here…this code will not compile. Why? Because not every type has the == operator defined! If I make a custom struct Dog but never implemented ==, this would never work. Thankfully, Swift is smart and type safe, so it catches this and will stop the build. So then, how can we guarantee that all elements T will have == defined?

Type Constraints

Some times just having a plain old type T generic will not cut it. It works fine for objects like Arrays, but that is because you don’t really need and specific functionality from an element in an Array. We just need it to exist. But for our cases, we need a way to ensure that our element can be equated to the other elements in the Array. This is where type constraints come in. Constraints allow us to restrict T down to a smaller subset, and in turn, T gains the functionality of that subset.

If you’ve been writing Swift code for even just a short time, you have probably come across the Equatable protocol. This is a rather simple protocol that requires the conforming type to implement the == operator. (I highly recommend making all of your types Equatable as well as Hashable - and with Swift 4.1 its dead easy.) So, if all elements conforming to Equatable have == implemented, lets leverage that. Lets rewrite our function using T with a constraint saying that T must be equatable.

func areAllElementsEqual<T: Equatable>(_ elems: [T]) -> Bool {
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        if elem != lastElem { // compiles!
            return false
        } else {
            lastElem = elem
        }
    }
    return true
}

Great, this code will now work. We have added the constraint Equatable to T, and now we can use == or != on our elements. We have now made our function fully generic and safe.

Let’s try it out!

areAllElementsEqual<Bool>([true, false, false, false]) // compiler error - Cannot explicitly specialize a generic function
areAllElementsEqual([1, 1, 1, 1]) // true
areAllElementsEqual(["Hello", "Hello", "Hello", "Hello, World"]) // false

Notice that first call explicitly specifies that this function will be using the Equatable type Bool. This is actually not allowed, but don’t worry, the compiler will infer the type for you!

Also note in this function that there is no chance for a runtime error because we have guaranteed that only Equatable types can use this function. If we were to try and compile this with a non-Equatable type, we would get an error.

areAllElementsEqual(
    [
    SomeNonEquatableType(), 
    SomeNonEquatableType(), 
    SomeNonEquatableType(), 
    SomeNonEquatableType()
    ]
    ) // compiler error - In argument type '[SomeNonEquatableType]', 'SomeNonEquatableType' does not conform to expected type 'Equatable'

Associated Types

After reading through our example, you might be wondering, “Hey Luke, why can’t we just use Equatable as the argument type for the elems Array?” Good question! Let’s take a look at this. Say we re-wrote our function to do just that:

func areAllElementsEqual(_ elems: [Equatable]) -> Bool { //Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        if elem != lastElem { // compiles!
            return false
        } else {
            lastElem = elem
        }
    }
    return true
}

Wow, no need for generics. Nice! Uh-oh - compiler error :/ But why? This error can be quite confusing to see at first, but the bottom line is this: If a protocol has an Associated Type (which includes a reference to Self) then that protocol cannot be treated like a type. I.e. it cannot be cast to, used as an argument type, etc. It can only be used as a generic constraint. So then…what is an associated type? And why do they restrict the protocol so much?

What are Associated Types

Essentially, associated types are generics for protocols. In the same way that you can use generics to define some type T in a function or class, so too can you use Associated Types to define some generic type to be used in a protocol conformance. Let’s look at an example. Say we are defining some models to represent different smart phones. Some smart phones have biometric authentication. So we decide to create a BiometricAuthenticatable protocol like this:

struct iPhone8 {}
struct iPhoneX {}

protocol BiometricAuthenticatable {
    func authenticate(with data: ??) -> Bool
}

extension iPhone8: BiometricAuthenticatable {
    func authenticate(with data: ??) -> Bool {

    }
}

extension iPhone10: BiometricAuthenticatable {
    func authenticate(with data: ??) -> Bool {

    }
}

As you can see, we have a problem here. What data do we pass into the authenticate method. For an iPhone X we will need facial scans. For an 8 we will need fingerprint scans. In the future, who knows what data we might need. How can we make this generic? Well, associated types of course!

struct iPhone8 {...}
struct iPhoneX {...}

protocol BiometricAuthenticatable {
    associatedtype BiometricData
    func authenticate(with data: BiometricData) -> Bool
}

extension iPhone8: BiometricAuthenticatable {

    typealias BiometricData = FingerprintScanData
    func authenticate(with data: BiometricData) -> Bool {
        ...
    }
}

extension iPhone10: BiometricAuthenticatable {

    typealias BiometricData = FacialScanData
    func authenticate(with data: BiometricData) -> Bool {
        ...
    }
}

Now, we can use our BiometricAuthenticatable protocol on any device so long as it defines which type of data it expects to receive. You can even make this more DRY by moving the default implementation into a protocol extension with where clauses:

struct iPhone8 {...}
struct iPhoneX {...}

protocol BiometricAuthenticatable {
    associatedtype BiometricData
    func authenticate(with data: BiometricData) -> Bool
}

extension BiometricAuthenticatable where BiometricData == FingerprintScanData {
    func authenticate(with data: BiometricData) -> Bool {
        ...
    }
}

extension BiometricAuthenticatable where BiometricData == FacialScanData {
    func authenticate(with data: BiometricData) -> Bool {
        ...
    }
}

extension iPhone8: BiometricAuthenticatable {
    typealias BiometricData = FingerprintScanData
}

extension iPhone10: BiometricAuthenticatable {
    typealias BiometricData = FacialScanData
}

Because we have moved our implementations of authenticate into protocol extensions, we now have no need to implement them in the device structs themselves. This makes adding more devices even easier!

extension iPhone8Plus: BiometricAuthenticatable {
    typealias BiometricData = FingerprintScanData
}

extension iPadX: BiometricAuthenticatable {
    typealias BiometricData = FacialScanData
}

And when we call these functions, we will now see that auto complete suggest the proper signatures. Awesome!

let iphoneX = iPhoneX()
iphoneX.authenticate(with: < xcode suggests FacialScanData>)

let iphone8 = iPhone8()
iphone8.authenticate(with: < xcode suggests FingerprintScanData>)

 What is Self?

Remember our previous compiler error with equatable?

Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements
We now know what associated type requirements are. But what is that Self? And why does it have a capital S?? Well, Self is kind of similar to any old Associated type, expect it is more specific. Self refers to the type that is actually conforming to the protocol, whichever type that may be. For instance, String conforms to Equatable. So in the case where the Equatable protocol refers to Self and String is the object we are equating, Self means String. When we are equating to Ints, Self means Int. Lets take a look at the signature for == in Equatable:

public static func ==(lhs: Self, rhs: Self) -> Bool

This is exactly like using a generic or an associated type. This is essentially saying that == takes in two values of the same type Self - which can be anything that conforms to equatable, and returns a boolean. So if we write "Hello" == "Hello"then Self is a String, but when we say 25 == 25 Self is an Int. Self gives your protocols a way to have a generic reference to the conforming type. Great! Self can also be very useful for protocol extensions. For instance, because of Self, we can write awesome protocol extensions that only apply to certain types. So, lets say we want to create a protocol called Shakeable that adds the ability to shake some view. For now, we only really care about the implementation for UIViews. With Self, we can do this:

protocol Shakeable {
    func shake()
}

extension Shakeable where Self: UIView {
    func shake() {
        ...
    }
}

Now any UIView that conforms to Shakeable will get this implementation of Shakeable for free. Not only that, but because this extension constrains Self to UIView, we can actually use functions and properties of UIView in our implementation. Nice!

Why are Associated Types and Self So Constraining?

Lets get back to our original problem. Why, when we try to use Equatable as a concrete type, does the compiler through this error?

func areAllElementsEqual(_ elems: [Equatable]) -> Bool { // compiler error - Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements
    guard var lastElem = elems.first else { return false }
    for elem in elems {
        if elem != lastElem { // compiles!
            return false
        } else {
            lastElem = elem
        }
    }
    return true
}

Well, think of it this way. This code says that as long as the elements conform to Equatable, they can be apart of this array. This would mean that our array may not necessarily be homogeneous, that is, this array could potentially be a mix of Strings, Ints, Doubles, etc. So, in a magical world in which the above code actually compiles, lets follow that outcome a little farther. Say we give the following input:

areAllElementsEqual(["Hello", 1, "heterogeneous", 2.0, "array"])

Now, our function loops through and trys to equate “Hello” and 1. How would this work? Strings and Ints can never be equal. So really it doesn’t make sense for the compiler to even allow us to make this comparison in the first place. Hence, the compiler restricts protocols with Self or associated type requirements to only be used as generic constraints, not as stand alone types.

Another example of this can be easily seen with our BiometricAuthenticatable protocol. Let’s say I wanted to do the following:

let authenticatable: BiometricAuthenticatable = iPhoneX() // compiler error - Protocol 'Equatable' can only be used as a generic constraint because it has Self or associated type requirements

authenticatable.authenticate(???)

This example doesn’t really make sense, because we are declaring authenticatable as a generic BiometricAuthenticatable and at run time we are assigning the specific implementation iPhoneX. Because this assignment happens at runtime, the compiler has no idea what the data parameter’s type should be. Should it be FacialScanData? Or FingerprintScanData? Who knows! So all in all, it makes sense for the Swift compiler to just disallow using protocols with associated types as concrete types all together.

Conclusion

We have now explored many aspects of generics in Swift and I hope you have learned a thing or two. Generics can be a great way to keep your code safe and DRY. Whether you are creating new objects, writing functions, or defining protocols, a well placed generic can save you a lot of duplicate code and eliminate runtime bugs.

What do you think? Will you try to use generics more in the future? What ways do you use generics in your code? Do you have any questions, feedback or comments to give? I’d love to hear from you.

-Luke Street
@ldstreet

]]>
Playing with Protocols, Pt. 2: Foundational Support https://firesideswift.fireside.fm/articles/playing-with-protocols-part-2 Thu, 12 Apr 2018 00:00:00 -0700 [email protected] 339813cb-2b24-467a-a885-1c94ee5e13f8 Hey all!  I'm happy to have you back, and thanks for taking an interest and reading!  If you missed part one, it can be found here.

I realize now that I never described what a protocol is in Swift in the first post, but was able to get away with it since the first post just covered design.  Allow me to rectify that right now.  According to the Apple documentation a protocol “defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality.”  That protocol can then be adopted to a class, struct, or enum that will be responsible for giving the blueprints some implementation. One of the largest benefits with using protocols over classes is that you run the risk of having to update a lot of code if your architecture changes in the future.  Since nothing is implemented by the protocols themselves, the classes, structs, or enums that conform to them are less coupled.  Now that that’s out of the way and we’ve got an idea of where we want our protocols to be, let’s fire up Xcode and actually start developing!  

Side Note:  A quick update before we get started.  I renamed the fight() function from the previous post to attack().  It makes more sense when writing code to say Character A attacks Character B.  It’s basically a more descriptive term.  Now, on with the post!

I used the standard “Single Page Application” to get the app off the ground. The first thing I like to do when creating a new application is organize my files in Xcode.  To that end I created three groups to store my files; Enumerations, Protocols, and Structs.  I’ll probably add more as time and development goes on, but this is a good way to start.  

Protocols_pt2_01

I believe it makes the most sense to start with the most abstract protocol for the application, which in our case is BasicCharacter.  Reviewing the UML diagram from the last post I know I’ll need six attributes and three functions.  To create a protocol, first create a new Swift file.  Name it whatever you’d like and Xcode will produce a file with some general boilerplate code and a single import statement.  Use the keyword ‘protocol’ and followed by the protocol’s name and you’re ready to go!  In our case it will look something like this:

import Foundation

protocol BasicCharacter {

}

Now we name all of the attributes and write out the functions.

Protocols_pt2_02

Well that’s…..sub-optimal.  There are errors everywhere!  What’s happening?  Taking it from the top, I used the ‘let’ keyword to define the name attribute.  This makes sense if I don’t want the user to have a chance to change it after it’s been set.  A character’s name should be constant.  There’s a debate that can be made that we should be able to change it in some way (people do change their names after all) but for the purposes of the game, I would rather not allow that to happen.  Xcode disagrees with me and there’s a reason for that.  If you refer to the definition of a protocol that Apple provides from the first paragraph, a protocol should have no impact on how a class, struct, or enum conforms to it.  By stating an attribute is a constant by using ‘let’, the protocol would be forcing any conforming classes to also use let.  It is still valid to set the attribute as a constant in the conforming class or struct (spoiler alert: it’s what I do).  All other errors are the same, the property should have at least a { get } and possibly a { get set } specifier.  These keywords determine whether the attribute is read or read-write.  Something to keep in mind when dealing with { get set } is that it must be settable, meaning that the attribute must be a var.  If we were to only use { get }, as is the case for name in the picture below, then when we define the property in the conforming class or struct, it can be a let.  Let’s clean this up.

Protocols_pt2_03

That’s much better!  Now it’s time to add the function signatures.

Protocols_pt2_04

No issues here (spoiler alert 2: yet).  Now we repeat this process for our other most-abstract protocols; Item and Objective.

import Foundation

protocol Item {

var strength: Int { get set }
var agility: Int { get set }
var intelligence: Int { get set }
var durability: Double { get set }
var isBroken: Bool { get set }

func breakItem()
func repairItem()
}

import Foundation

protocol Objective {

var experience: Int { get }
var reward: Item! { get set }

}

Good job!  We’ve just created the foundation for the rest of the app.  Think about our design from the first post now.  Hero and Enemy both inherit from BaseCharacter and Weapon and Armor both inherit from Item.  How do we express that in our code?  It’s actually simple.  We say that the sub-protocols conform to the foundational protocols.  In the case of Hero we would have:

import Foundation

protocol Hero: BasicCharacter {

    var inventory: [Item]! { get set }

    func loot(theEnemy: Enemy)
    func equip(theItem: Item)
    func complete(theObjective: Objective)

}

And for Enemy it’d be:

import Foundation

protocol Enemy: BasicCharacter {

    var loot: Item! { get set }

    func flee()

}

Following suit with Weapon and Armor we get:


import Foundation

protocol Weapon: Item {

    var minDamage: Int { get set }
    var maxDamage: Int { get set }
    var type: WeaponType { get }

}

import Foundation

protocol Armor: Item {

    var value: Int { get set }
    var type: ArmorType { get }

}

What you’re seeing here is an elegant and powerful idea.  Each sub-protocol will take on the responsibilities of the foundational protocol.  What this means in practice is that if I were to create a Hero struct, I wouldn’t need this:

struct HeroStruct: Hero, BasicCharacter {
...
}

But rather I could do this: 

struct HeroStruct: Hero { ... }

It seems simple with the given example but the fact conforming to the terminal protocol in a waterfall-like series of other protocols will hold a class, struct, or enum responsible for all of the “methods, properties, and other requirements (to quote Apple)” laid out in the protocols before it make life easier on us as developers.  We have less of a chance to miss out on conforming to a protocol if there are fewer protocols of which to conform.  Y’all know me, the less I have to think about configuration and setup like this, the better off I am!

I had more planned for this post but it’s already gotten long.  I’ll move on to creating the structs in post three!  I have also created three more protocols that conform to Hero and three more that conform to Enemy.  I wanted to have something more concrete to play around with when writing the code.  For Hero they are; Wizard, Warrior, and Archer.  For Enemy they are; Orc, Goblin, and Android (yeah I went there).  I’ll cover these more in post three as well (I told you I had a lot planned haha).  In the meantime, see if you can write up your implementations of these.  I’d love to hear what types of attributes or functions you think each should have!  

Yours in code,

Zack

]]>
Having Fun with UIDynamicAnimator https://firesideswift.fireside.fm/articles/having-fun-with-uidynamicanimator Thu, 05 Apr 2018 00:00:00 -0700 [email protected] dad4f795-edc5-4421-a1eb-497f9b29e237 The beauty of having a blog instead of just a podcast is now, I can show you all some cool stuff that isn't as easy to explain over audio. One obvious thing is any topic to do with views. This particular topic is actually from a Learn Swift LA meetup that I led back in May of last year. It came about because someone introduced me to the UIDynamicAnimator, and I found it so fun and easy to learn that I needed to show it off in the next meetup.

Here's what we're going to do. We're going to make an air hockey game. This works best on an iPad, but you can totally play this on your iPhone as well. Air hockey is a two player game, so be sure to grab friend when you're done and totally dominate them with your superior coding and mad puck skills! To get the most out of this post code along with me. I'll give you step-by-step instructions all the way down.

Start by creating a new Xcode iOS project. Choose a new Single View App and click Next.

UIDynamicAnimator_01

Let's name our project AirHockey. Don't forget to set the Language to Swift. We also don't need Core Data or tests for this project, so you can safely uncheck those before clicking Next.

UIDynamicAnimator_02

Pick a place to save your new project and click Create. Now we're really ready to begin.

Now this app will work best on iPad, but it can most definitely work on iPhone as well. So let's keep the Devices set to "Universal", but we don't want the screen to rotate at all, so only have Portrait checked. I don't remember the earliest SDK we can use for this app, but I do know it works well with iOS 10, so let's set that as our Deployment Target.

I like working with Storyboards. This part sucks for blog posts, because there's a lot to do, but I don't want to have a million screenshots. We're only going to add 7 UIViews to our initial ViewController, so it shouldn't be too bad. Start by adding the center view. It's hard to see in the screenshot below, but it's 3 pixels high, centered vertically, and spans the width of the view. Then add the top view. Constrain it to the edges of the Safe Area and the top of the center view. Finally add the bottom view, and constrain it to the edges of the Safe Area and the bottom of the center view. I made the colors red and green to make them more obvious. We want the final color to be white like a hockey rink.

UIDynamicAnimator_03

Now we need to add the goals. Like I said earlier, these work better for the iPad, but will totally still work for iPhone as well. I'm using an iPhone layout, because it's easier to see in a blog post. The goals are just UIViews like the other views we added. Now this is VERY important. These views must be subviews of the ViewController's view not subviews of the top and bottom views we added earlier. Note the hierarchy on the left outline panel. For sizes, I'm setting the height to 40 and the width to 200. Feel free to set them to your preference.

UIDynamicAnimator_04

Finally we need to add the paddles and the puck. Just like before, we need to have all our objects in the same view hierarchy. Constrain your puck to the vertical and horizontal centers of the view. For the paddles, constrain them to the horizontal center of the view and about 100 px from the top or bottom of the view respectively. I used a size of 100x100 for my paddles and 50x50 for my puck, but you can play with that to see what works for you. When you finish this step, run your code to make sure everything looks the way you want it to look.

UIDynamicAnimator_05

Next, connect outlets to all of these views, so we can reference them in our code. For those of you who haven't done this before, with the assistant editor open, you can hold the control button, click on a view, and drag into your code. A dialog box will appear to let you name your new outlet.

UIDynamicAnimator_06

Alright, now all the view setup is done, we can start playing with code. Navigate to your ViewController class, and add this property near the top:

class ViewController: UIViewController {

    var animator: UIDynamicAnimator!

    ...
}

This UIDynamicAnimator is the key to making this all work. It's pretty amazing how much power you have just by adding this little guy to your code. To demonstrate, let's add a little gravity. Add this to your viewDidLoad() method and run it.

override func viewDidLoad() {
    super.viewDidLoad()

    animator = UIDynamicAnimator(referenceView: view)

    let gravityBehavior = UIGravityBehavior(items: [puck])
    animator.addBehavior(gravityBehavior)
}

Your puck just fell off the screen! Now that's interesting. We can even control the direction and magnitude of gravity. Add this to your code now and run it.

    let gravityBehavior = UIGravityBehavior(items: [puck])
    gravityBehavior.gravityDirection = CGVector(dx: 0, dy: -1)
    animator.addBehavior(gravityBehavior)

Now the puck flies up through the top of the screen! So much power!!! But it does suck that the puck leaves the screen, so let's make it stay in our view. We'll need this for our game anyway. Add a collision behavior.

    let collisionBehavior = UICollisionBehavior(items: [puck])
    collisionBehavior.translatesReferenceBoundsIntoBoundary = true
    animator.addBehavior(collisionBehavior)

Now the puck stops at the top of your view with a little bounce. That's pretty nice. We want the puck to collide with the paddles, so let's add them to this same collision behavior.

    let collisionBehavior = UICollisionBehavior(items: [puck, topPaddle, bottomPaddle])
    collisionBehavior.translatesReferenceBoundsIntoBoundary = true
    animator.addBehavior(collisionBehavior)

This is interesting. Now the puck runs into the top paddle, but the gravity still exists, and the puck pushes the paddle to the top of the view where it stays. This is close to what we want, but we really don't need the gravity for our game, so let's remove it.

//        let gravityBehavior = UIGravityBehavior(items: [puck])
//        gravityBehavior.gravityDirection = CGVector(dx: 0, dy: -1)
//        animator.addBehavior(gravityBehavior)

What we really want is to control our paddles. For this we need some pan gesture recognizers. From the storyboard, add a pan gesture to the topHalf, topPaddle, bottomHalf, and bottomPaddle views. Be sure to label them each time you add them, or it will get very confusing when we connect them to their actions.

UIDynamicAnimator_07

Create a new IBAction called userPannedTopHalf() and connect both the topHalf and topPaddle pan gestures to it. Make sure to set the type to UIPanGestureRecognizer when you create the new action.

UIDynamicAnimator_08

Do the same thing with the bottom pan gestures, but create an IBAction called userPannedBottomHalf. Add this code your ViewController, and run your code.

class ViewController: UIViewController {
    ...
    var topSnapBehavior: UISnapBehavior?
    var bottomSnapBehavior: UISnapBehavior?
    ...

        @IBAction func userPannedTopHalf(_ sender: UIPanGestureRecognizer) {
        if topSnapBehavior != nil {
            animator.removeBehavior(topSnapBehavior!)
        }

        switch sender.state {
        case .began, .changed:
            topSnapBehavior = UISnapBehavior(item: topPaddle, snapTo: sender.location(in: view))
            animator.addBehavior(topSnapBehavior!)
        default:
            break
        }
    }

    @IBAction func userPannedBottomHalf(_ sender: UIPanGestureRecognizer) {
        if bottomSnapBehavior != nil {
            animator.removeBehavior(bottomSnapBehavior!)
        }

        switch sender.state {
        case .began, .changed:
            bottomSnapBehavior = UISnapBehavior(item: bottomPaddle, snapTo: sender.location(in: view))
            animator.addBehavior(bottomSnapBehavior!)
        default:
            break
        }
    }
}

Now when you move your finger in the bottomHalf of the screen, the bottom paddle snaps to your finger. You can then use this to hit the puck. Pretty cool, huh? And you can do this with the top paddle as well. You can even "throw" your paddle like Thor throws his hammer, and like his hammer, it'll come right back to you by moving your finger a little bit on your side.

This is a pretty good place to stop for this post. You have a functioning ice rink with no need for a zamboni. You have two functioning paddles with a puck. (Ok, they're square, which is a little weird.) Next time we'll make the paddles and the puck circular, enforce side rules, and add in a scoring mechanic. Feel free to try to do these things yourself in the meantime. Trust me, you'll learn more by exploring than by me telling you. Until then, happy coding!

-Steve

P.S. Here the final code, so you can check your code against mine.

]]>
Playing with Protocols, Pt. 1: Design https://firesideswift.fireside.fm/articles/playing-with-protocols-part-1 Thu, 29 Mar 2018 00:00:00 -0700 [email protected] 03f6e397-d537-4a5b-bd0b-024b25a7a6d7 Hey all!  Today I'm going to revisit a topic covered in one of our older episodes, protocols!  That episode was a fun one to prepare for, as I spent most of the week leading up to it building out a (small) game engine.  The code can be found here.  That's right, we used to have a Github that we uploaded code to for a little bit.  Maybe we'll get back to that in the future.  We actually had a misstep in that episode that we have yet to repeat since recording it.  We read nearly that entire playground.  It wasn't fun for us and we don't think it provided our listeners with good content.  I still wanted to talk about it though, and now that we've got the blog....why not do it here???  I'm envisioning the Playing with Protocols group of blog posts to cover creating a game engine using protocols, from design to implementation, using the old playground I wrote as a template.  

Before I get started on any project I like to design the parts of my system.  I view it as the foundation on which the project will be built, and in all honesty, it's one of my favorite parts of writing software.  It's a time to show off your creativity, and build something beautifully.  You can set yourself up for success if you design your project correctly.  The flip side of that is also true.  Hasty designs or ill-considered assumptions lead to flawed projects where you spend more time fixing bugs than you do implementing features, and no one wants that!  

Step One: Begin at the beginning

You would think this goes without saying, but in practice it can be somewhat hard to do.  Decide what you want to build, then figure out how you are going to build it.  The goal at the end of this series of posts is to have a game engine for a role-playing game (RPG).  For those who may be unfamiliar with what an RPG is, it is generally a game in which the player will have some grand objective but initially lacks the skills to complete that objective.  Sound familiar?  To me it's analogous to my coding journey.  When I first sat down to code I didn't know the difference between a stack and a queue.  When I started learning Swift I couldn't tell you how to create a dictionary, much less make a call to a backend database such as Firebase, and then parse the results.  All of this I learned with time.  Sean Allen does a great job of explaining it in his video here.  

Where does that leave us now?:  Starting is usually the hardest part of any project for me, and I don't think I'm alone in that.  The trick is to break your idea down to it's most atomic parts and then turn those into features.   What happens in an RPG?  A player will usually choose between a set of specializations for their character to be, such as Wizard, Warrior, Archer, etc.  The player will guide the character through smaller objectives while fighting enemies (or not) to collect experience.  As the character gains experience it will become stronger and be able to defeat stronger enemies and clear tougher objectives.  Repeat this process until the character is strong enough to tackle the final objective.  Some companies have made literally billions of dollars off of this model (looking at you Blizzard).  

Have we accomplished anything yet?:  It may not feel that way now, but by defining our goal even in abstract terms as above, we start to get a clearer picture of what we need to build for our project.  There will be a character, enemies, items and objectives.  Let's take a look at these individually:

Characters:

What makes a character?  Let's say our character has these attributes: a name, health, experience, strength, agility, and intelligence.  We also know that a player can choose a type for a character to be.  Of course that's not all a character is.  It would be a pretty boring game if the character sat on the screen the entire time unable to do anything!  Each character will also be associated with a group of actions.  A character can fight, heal, loot an enemy, and (hopefully not) die.  

Enemies:

We need to have some opposition!  Enemies will be of a certain type and will have a name, health, strength, agility, intelligence, and will give the player some amount of experience after they are defeated.  They too can fight and heal. In addition to those actions an enemy can also flee.  We're already starting to see similarities between our characters and the opponents that they will face.  Things are shaping up!

Items:

If a player can loot an enemy and an enemy can be looted, then we need to have items to facilitate this action.  What adventurer doesn't like a pile of glittery treasure?  Items should definitely be of more than one type, and should also have stats on them.  It feels as if things are getting complicated again, so let's break it all down one more time.  An item could be a piece of armor or a weapon.  Depending on type, it will have different stats.  

Objectives:

Objectives will have certain goals for characters to meet before they can be completed.  They will reward characters with experience and possibly an item.

Step Two: Abstractualize

I'm pretty sure that's a word.  Now we start getting into the protocol portion of the post.  Look at every possible object we've broken down so far.  Extract commonalities to the most abstract protocol and then refine from there.  Both characters and enemies have names, health, experience and basic stats.  They also share the fight, heal, and die actions.  If we create an abstract protocol that contains these traits and actions then we will be forced to define similar functionality for the structs which implement them.  Items are broken down into weapons and armor, but again, there are shared traits between them such as the types of stats present.  In the current design objectives stand on their own.

Step Three: Visualize

This is when each of the designs covered so far become more concrete.  Simply the act of getting the design out of your head and onto something visual helps you decide where to take your application.  There are plenty of times when designing a program one way of doing things may seem to make sense up until the point the ideas flow from your head and onto (possibly metaphorical) paper.  I used draw.io to create the following Unified Modeling Language (UML) diagram.  What is UML?  It's basically a picture of your program's architecture. 

Character UML:

Protocols_pt1_01

These diagrams are the blueprints of the application.  Reading these make sense once you've walked through a few them.  Each box is broken down into three sections with the topmost being the name, the middle being the attributes, and the final being the functions.  A diagram may omit the second or third section (as the Enemy diagram has done with the second section), but must always have the first.  Let's look at what we've got here.  There's an abstract protocol named BasicCharacter.  It has the attributes we mentioned earlier in the second section, with the function signatures in the third section.  This protocol is inherited by two other protocols, the Hero, and the Enemy.  In addition to the what's defined in the BasicCharacter, these two protocols will force you to build out additional functionality based on whether you adhere to the Hero or the Enemy.

Item UML:

Protocols_pt1_02

The item UML diagram is very similar to that of the character UML diagram. All items will share the basic strength, agility, intelligence, durability, and isBroken traits. An item is also responsible for breaking down and repairing. We will have two flavors of item in our game engine, weapons and armor. There's a need to define two protocols specific to each type of item because they will fill very specific roles. It wouldn't make sense (in most cases) to have a weapon that also had some armor value, just as armor should not have damage attributes (again in most cases) (yes I know getting punched with a gauntlet wouldn't be enjoyable). The final attribute in each protocol will be an enumeration I'll cover next article.

Objectives UML:

Protocols_pt1_03

The final protocol I've defined is for objectives. These are going to be straight forward in that they just hold attributes for what a player would get if he or she completed the objective. The reward is an optional object that conforms to the Item protocol because not all objectives will have an associated reward.

What's next?

These protocols are great, but they don't do much on their own. Literally. There's no implementation at all. Next up we're going to tackle writing out our protocols and creating the structs that adhere to our them.

I hope you enjoyed my first post as much as I enjoyed writing it! Are there flaws in my design? Most definitely! That's part of the process and it's very important to keep it in mind. Don't become rigid in your first design because it can (and most likely should) change. With that being said, let me know what you think of it so far in the comments below. These posts aren't planned ahead so you're getting an inside look at building this engine out first hand. It will get ugly at times. Again, that's part of the process. There are very few (read: NONE) developers I know that can sit at a computer, fire up an IDE, and write a flawless program from start to finish. All of the great developers do share one trait though; they absolutely love the process. Writing code isn't as romantic as it's portrayed in pop culture. Keep working at it and you'll become great as well.

-Zack

]]>
Welcome to the Fireside!!! https://firesideswift.fireside.fm/articles/welcome Thu, 22 Mar 2018 00:00:00 -0700 [email protected] 3febf73f-52cd-4e95-9851-9d71e7d1d167 Hey everyone!

Welcome to the Fireside Swift blog. This will not only be a place for us to create textual content to go along with episodes (everyone could finally SEE examples of code!) but we would like it to also be a home where you, our listener, has a chance to not only interact with us more easily but each other as well. We're aiming to build a community here, and we can't do it without you. Let's light the kindling and create something special. Come along!

  • Steve and Zack
]]>