6

A script to automate overriding iOS simulator status bar values

 4 years ago
source link: https://www.jessesquires.com/blog/2019/09/30/automating-simctl-status-bar/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

30 Sep 2019

Updated: 30 Sep 2019

software-dev    ios, xcode

I recently wrote about overriding iOS simulator status bar display settings using simctl status_bar. In that post I provided some ways we can improve the tool, but I realized we can do even better.

My previous solution, was to wrap simctl status_bar in a custom command to make it easier to use:

$ fix_status_bar "iPhone 11"

A noted improvement, but after some use I’m tired of having to specify the simulator name — especially when I need to fix the status bars on multiple simulators, or if a default simulator has a long name, like "iPad Pro (12.9-inch) (3rd generation)". Luckily, everything we need to do this is already part of xcrun simctl.

Running xcrun simctl list devices will print a list of all simulators and their status. The output looks something like this:

-- iOS 12.0 --
    iPhone X (10939DAA-4FBA-489A-AAF3-555E224146B1) (Shutdown)
-- iOS 13.0 --
    iPhone 8 (A2F89D01-006F-4439-8F30-7EDB809E8E68) (Shutdown)
    iPhone Xs (9F75F237-564D-449C-8AB8-7D1C1380E214) (Booted)
    iPhone 11 (BFD3E2D2-86B3-477E-AEB2-2CC6CFA50A53) (Booted)
    iPhone 11 Pro Max (637F617E-F3A9-43AF-92DE-B11DF82C2586) (Shutdown)
    iPad Pro (12.9) (98D6FBA0-EDB8-4B8F-A8AD-64A6B000B448) (Shutdown)
-- tvOS 13.0 --
    Apple TV (395B73E3-EA43-480B-BD2E-636C9D223CC9) (Shutdown)
    Apple TV 4K (14E57D08-CE8B-4A1B-932C-594F8D6ED86A) (Shutdown)
-- watchOS 6.0 --
    Apple Watch Series 5 - 44mm (78017ABD-4500-4A22-BA15-EAEEF313E222) (Shutdown)

For each runtime, it prints the device name, identifier, and status (“Booted” or “Shutdown”). Another limitation of simctl status_bar that I failed to mention before is that you can only run it for simulators that are currently running. This list provides us with all the information we need to automatically fix the status bars for all open simulators.

Let’s write a script in Swift to do this.

Executing xcrun in Swift

The first step is to run xcrun simctl list devices, which we can do by creating an instance of Process. Then we can capture and parse the output.

extension Process {
    /// Creates a process to execute `xcrun`.
    ///
    /// - Parameter args: The arguments to pass to `xcrun`.
    func xcrun(_ args: String...) -> String {
        self.launchPath = "/usr/bin/xcrun"
        self.arguments = args

        let pipe = Pipe()
        self.standardOutput = pipe

        self.launch()
        self.waitUntilExit()

        let data = pipe.fileHandleForReading.readDataToEndOfFile()

        guard let output = String(data: data, encoding: .utf8) else {
            return ""
        }
        return output
    }

    /// Executes `xcrun simctl list devices`
    func xcrun_list_devices() -> String {
        return self.xcrun("simctl", "list", "devices")
    }
}

Parsing the devices list

UPDATE: There is an easier way to parse the device list. As Marcelo Fabri noted on Twitter, you can pass -j to xcrun simctl list devices to get a JSON representation of devices. I have updated the script on GitHub to use this method instead. However, I’ll leave the rest of this post as it was. The parsing of devices is just an implementation detail.

Next we need to parse the list of devices. There are 3 distinct pieces of information: name, identifier, and status. A device name could be anything, which means we cannot expect it to follow any specific format. In fact, in the example output above there are multiple variations. The most reliable way to retrieve each piece of information is to write a regular expression to match the device identifier.

/// == Example ==
/// input: "iPhone X (10939DAA-4FBA-489A-AAF3-555E224146B1) (Shutdown)"
/// match: "(10939DAA-4FBA-489A-AAF3-555E224146B1)"
let regex = try! NSRegularExpression(pattern: #"(\(([\w\-]{36})\))"#, options: [])

Once we know the location of the identifier in the string, we can easily extract the name, identifier, and status separately. Before we start parsing, we need to run the command, and split the output into an array of strings, where each element is one line of the output.

let devicesList = Process().xcrun_list_devices()
let devices = devicesList.split(separator: "\n").map { String($0) }

Then we can parse each line.

let count = line.count

let rangeOfMatch = regex.rangeOfFirstMatch(in: line, options: [], range: line.nsRange)

if rangeOfMatch.location != NSNotFound {
    let deviceName = line.dropLast(count - rangeOfMatch.location).byTrimmingWhiteSpace

    let deviceID = line
        .dropFirst(rangeOfMatch.lowerBound + 1) // + 1 to remove the "("
        .dropLast(count - rangeOfMatch.lowerBound - rangeOfMatch.length + 1) // +1 to remove the ")"
        .byTrimmingWhiteSpace

    let deviceStatus = line.dropFirst(rangeOfMatch.upperBound).byTrimmingWhiteSpace
}

Once we have that, we need to be able to run simctl status_bar with the overrides to apply to the status bars.

extension Process {
    /// Executes `xcrun simctl status_bar` on the specified device.
    ///
    /// - Parameter device: The device for which status bar values should be overridden.
    func xcrun_fix_status_bar(_ device: String) -> String {
        return self.xcrun(
            "simctl", "status_bar", device, "override",
            "--time", "9:41",
            "--dataNetwork", "wifi",
            "--wifiMode", "active",
            "--wifiBars", "3",
            "--cellularMode", "active",
            "--cellularBars", "4",
            "--batteryState", "charged",
            "--batteryLevel", "100"
        )
    }
}

For any device that is booted, we call xcrun_fix_status_bar(:) with the device identifier.

if deviceStatus.contains("Booted") {
    Process().xcrun_fix_status_bar(deviceID)
}

I’ve posted the full script on GitHub under a new project called Nine41. The following is an example run, which outputs the name of each simulator that was found and fixed.

$ ./nine41.swift
Fixing status bars...
✅ iPhone 8, A2F89D01-006F-4439-8F30-7EDB809E8E68
✅ iPhone Xs, 9F75F237-564D-449C-8AB8-7D1C1380E214
✅ iPhone 11, BFD3E2D2-86B3-477E-AEB2-2CC6CFA50A53

Like I mentioned in my previous post, you can create a custom command for this if you like:

function fix-status-bars() {
    Path/To/Your/Script/nine41.swift
}

Or, you could compile it and put the binary in your PATH. Enjoy!


Recommend

  • 62

    上下文假如我们项目中引入的三方包出现了一个小的bug,我们通常会fixed它,然后创建一个pull request,但是通常我们等不到pull request被merge之后的release版本。这种现象非常普遍,对于这种情况,我们通过有如下三种方案: 使用merge了pull request的snapshot版...

  • 37

    I’ve been programming in Java for over half a decade, and thought I knew how overloading and overriding worked. It was only once I started thinki...

  • 39

    I've been programming in Java for over half a decade, and thought I knew how overloading and overriding worked. It was only once I started thinking of and writing up the following corner cases, that I realized I didn't know it nearly as well as...

  • 22

    When a super class method (overridden method) declares that it can throw an exception then sub class method (overriding method) must also declare that it can throw the same kind of exception or a sub type of that...

  • 10

    Writing Cleaner View Code in Swift By Overriding loadView() Writing Cleaner View Code in Swift By Overriding loadView() July 23, 2018 ...

  • 10
    • hackernoon.com 4 years ago
    • Cache

    Overloading Vs. Overriding in C#

    Overloading Vs. Overriding in C#December 15th 2020 new story

  • 9
    • www.eigenbahn.com 4 years ago
    • Cache

    Overriding part of an Emacs theme

    Overriding part of an Emacs themeApril 6, 2020IntroductionLike most sane people, I don’t use Emacs’ default theme but instead rely on custom themes.Through a thoughtful choice of colors, the venerable edito...

  • 10

    There are a variety of “buttons” in HTML. You’ve got: <button>Button</button> <input type="button" value="Button"> Plus, for better or worse, people like having links that are style...

  • 10

    哎呀妈呀,等老久了吧!关于 Java 方面的文章终于来了,快快快,扶寡人起来,还能再举——鼎(明眼人都能看的出来,我受大秦帝国之纵横天下这部剧的影响了)。 说回正题。重写(Overriding)算是 Java 中一个非常重要的概念,理解重写到底是什么对每个...

  • 11

    26 Sep 2019 Updated: 28 Sep 2019 software-dev   ...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK