Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hard Coding NSUserActivityTypes for Restoring Different Data Models

As of iOS 13, Apple recommends storing user state using an NSUserActivity object attached to a scene, so I've been trying to a) better understand how NSUserActivity works and b) implement that in my own code. In working through Apple's documentation, I came across this piece of code:

        class var activityType: String {
        let activityType = ""

        // Load our activity type from our Info.plist.
        if let activityTypes = Bundle.main.infoDictionary?["NSUserActivityTypes"] {
            if let activityArray = activityTypes as? [String] {
                return activityArray[0]
            }
        }

        return activityType
    }

I understand what this is doing (it looks at the Info.plist file for an entry called "NSUserActivityTypes", if it exists it tries to get the associated array of activityTypes, and then it reads the first item in the array), but what I don't understand is why. In particular, I don't understand why we're only reading the first item in activityArray. In this case we know the first (and only item) is "com.apple.apple-samplecode.StateRestoration.activity" because we have to manually create that plist entry. But I don't understand why we are hard coding looking at the first item of the array in order to get the activity type, because if we know we're just going to get back the String "com.apple.apple-samplecode.StateRestoration.activity", why not just write the code to be this:

class var activityType: String {
       return "com.apple.apple-samplecode.StateRestoration.activity"
    }

I've never worked with NSUserActivity before, and I understand that it can be (usually is?) used for things other than state preservation/restoration, so there could be many different kinds of useractivities that your app could support (handoff, Siri integration, etc.). So I would assume that we want our code to be as robust as possible in not making any assumptions about the kinds of NSUserActivity objects we might receive.

Maybe someone who has more experience with NSUserActivity can help explain the ways in which NSUserActivity might be handed to my app, and why we can hard code in the first element of an array, while in other places we want to check if a passed-in activity is the right kind of activity (even though we know our array of supported activities has only one kind of activity, so presumably there's only one kind of activity we'd receive in the first place?).

Also, this isn't unique to Apple's sample code... this blog post also takes a similar approach when reading the Info.plist file:

extension Bundle {
    var activityType: String {
        return Bundle.main.infoDictionary?["NSUserActivityTypes"].flatMap { ($0 as? [String])?.first } ?? ""
    }
}
like image 503
RyanBDevilled Avatar asked Nov 07 '22 10:11

RyanBDevilled


1 Answers

I do have the same doubt when reading the Apple's example of restoring user state using NSUserActivity, because in my past project I always have a Constant.swift to handle all the hardcode string (something like id, or key), for example:

Constants.swift

struct Constants {
    static let userActivityTypeA = "com.apple.apple-samplecode.StateRestoration.activityA"
    static let userActivityTypeB = "com.apple.apple-samplecode.StateRestoration.activityB"
}

I understand what this is doing (it looks at the Info.plist file for an entry called "NSUserActivityTypes", if it exists it tries to get the associated array of activityTypes, and then it reads the first item in the array), but what I don't understand is why.

In my opinion, it is not a must to follow the Apple's example (adding the activityType in the info.plist & read it using Bundle.main.infoDictionary). Apple just provide a way that is cleaner & can be easily identify what that String is, by providing a fixed key "NSUserActivityTypes" in the info.plist.

(Imagine someday your colleague pick up your project and he may have no idea what is that reverse domain string in the Constants.swift or wherever you placed it.)

why we can hard code in the first element of an array, while in other places we want to check if a passed-in activity is the right kind of activity (even though we know our array of supported activities has only one kind of activity, so presumably there's only one kind of activity we'd receive in the first place?).

It really depends on how many activityType your apps support, for example, you may want to add few more types in the future, like type A go to ViewController A, and type B will open an In-App browsers in your apps. If you know your apps only support one kind of activity, Yes you don't even need to check it, but in general, we always want to confirm what is received and give corresponding respond.

like image 53
paky Avatar answered Nov 13 '22 23:11

paky