Natasha The Robot https://www.natashatherobot.com Fri, 07 Jul 2017 11:06:10 +0000 en-US hourly 1 https://wordpress.org/?v=4.7.2 42122868 The Easiest Way to Get a URL for your Apple Wallet Passkit Pass https://www.natashatherobot.com/url-apple-wallet-passkit-pass/ https://www.natashatherobot.com/url-apple-wallet-passkit-pass/#comments Fri, 07 Jul 2017 11:06:10 +0000 https://www.natashatherobot.com/?p=6602 For try! Swift NYC, we are playing with the idea of adding QR codes to conference badges that will add attendee’s contact information business card to your Wallet app. It is easy to generate a QR code from a URL. And it is relatively easy to create a PassKit Pass – it’s basically just a bunch of files in a folder. But I was super confused on that step in-between – how do I create a link from the final Passkit .pkpass file…

The Distributing Passes instructions include the following information about this:

So what the hell is a MIME type?!!! I have no idea. But googling around for it, I learned that I couldn’t set it for my jekyll website. I needed another solution!

So after the initial panic and confusion, I took a breath and stepped back. I realized that .pkpass is simply a file. All that was needed was a link to this file (and that MIME type thing, but I chose to ignore it at that moment). Immediately, my thoughts went to Amazon Services, with S3 as the perfect file storage solution.

So I signed-in and created the tryswiftpasskit bucket. I set the location to US East (N. Virginia), since the conference would take place in New York, so that’s the closest.

I went through the rest of the bucket creation process and finished. I then went inside my bucket and clicked the Upload button. I downloaded the Sample Passes listed in their tutorial, so I had one ready to test!

I set the permissions to public, since the link has to be public in order to get the .pkpass file to download into the Wallet app:

And now the magical part… remember that whole MIME type thing?!! From googling around, I figured out enough to set is as the Content-Type property on the file:

It should be set to application/vnd.apple.pkpass. Save and finish and you’re ready to go!

The final step is to get that link. Click into the object in your bucket, and you’ll see it right there for you:

That’s it! Open that link in Safari or via the Mail app (or generate a QR Code) and you’ll see the pass 💃🏻

]]>
https://www.natashatherobot.com/url-apple-wallet-passkit-pass/feed/ 6 6602
Swift: Alternative to Default Implementations in Protocols https://www.natashatherobot.com/swift-alternative-to-default-implementations-in-protocols/ https://www.natashatherobot.com/swift-alternative-to-default-implementations-in-protocols/#comments Sun, 26 Mar 2017 23:51:49 +0000 https://www.natashatherobot.com/?p=6592 I recently wrote about why I prefer not to use default implementations in Swift protocols.

TL;DR

  • I want a lot of conscious thought put in into each method of the protocol – adding a default implementation will make it easy to forget and not think about much. Oh, and the compiler won’t complain!
  • I want it to be easy to make changes in the implementation. If a method is not included in the file because the implementation is in the protocol, it’s more work to make the decision to add that into the file. The assumption is that the default should win. If it’s already there, it’s easier to just go in and make a small change.
  • Similar to the above point, I want it to be super readable where each method is coming from. If a bunch of methods are in the default implementation of the protocol, it’s not as clear just by looking at the file.

But there might still be repeating or “default” implementations that are shared across protocol implementations. In my case, I approached this issue by creating an object with some variables that could be shared across protocols:

struct SessionDataDefaults {
        
    let title: String
    let subtitle = "try! Conference"
    let logoImageURL = Bundle.trySwiftAssetURL(for: "Logo.png")!
    let imageURL: URL?
    let location: String
    let summary = Conference.current.localizedDescription
    let twitter = Conference.current.twitter!
    
    init(session: Session) {
        title = session.localizedString(for: session.title ?? "TBD", japaneseString: session.titleJP)
        
        if let url = session.imageWebURL {
            imageURL = URL(string: url)
        } else if let assetName = session.imageAssetName {
            imageURL = Bundle.trySwiftAssetURL(for: assetName)
        } else {
            imageURL = nil
        }
        
        if let location = session.location {
            self.location = location.localizedName
        } else {
            self.location = Venue.localizedName(for: .conference)
        }
    }
}

However, when I wrote the article about it, @jckarter tweeted the following:

In my case, it didn’t make sense to create a separate protocol with default implementation because the use of the default values is pretty random – there isn’t any type of session that will use all defaults at the same time (this is one reason I didn’t want to include a default implementation in the first place).

But this did spark another idea! I could make my data defaults object conform to the same protocol as the objects where the defaults are being used – in my case, SessionDisplayable:

struct SessionDataDefaults: SessionDisplayable {
        
    fileprivate let session: Session
    
    init(session: Session) {
        self.session = session
    }
    
    var title: String {
        return session.localizedString(for: session.title ?? "TBD", japaneseString: session.titleJP)
    }
    
    var subtitle: String {
        return Conference.current.name ?? "try! Conference"
    }
    

    var logoURL: URL {
        return Conference.current.logoURL
    }
 
    
    var location: String {
        if let location = session.location {
            return location.localizedName
        } else {
            return Venue.localizedName(for: .conference)
        }
    }
    
    var sessionDescription: String {
        return "❤️".localized()
    }
    
    var presentationSummary: String {
        return Conference.current.localizedDescription
    }
    
    var twitter: String {
        return Conference.current.twitter!
    }
    
    var selectable: Bool {
        return false
    }
}

The best part about this is that when a method needs to use the default implementation, it can use the defaults method that has the same exact name as the method being implemented!

Here is an example of another SessionDisplayable using the new defaults:

struct CoffeeBreakSessionViewModel: SessionDisplayable {
    
    private let session: Session
    private let dataDefaults: SessionDataDefaults
    
    init?(_ session: Session) {
        if session.type == .coffeeBreak {
            self.session = session
            self.dataDefaults = SessionDataDefaults(session: session)
        } else {
            return nil
        }
    }
    
    var title: String {
        if let sponsor = session.sponsor {
            return String(format: "Coffee Break, by %@".localized(), sponsor.name)
        }
        return "Coffee Break".localized()
    }
    
    var subtitle: String {
        if let sponsor = session.sponsor {
            return sponsor.localizedName
        }
        // Matching variable name in the defaults
        // makes it super easy to use!!
        return dataDefaults.subtitle
    }
    
    var logoURL: URL {
        if let imageURL = dataDefaults.imageURL {
            return imageURL
        }
        
        if let sponsor = session.sponsor {
            return sponsor.logoURL
        }
        
        // Matching variable name in the defaults
        return dataDefaults.logoURL
    }
    
    var location: String {
        // Matching variable name in the defaults
        return dataDefaults.location
    }
    
    var sessionDescription: String {
        return "❤️".localized()
    }
    
    var presentationSummary: String {
        // Matching variable name in the defaults
        return dataDefaults.presentationSummary
    }
    
    var selectable: Bool {
        return session.sponsor != nil
    }
    
    var twitter: String {
        let twitter = session.sponsor?.twitter ?? dataDefaults.twitter
        return "@\(twitter)"
    }
}

I’m very happy with this solution. It’s super easy to use the defaults, but still allows for the compiler to complain when a protocol method is not implemented.

]]>
https://www.natashatherobot.com/swift-alternative-to-default-implementations-in-protocols/feed/ 3 6592
Swift: Why You Shouldn’t Use Default Implementations in Protocols https://www.natashatherobot.com/swift-use-default-implementations-protocols/ https://www.natashatherobot.com/swift-use-default-implementations-protocols/#comments Fri, 24 Mar 2017 13:19:34 +0000 https://www.natashatherobot.com/?p=6578 I’m currently doing a very big refactor of try! Swift Data now that the Tokyo conference is over and I have time to repay some technical debt. As part of the refactor, I’m removing a bunch of large switch statements for view-level display data and putting them into individual view models that conform to a strict protocol.

The Setup

The conference app has different sessions – talks, breakfast, lunch, announcements, etc. They are all displayed in a Table View with a title, subtitle, location, etc. Before, the data layer was messy like this:

//  Session.swift

import RealmSwift
import Foundation

@objc public enum SessionType: Int {
    case workshop
    case meetup
    case breakfast
    case announcement
    case talk
    case lightningTalk
    case sponsoredDemo
    case coffeeBreak
    case lunch
    case officeHours
    case party
}

public class Session: Object {
    /** The type of content in this particular session */
    open dynamic var type: SessionType = .talk

    // many other class properties here
 

    /***************************************************/

    // VIEW DISPLAY LOGIC BELOW

    /** The main name of this session */
    public var formattedTitle: String? {
        switch self.type {
           // VERY LONG SWITCH STATEMENT
           // LOTS OF DISPLAY LOGIC
        }
    }

    /** A follow-up tagline for the session */
    public var formattedSubtitle: String? {
        switch self.type {
           // VERY LONG SWITCH STATEMENT
           // LOTS OF DISPLAY LOGIC
        }
    }

    /** What image, if any is available for this session */
    public var logoURL: URL {
        switch self.type {
           // VERY LONG SWITCH STATEMENT
           // LOTS OF DISPLAY LOGIC
        }
    }

    /** The location for where this session will occur */
    public var formattedLocation: String {
        switch self.type {
           // VERY LONG SWITCH STATEMENT
           // LOTS OF DISPLAY LOGIC
        }
    }

    /** A long-form description of the session */
    public var sessionDescription: String {
        switch self.type {
           // VERY LONG SWITCH STATEMENT
           // LOTS OF DISPLAY LOGIC
        }
    }

    /** Presentation Summary */
    public var presentationSummary: String {
        switch self.type {
         // VERY LONG SWITCH STATEMENT
         // LOTS OF DISPLAY LOGIC
        }
    }
   
   // YOU GET THE POINT
   // MORE METHODS HERE WITH A LOT OF SWITCH STATEMENTS
}

So I extracted the data display methods into a protocol:

protocol SessionDisplayable {
    
    /** The main name of this session */
    var title: String { get }
    
    /** A follow-up tagline for the session */
    var subtitle: String { get }
    
    /** What image, if any is available for this session */
    var logoURL: URL { get }
    
    /** The location for where this session will occur */
    var location: String { get }
    
    /** A long-form description of the session */
    var sessionDescription: String { get }
    
    /** Presentation Summary */
    var presentationSummary: String { get }
    
    /** What Twitter handle, if any represents this session */
    var twitter: String { get }
    
    /** Whether this type of session requires a new view controller to display more information */
    var selectable: Bool { get }
}

And created individual view models for each session type. For example, here is the BreakfastSessionViewModel:

//  BreakfastSessionViewModel.swift

struct BreakfastSessionViewModel: SessionDisplayable {
    
    private let session: Session
    private let dataDefaults: SessionDataDefaults
    
    init?(session: Session) {
        if session.type == .breakfast {
            self.session = session
            self.dataDefaults = SessionDataDefaults(session: session)
        } else {
            return nil
        }
    }
    
    var title: String {
        return dataDefaults.title
    }
    
    var subtitle: String {
        return dataDefaults.subtitle
    }
    
    var logoURL: URL {
        return dataDefaults.imageURL ?? dataDefaults.logoImageURL
    }
    
    var location: String {
        return dataDefaults.location
    }
    
    var sessionDescription: String {
        return "❤️".localized()
    }
    
    var presentationSummary: String {
        return dataDefaults.summary
    }
    
    var selectable: Bool {
        return false
    }
    
    var twitter: String {
        return "@\(dataDefaults.twitter)"
    }
}

This allowed me to have only one switch statement:

public struct SessionViewModel: SessionDisplayable {
    
    private let displayble: SessionDisplayable
    
    public init(session: Session) {
        switch session.type {
        case .workshop:
            displayble = WorkshopSessionViewModel(session)!
        case .meetup:
            displayble = MeetupSessionViewModel(session)!
        case .breakfast:
            displayble = BreakfastSessionViewModel(session)!
        case .announcement:
            displayble = AnnouncementSessionViewModel(session)!
        case .talk:
            displayble = TalkSessionViewModel(session)!
        case .lightningTalk:
            displayble = LightningTalkSessionViewModel(session)!
        case .sponsoredDemo:
            displayble = SponsoredDemoSessionViewModel(session)!
        case .coffeeBreak:
            displayble = CoffeeBreakSessionViewModel(session)!
        case .lunch:
            displayble = LunchSessionViewModel(session)!
        case .officeHours:
            displayble = OfficeHoursSessionViewModel(session)!
        case .party:
            displayble = PartySessionViewModel(session)!
        }
    }
    
    public var title: String { return displayble.title }
    
    public var subtitle: String { return displayble.subtitle }
    
    public var logoURL: URL { return displayble.logoURL }
    
    public var location: String { return displayble.location }
    
    public var sessionDescription: String { return displayble.sessionDescription }
    
    public var presentationSummary: String { return displayble.presentationSummary }
    
    public var twitter: String { return displayble.twitter }
    
    public var selectable: Bool { return displayble.selectable } 
}

The Problem

The big thing here is that multiple session view models have the same default data implementation. That is why I created a SessionDataDefaults object to access the default data easily (see the use-case in the BreakfastSessionViewModel implementation).

//  SessionDefaults.swift

struct SessionDataDefaults {
        
    let title: String
    let subtitle = "try! Conference"
    let logoImageURL = Bundle.trySwiftAssetURL(for: "Logo.png")!
    let imageURL: URL?
    let location: String
    let summary = Conference.current.localizedDescription
    let twitter = Conference.current.twitter!
    
    init(session: Session) {
        // properties set here
    }
}

So as you can imagine, some of the session view model implementations (check the BreakfastSessionViewModel for reference) use the default data values.

When I shared this refactor with a friend, he immediately saw a new refactoring opportunity – create default implementations of the relevant methods in the protocol!

Default Implementation?

At first, that sounded great, but after thinking about it I decided against the default implementation refactor. Here’s why:

  • Each variable of a session needs a lot of thought put into it. Even if the implementation details end up being the same as the default, I want it to be a strong conscious choice. If I make it a default implementation, it would be too easy to forget and not think about much. Oh, and the compiler won’t complain!
  • I want it to be easy to change the variable for each session. If a variable is not included in the file because the implementation is in the protocol, it’s more work to make the decision to add that into the file. The assumption is that the default should win. If it’s already there, it’s easier to just go in and make a small change.
  • Similar to the above point, I want it to be super readable where each variable is coming from. If a bunch of variables are in the default implementation of the protocol, it’s not as clear just by looking at the file.

I think adding default implementations to protocols should be considered very very carefully. The default needs to be something that is consistent and is the default for most cases, so if someone forgets to implement it (because the compiler won’t complain), it’s most likely not a big deal.

The one case where I love default implementations in protocols is when the function is not included in the the protocol definition – it’s just a common method with no interface exposed. Otherwise, it’s way too easy to forget about and introduce confusing bugs later!

]]>
https://www.natashatherobot.com/swift-use-default-implementations-protocols/feed/ 4 6578
Swift: When to use guard vs if https://www.natashatherobot.com/swift-when-to-use-guard-vs-if/ https://www.natashatherobot.com/swift-when-to-use-guard-vs-if/#comments Thu, 23 Mar 2017 04:11:21 +0000 https://www.natashatherobot.com/?p=6568 One thing I’ve noticed recently in my code-base is that I tend to default to guard vs if. In fact, whenever I write an if statement, I facepalm myself and change it to a guard without thinking much.

But that’s become a problem. There is in fact a difference between guard and if and thought does need to be put into which one to use.

The difference is a bit subtle, but it is there. guard should be used when certain values are expected to be present for the function to execute as intended.

For example, in the try! Swift app, when it displays a presentation session type, the presentation title is the session title.

However, not every session has a presentation, so the presentation is optional. However, for this specific session type, it is expected that a presentation is in fact present and that it has a title. This is a perfect use-case for guard!

@objc public enum SessionType: Int {
    case workshop
    case meetup
    case breakfast
    case announcement
    case talk
    case lightningTalk
    case sponsoredDemo
    case coffeeBreak
    case lunch
    case officeHours
    case party
}

public class Session: Object {
    // this is optional because not all sessions have presentations
    // e.g. no presentation during breakfast
    open dynamic var presentation: Presentation?
    // other properties here

    /** The main name of this session */
    public var formattedTitle: String {
               
        switch self.type {
        case .talk, .lightningTalk:
            // for the talk / lighting talk session type
            // we expect the presentation to be there
            // if it's not there, it's a fail, so `guard` is used
            guard let presentation = presentation else { return defaultTitle }
            return presentation.localizedTitle
        // other cases continued...
        }
    }

The talk title should always be present for a presentation session type. If it’s not there, it’s a fail. This is why we use guard in this case.

However, consider another case. A Coffee Break session could be sponsored. In that case, the title of the coffee break should include the sponsor name. Both are correct – if there is a sponsor, we include the name of the sponsor, but if there isn’t one, we don’t include it. This is the type of case where if should be used:

public class Session: Object {

    /** A sponsor, if any, responsible for this session. */
    open dynamic var sponsor: Sponsor?

    /** The main name of this session */
    public var formattedTitle: String {
               
        switch self.type {
        case .coffeeBreak:
            // some sessions are sponsored, some aren't
            // it's not a fail if there is no sponsor
            // so `if` is used
            if let sponsor = sponsor {
                return "Coffee Break, by \(sponsor.name)".localized()
            }
            return "Coffee Break".localized()
        // other cases continued...
        }
    }

So as @ecerney puts it so well, think of guard as a lightweight Assert:

Like an if statement, guard executes statements based on a Boolean value of an expression. Unlike an if statement, guard statements only run if the conditions are not met. You can think of guard more like an Assert, but rather than crashing, you can gracefully exit.

So think if before you guard!

]]>
https://www.natashatherobot.com/swift-when-to-use-guard-vs-if/feed/ 13 6568
Architecting for Features https://www.natashatherobot.com/architecting-for-features/ https://www.natashatherobot.com/architecting-for-features/#respond Sun, 12 Mar 2017 08:54:27 +0000 https://www.natashatherobot.com/?p=6553 A few months ago, I gave a talk titled Build Features, Not Apps at iOS Conf SG – you can view the full talk here. It was clearer than ever to me after WWDC 2016 that the future of apps is a web of distributed features instead of one concentrated app. Think of Apple Watch, Today’s Widget, Interactive Notifications, App Search, iMessage Stickers, Apple Maps Integration and the list goes on…

If done right, each of these extensions will be just that – extensions (or features) of your app. For example, you shouldn’t have your whole app functionality in a Today’s Widget – just the part that is useful to the user on a quick glance (such as the weather right now).

But increasingly, these types of extensions allow the user to break free from your app. They can just glance at the relevant information – such as the weather right now – without ever bothering to open your app. They’re still using your service, but not in a traditional “opening your app” sense.

These small and invisible interactions with your app really change the concept of what an app is. An app is now something that gives the user the information or interaction they need at the moment they need it – again, without ever needing to remember to open the actual app. This paradigm shift, while disruptive to apps that do rely on being opened (e.g. they have advertisements in there), also gives a lot of new ways of interacting and building an even stronger brand with consumers. Your app is now everywhere! Not just stuck inside your app waiting to be opened!

This is why for the try! Swift app, I really focused on setting it up for features instead of adding new functionality to the app in time for try! Swift Tokyo.

The key here was decoupling the data layer, including Realm, from the iOS app. The same data layer would be re-used for the Watch App, Apple TV App (in the future maybe), the iOS app, and iOS extensions (such as a Today’s Widget and Interactive Notifications).

Unfortunately, that’s much harder to do than it should be. I recommend the article Creating Cross-Platform Swift Frameworks for iOS, watchOS, and tvOS via Carthage and CocoaPods by Basem Emra to get started.

I personally have never made a CocoaPod, especially one with a dependency on Realm, so I had a really hard time getting everything to link and work together. Luckily, @aaalveee helped us get started, @k_katsumi helped make the pod work across extensions, and @TimOliverAU did a lot of work to set the project up for Realm Mobile Platform (still some work to do there).

It was a lot of work (and still a lot of work left to do), but I’m super happy with the result – you can see the trySwiftData framework on Github here. The best part is that the data framework includes a lot of complicated data formatting code – such as session titles based on speakers vs announcements – that used to be duplicated in code both in iOS and watchOS apps. We were even able to add a Today’s Widget with limited time, using the same data pod.

I’m looking forward to now move a lot quicker with adding new features, including Interactive Notifications and App Search in time for the next try! Swift in NYC 🚀

]]>
https://www.natashatherobot.com/architecting-for-features/feed/ 0 6553
How to Reuse Paging Interface Controllers in watchOS https://www.natashatherobot.com/how-to-reuse-paging-interfacecontrollers-in-watchos/ https://www.natashatherobot.com/how-to-reuse-paging-interfacecontrollers-in-watchos/#comments Mon, 26 Sep 2016 17:10:02 +0000 https://www.natashatherobot.com/?p=6525 watchOS is currently very non-dynamic. You have a storyboard and you have to put all your Interface Controllers in it, even when they’re all pretty much the same, like in this Italian Food Apple Watch app:

Each Interface Controller simply has an image and a label:

WatchOS Paging Storyboard

Even though these are exactly the same and have the exact same logic in the Interface Controllers, for the past year, I haven’t been able to figure out how to re-use one Interface Controller for each of these!

So I had three different controllers where I copied and pasted all the code and only changed the model-layer details for the image and label data. But alas, after searching once again, I finally stumbled on a horrible solution that actually works.

The Model

First, here is the simple FoodItem model that I’m using to populate the data for the Interface Controllers:

struct FoodItem {
    let title: String
    let imageName: String
}

extension FoodItem {
    
    static let foodItems = [
        FoodItem(title: "Camogliese al Rum", imageName: "comogli"),
        FoodItem(title: "Pesto alla Genovese", imageName: "pasta"),
        FoodItem(title: "Focaccia di Recco", imageName: "recco"),
    ]
}

The Storyboard

The next step is to create the reusable Interface Controller, let’s name it FoodItemInterfaceController, and to assign it as the class for every single Interface Controller in the storyboard:

FoodItemInterfaceController

Next, create and connect the IBOutlets for the image and label in the FoodItemInterfaceController:

fooditeminterfacecontroller_swift_-_edited

Finally, you have to add a unique identifier for each of your Interface Controllers in the Storyboard:

interface_storyboard_-_edited

The Interface Controller

Now comes the ugly part… When the first interface controller loads, you have to trick it into loading all the others instead…

import WatchKit

class FoodItemInterfaceController: WKInterfaceController {

    @IBOutlet var image: WKInterfaceImage!
    @IBOutlet var label: WKInterfaceLabel!
    
    // you have to keep track of whether this is the first load...
    static var first = true
    
    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        
        // if first load... 
        if FoodItemInterfaceController.first {
            // then reload with the data for all 3 controllers... 
            // the Names are the storyboard identifiers 
            // the Context is the data
            if FoodItemInterfaceController.first {
            WKInterfaceController.reloadRootControllers(
                withNames: ["FoodItem1", "FoodItem2", "FoodItem3"],
                contexts: FoodItem.foodItems)
            FoodItemInterfaceController.first = false
        }
        
        // the data is in the context that's passed into this method
        if let foodItem = context as? FoodItem {
            // set the proper data into the image and label
            image.setImage(UIImage(named: foodItem.imageName))
            label.setText(foodItem.title)
        }
    }
}

Conclusion

First, this is slower than just hardcoding all the Interface Controllers, since the first time the Interface Controller loads, it has to reload everything. But at least the code is in one place, right?

Also, there is no way to my knowledge to have a dynamic data set (e.g. you get the variable food item array data from the server and want to display it in more than 3 pages. Although in that case, you can use a table instead of paging interface.

Oh, and of course, you still have to duplicate the Interface Controllers in the Storyboard, so even though these all have the same-sized images and labels with the same layout and fonts, if you make a change to one, you have to remember to make it to all so they all look the same at the end. I forgot to do this a few times even for this demo…

You can view the full source code on Github here.

]]>
https://www.natashatherobot.com/how-to-reuse-paging-interfacecontrollers-in-watchos/feed/ 1 6525
Creating a Framework for both iOS and watchOS https://www.natashatherobot.com/creating-a-framework-for-both-ios-and-watchos/ https://www.natashatherobot.com/creating-a-framework-for-both-ios-and-watchos/#comments Mon, 26 Sep 2016 09:18:39 +0000 https://www.natashatherobot.com/?p=6517 The try! Swift app runs on both iOS and watchOS, and I’d like to expand it to support fancy extensions and possibly something with iMessage. And maybe I’ll want to have a tvOS app eventually to hook up to the tv monitors around one of my conferences.

While I was ok duplicating the Model layer between the two platforms initially, as more extensions and platforms are something I want to code quickly on, it was time to move that code out into a framework.

I haven’t made a framework before, but it’s super easy. The hard part was realizing that there there isn’t an option to create a cross-platform framework. I was forced to choose between iOS, macOS, tvOS, and watchOS. Which makes a lot of sense. Apple frameworks that are available on iOS are not available on watchOS, etc.

But in my case, I just had super simple Model-layer code, so I wanted to share this code between my iOS and watchOS platforms. After searching around for how to do it, I finally found a simple solution that works.

I just created a Models folder for shared files, and put it in a folder outside the two frameworks:

shared framework code

Then I just dragged the files into to the correct framework targets (do NOT select “Copy as needed”).

Model Duplication

The key here is that if you want to add something to your model layer that only pertains to the specific framework, you have to make a new file with an extension for that model. Oh, and of course, the files have the exact same reference, so any change you make to one framework, you make to the other.

So it’s definitely a bit dangerous and not a great solution, but the only one I’ve found unfortunately. Now I finally feel like I can move fast with other app extensions I have in mind!

If anyone has better solutions, happy to hear about them in the comments!

]]>
https://www.natashatherobot.com/creating-a-framework-for-both-ios-and-watchos/feed/ 11 6517
Swift 3.0 Refactoring Cues https://www.natashatherobot.com/swift-3-0-refactoring-cues/ https://www.natashatherobot.com/swift-3-0-refactoring-cues/#comments Tue, 20 Sep 2016 10:03:42 +0000 https://www.natashatherobot.com/?p=6505 I’ve been upgrading the try! Swift app to Swift 3.0 for what feels like 3 days now (I’ve had to take a lot of breaks to be in the right patient mindset, especially with not knowing how many more errors will come up after the immediate ones were fixed).

But anyway, this morning my BUILD SUCCEEDED!! I fixed the final warnings (almost) and tried out the app to see that it works generally. I still have time to fix any bugs in the future before the next version goes in production.

And although the process was definitely frustrating, a lot of the things that needed fixing were very repetitive. For example, the String API has added a new (and very ugly IMHO) describing parameter:

// covered here: https://www.natashatherobot.com/nsstringfromclass-in-swift/
String(MyTableViewCell) 

// is now this
// * notice the required .self as well! *
String(describing: MyTableViewCell.self)

Well, I have A LOT of TableViewCell’s in my app, and when combined with registering Nibs or Dequeuing Cells, it was just an ugly train wreck:

// Swift 3.0 registering a cell example
 tableView.register(UINib(nibName: String(describing: TextTableViewCell.self), bundle: nil), forCellReuseIdentifier: String(describing: TextTableViewCell.self))

// Swift 3.0 dequeuing a cell example
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TextTableViewCell.self), for: indexPath) as! TextTableViewCell

I had to fix these with the describing and .self over and over again during my Swift 3.0 upgrading process. As soon as I finished the upgrade, I knew I had to fix this issue. After all, hopefully the String API will be fixed back to having no describing parameter in Swift 4 – I want to refactor this repetitive change in only one place in the future!

The Refactor

Luckily, in this case, I knew just the solution! In fact, I talked about this solution in my POP talk… I should definitely have implemented this earlier, but when I first started making the app, I didn’t know about it, and it hasn’t been a big enough issue to actually do the refactor until this painful Swift 3.0 upgrade 😬

All it took was a few minutes and a few protocols! A timely reminder of how awesome Swift actually is! For a more detailed explanation, make sure to watch my talk or read about it from the source here. Otherwise, here is the quick version:

First, create a Protocol with a default variable for generating the nib name string from the class name:

protocol NibLoadableView: class { }

extension NibLoadableView where Self: UIView {
    
    static var nibName: String {
        // notice the new describing here
        // now only one place to refactor if describing is removed in the future
        return String(describing: self)
    }
    
}

// Now all UITableViewCells have the nibName variable
// you can also apply this to UICollectionViewCells if you have those
// Note that if you have some cells that DO NOT load from a Nib vs some that do, 
// extend the cells individually vs all of them as below! 
// In my project, all cells load from a Nib. 
extension UITableViewCell: NibLoadableView { }

Next, do the same thing to generate the reuse identifier string from the cell class:

protocol ReusableView: class {}

extension ReusableView where Self: UIView {
    
    static var reuseIdentifier: String {
        return String(describing: self)
    }
    
}

extension UITableViewCell: ReusableView { }

Now the good stuff! You can take advantage of the above protocols to simplify the nib registration and cell dequeuing:

//  UITableViewExtension.swift

extension UITableView {
    
    func register<T: UITableViewCell>(_: T.Type) where T: ReusableView, T: NibLoadableView {
        
        let nib = UINib(nibName: T.nibName, bundle: nil)
        register(nib, forCellReuseIdentifier: T.reuseIdentifier)
    }
    
    func dequeueReusableCell<T: UITableViewCell>(forIndexPath indexPath: IndexPath) -> T where T: ReusableView {
        guard let cell = dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as? T else {
            fatalError("Could not dequeue cell with identifier: \(T.reuseIdentifier)")
        }
        
        return cell
    }
}

Now time to refactor!

// Swift 3.0 original registering a cell example
 tableView.register(UINib(nibName: String(describing: TextTableViewCell.self), bundle: nil), forCellReuseIdentifier: String(describing: TextTableViewCell.self))

// Swift 3.0 refactored registering cell example: 
tableView.register(TextTableViewCell.self)

// Swift 3.0 original dequeuing a cell example
let cell = tableView.dequeueReusableCell(withIdentifier: String(describing: TextTableViewCell.self), for: indexPath) as! TextTableViewCell

// Swift 3.0 refactored dequeuing a cell example
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as TextTableViewCell

Here is my full commit. I was very happy with this result and look forward to less repetition when upgrading to Swift 4.0 in the future!

So although the Swift 3.0 upgrade is very painful, it is also a good time to notice all those repetitive things in your code and refactor.

Happy upgrading and remember to Breathe!

]]>
https://www.natashatherobot.com/swift-3-0-refactoring-cues/feed/ 6 6505
CloudKit: What is it good for? https://www.natashatherobot.com/cloudkit-what-is-it-good-for/ https://www.natashatherobot.com/cloudkit-what-is-it-good-for/#comments Sun, 18 Sep 2016 14:29:02 +0000 https://www.natashatherobot.com/?p=6491 When I first created the try! Swift app in haste in time for my first conference in Tokyo earlier this year, I hard-coded everything. After all, what could possibly change two weeks before the conference?!!!

Well, turned out that one speaker had to cancel last minute and we had to make some last-minute speaker / presentation changes. While, it’s not the biggest deal to have one talk information wrong, I really hated that I couldn’t update any information remotely to accommodate for any last-minute changes without having to go through the app review process again (at that time, it was still two weeks). So I made that the priority feature for the try! Swift NYC app in time for my latest conference in early September.

The obvious way to accomplish this that came to mind was via some type of JSON payload. In fact, @BasThomas actually built it! However, when I mentioned this to @danielboedewadt while catching up during WWDC, he recommended using CloudKit, especially since it has the silent notifications feature. I was immediately excited to play with something new and shiny, so when it came time for me to work on the try! Swift NYC app, I selfishly deleted Bas’s JSON work and started implementing CloudKit instead.

The Business Use-case

From the business perspective, the reality is that the conference schedule would mostly stay the same. In addition, many of our attendees are from abroad with varying levels of internet access. I also had no idea how bad the wifi would be in the venue (although it turned out to be great!).

From that perspective, hard-coding / having as much information as possible on load was still necessary. I wanted users to open the app and have everything load right away without waiting for any kind of annoying spinners while waiting for updates from the cloud.

Persistence

In the try! Swift Japan app, I hard-coded everything into arrays and dictionaries to load every time (after all, it wasn’t that much information). However, to be able to make updates from CloudKit in the try! Swift NYC app, I knew I needed a more robust local persistence layer.

Realm has been on my list of things I wanted to try but haven’t gotten to, so I decided to try it out instead of CoreData. Pretty happy I did! But that’s another topic.

Data Duplication?

When I started working with CloudKit, my mindset was still stuck in the typical architecture where I would have to sync all the data between the cloud and my local persistence layer. I was planning on saving hard-coded data into Realm on the first app load (since most of it was static and good enough after-all in case there is no internet connection), and then having a mirror database with all the data in CloudKit that I would sync with my local Realm database.

However, the more I thought about my business use-case and how rare it is to have the data actually change, and how expensive it would be to query everything and figure out all the changes, I came up with a much lighter use of CloudKit!

Ch-ch-ch-ch-Changes

Instead of duplicating my entire database layer in the Cloud, I created a more generic Change table in my CloudKit database:

CloudKit Change Table

So for example, if the Speaker bio changed (which is the only change that happened using this system), I would create the following Change object:

CloudKit Change Create

So basically, the Speaker with id 7 (in Realm) needs to have their “bio” field changed to the new value provided.

Subscribe to Changes

On the app side, we just subscribe to any Creation changes in CloudKit to get notifications to make the update!

class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {        
        insertDefaultData()
        
        let notificationSettings = UIUserNotificationSettings(types: UIUserNotificationType(), categories: nil)
        application.registerUserNotificationSettings(notificationSettings)
        application.registerForRemoteNotifications()
        
        subscribeToCloudChangeNotifications()
        
        // sync for changes on app load
        configureData()

        // configure other stuff here
        
        return true
    }

    // handle the silent notification when a Change is made
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void)
    {
        ChangeManager.syncChanges()
        completionHandler(.noData)
    }
 
    // Subscribing to Change notifications
    func subscribeToCloudChangeNotifications() {
        let defaults = UserDefaults.standard
        DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.default).async {
            if !defaults.bool(forKey: "SubscribedToCloudChanges") {
                let predicate = NSPredicate(value: true)
                
                let subscription = CKSubscription(recordType: "Change", predicate: predicate, options: .firesOnRecordCreation)
                
                let notificationInfo = CKNotificationInfo()
                notificationInfo.shouldSendContentAvailable = true
                
                subscription.notificationInfo = notificationInfo
                
                let publicDB = CKContainer.default().publicCloudDatabase
                publicDB.save(subscription, completionHandler: { subscription, error in
                    if let _ = subscription {
                        defaults.set(true, forKey: "SubscribedToCloudChanges")
                    }
                }) 
            }
        }
    }

    // sync any changes on app load
    func configureData() {
        let defaults = UserDefaults.standard
        
        let appSubmitionDate = Date.date(year: 2016, month: 8, day: 16, hour: 5, minute: 0, second: 0)
        if defaults.object(forKey: ChangeManager.lastChangedDataNotification) == nil {
            defaults.setObject(appSubmitionDate, forKey: ChangeManager.lastChangedDataNotification)
        }
        
        ChangeManager.syncChanges()
    }

    // other stuff here
}

Now, when a Change happens, the change can be synced with the local Realm database as follows:

import RealmSwift
import CloudKit

struct ChangeManager {
    
    static let lastChangedDataNotification = "LastChangedDataNotification"
    
    static func syncChanges() {
        let defaults = UserDefaults.standard
        DispatchQueue.global(priority: DispatchQueue.GlobalQueuePriority.high).async {
            let publicDB = CKContainer.default().publicCloudDatabase
            guard let lastChangeDate = defaults.object(forKey: ChangeManager.lastChangedDataNotification) as? Date else {
                let appSubmitionDate = Date.date(year: 2016, month: 8, day: 16, hour: 5, minute: 0, second: 0)
                defaults.setObject(appSubmitionDate, forKey: ChangeManager.lastChangedDataNotification)
                return
            }
            
            let predicate = NSPredicate(format: "creationDate > %@", lastChangeDate as CVarArg)
            let query = CKQuery(recordType: "Change", predicate: predicate)
            publicDB.perform(query, inZoneWith: nil) { result, error in
                
                guard let result = result else {
                    // will update again on future launch
                    return
                }
                
                result.forEach {
                    updateRecord($0)
                }
                defaults.set(Date(), forKey: ChangeManager.lastChangedDataNotification)
            }
        }
    }
}

private extension ChangeManager {
    
    static func updateRecord(_ record: CKRecord) {
        guard let object = record["object"] as? String,
            let id = record["id"] as? Int,
            let field = record["field"] as? String,
            let newValue = record["newValue"] as? String else {
                return
        }
        
        
        let realm = try! Realm()
        if object == "Speaker" {
            if let speaker = realm.objects(Speaker).filter("id == \(id)").first {
                if field == "imagePath" {
                    guard let imageAsset = record["image"] as? CKAsset else {
                        return
                    }
                    
                    try! realm.write {
                        speaker["imageName"] = nil
                        speaker[field] = imageAsset.fileURL.path
                    }
                    
                } else {
                    try! realm.write {
                        speaker[field] = newValue
                    }
                }
            }
        } else if object == "Presentation" {
            if let presentation = realm.objects(Presentation).filter("id == \(id)").first {
                try! realm.write {
                    presentation[field] = newValue
                }
            }
        }
    }
}

Not the most beautiful code, but it works…

Conclusion

One of the things I was disappointed with for CloudKit was how Stringly-typed it is – given that this is a relatively new native solution from Apple. I was hoping to see something closer to what Parse did. But it ended up working well with Realm subscripts that way. I think the code can definitely be improved to be less Stringy, but I was happy with this initial architecture.

I was able to create a super light updating layer with a mixture of hard-coded pre-loaded data and silent notification updates included without having to install yet another third-party solution. However, if I needed something more heavy-duty, I would have to consider Firebase or just a plain old JSON API.

Note: This blog post is more of a general architecture overview of CloudKit. For full code example, see the try! Swift NYC app source code here.

]]>
https://www.natashatherobot.com/cloudkit-what-is-it-good-for/feed/ 1 6491
Swift: What are Protocols with Associated Types? https://www.natashatherobot.com/swift-what-are-protocols-with-associated-types/ https://www.natashatherobot.com/swift-what-are-protocols-with-associated-types/#comments Thu, 28 Jul 2016 15:17:27 +0000 https://www.natashatherobot.com/?p=6473 I recently gave a talk about Protocols with Associated Types (PATs) – I was worried that my audience would already know everything about PATs, but turns out the opposite is true.

Many didn’t know what PATs were – which I should have anticipated in hindsight, since it took me a while to learn about them myself. So I wanted to explain them right here, especially since they are challenging to understand and there aren’t that many great explanations that I’ve found.

The best explanation that really helped me was by Gwendolyn Weston at the try! Swift conference in Tokyo (video here), so the example is inspired by her talk. There will be Pokemon…

Before PATs

I’m currently Level 9 in Pokemon Go, and I learned (thanks to my personal trainer @ayanonagon) that all Pokemon have some common traits, such as the power to attack.

Coming from Objective-C or other object-oriented languages, it might be tempting to have a Pokemon subclass for all the common functionality. Since each Pokemon attacks with a different power – lightning or water or fire, etc – we can use the power of Generics in our subclass:

// we have to ensure that the generic Power 
// has an init function
protocol Initializable {
    init()
}

// Pokemon subclass
// Each Pokemon has a different Power, 
// so Power is a generic
class Pokemon<Power: Initializable> {
    
    func attack() -> Power {
        return Power()
    }
}

At this point, we would have different Powers modeled:

// power types
struct 🌧: Initializable { // implementation }
struct 🌩: Initializable { // implementation }
struct 🔥: Initializable { // implementation }

Now, other Pokemon can subclass from our base Pokemon class, and they will automatically have the attack function!

class Pikachu: Pokemon<🌩> {}
class Vaporeon: Pokemon<🌧> {}

let pikachu = Pikachu()
pikachu.attack() // 🌩

let vaporeon = Vaporeon()
vaporeon.attack() // 🌧

The problem here is that we’re subclassing. If you’ve watched Dave Abrahams’ WWDC talk on Protocol-Oriented Programming in Swift, you should be seeing the face of Crusty in your head right now…

The problem with subclassing is that while it starts out with great intentions, eventually things get a lot messier as exceptions arise (e.g. Pokemon Eggs can’t attack). I highly recommend reading Matthijs Hollemans’s Mixins and Traits in Swift 2.0 to understand this more.

After all, as Dave Abrahams stated, Swift is a Protocol-Oriented Language, so we need to change our Object-Oriented mindset…

Hello, PATs

Instead of subclassing, let’s do the same thing with PATs! Instead of subclassing all the things, we can create a Protocol that focuses on the Pokemon’s ability to attack. Remember, since each Pokemon has a different Power, we need to make that a Generic:

protocol PowerTrait {
    // That's it! An Associated Type is just 
    // different syntax for Generics in protocols
    associatedtype Power: Initializable
    
    func attack() -> Power
}

extension PowerTrait {
    // with protocol extensions, 
    // we can now have a default attack function
    func attack() -> Power {
        return Power()
    }
}

Now, any Pokemon that conforms to the PowerTrait protocols will have the attack functionality no subclassing necessary!

struct Pikachu: PowerTrait {
    // since we're using the default attack functionality
    // we have to specify the type of the associated type
    // just like we did when subclassing with a generic
    // * note that this is still called typealias, but will be changed 
    // to associatedtype in future versions of Swift
    associatedtype Power = 🌩
}
let pikachu = Pikachu()
pikachu.attack() //🌩

struct Vaporeon: PowerTrait {
    // when the attack function is overwritten
    // 🌧 is inferred as the associated type
    // based on the method signature
    func attack() -> 🌧 {
        // custom attack logic
        return 🌧()
    }
}
let vaporeon = Vaporeon()
vaporeon.attack() //🌧

Conclusion

That’s it! A protocol with associated type is just a fancy term to a protocol that has generics in it. By using PATs (and protocols in general), we have a powerful tool that promotes really nice composition in favor of messy inheritance.

To learn more about the limitations of PATs and go deeper, I highly recommend Alexis Gallagher’s talk here.

Happy catching!

Join me for a Swift Community Celebration 🎉 in New York City on September 1st and 2nd. Use code NATASHATHEROBOT to get $100 off!

]]>
https://www.natashatherobot.com/swift-what-are-protocols-with-associated-types/feed/ 5 6473