Hacker News new | past | comments | ask | show | jobs | submit | skrrtww's comments login

"system private window picker" refers to https://developer.apple.com/documentation/screencapturekit/s... .

Any material use of ScreenCaptureKit that doesn't involve that content sharing picker specifically will trigger this prompt. That means asking for SCShareableContent, or a couple other generic uses I forget.


Thanks for this information! Makes sense now.


This prompt is not even tied into the underlying TCC system; it's basically purely decorative. Failing to respond to the prompt, or responding "Open System Settings" to the prompt, does not even revoke the existing permission.

The prompt is also not even tied to the application bundle's code signature; tampering with the signature will not re-trigger the prompt. Nor will the prompt be re-triggered even if the application's entire bundle ID (com.example.example) changes.

No; the only way to re-trigger this prompt for an application is to rename the app bundle itself. That's right. Renaming Test.app to Test-dumb.app will trigger the prompt when nothing else will.

This isn't really worth criticizing that much because the prompt I think is designed as purely like a "don't forget about this" type of measure, and not one tied into actual security. But also that speaks greatly to the design challenges facing the TCC system more broadly, that this type of thing is seen as necessary.


> I think is designed as purely like a "don't forget about this" type of measure

I believe this to be the correct way to see the "feature". While annoying, it's not bad to be reminded of sensitive shit you have installed. A month seems reasonable to me, but perhaps and override ("don't ever remind me any more") should be available.


I wish they would stop introducing more magic syntaxes.


I agree! I'm a Go programmer, and while I do wish it had some more features at times, Swift is an example of how it can easily go out of control and ruin a promising language.

For example tests, there's so much magic. How do I know it runs the test for each item in the arguments array? What if there were multiple arguments? After using Go for close to a decade now, I'm really seeing the wisdom of avoiding magic, and making your testing code the same language as your building code! Compare:

Swift:

    @Test("Continents mentioned in videos", arguments: [
      "A Beach",
      "By the Lake",
      "Camping in the Woods"
    ])
    func mentionedContinents(videoName: String) async throws {
      let videoLibrary = try await VideoLibrary()
      let video = try #require(await videoLibrary.video(named: videoName))
      #expect(video.mentionedContinents.count <= 3)
    }
Go:

    func TestMentionedContinents(t *testing.T) {
      tests := []struct{ Name string }{
        {"A Beach"},
        {"By the Lake"},
        {"Camping in the Woods"},
      }
      for _, tt := range tests {
        video, err := library.FindVideoByName(tt.Name)
        if err != nil {
          t.Fatalf("failed to get video: %v", err)
        }
        if len(video.MentionedContinents) > 3 {
          t.Errorf("video %q mentions more than 3 continents", tt.Name)
        }
      }
    }


Go with timeout handling in case the FindVideo function takes too long (idk Swift magic well enough to know if it'd do this automatically!)

    func TestMentionedContinents(t *testing.T) {
      tests := []struct{ Name string }{
        {"A Beach"},
        {"By the Lake"},
        {"Camping in the Woods"},
      }
      for _, tt := range tests {
        t.Run(tt.Name, func(t *testing.T) {
          ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond)
          defer cancel()

          video, err := library.FindVideoByName(ctx, tt.Name)
          if err != nil {
            t.Fatalf("failed to get video: %v", err)
          }
          if len(video.MentionedContinents) > 3 {
            t.Errorf("video %q mentions more than 3 continents", tt.Name)
          }
        })
      }
    }


> How do I know it runs the test for each item in the arguments array?

At the risk of coming across a bit rudely: this feels analogous to asking “how do I know `for _, tt := range tests` loops over every element in the array?” Both are language/syntactic constructs you have to learn.


Maybe I'm being a bit harsh myself, but with the Go code, it's the same syntax that I use whenever I would write a for loop anywhere else in my production codebase. It's not something special to testing, it's literally _just code_.

However, I do like Swift, I in fact single-handledy wrote an entire iPhone app used by 10s of thousands of people on it and there were a lot of wonderful things, like nullability being "solved", and smart enums etc. This isn't a language war, I like them both, and could point out flaws in either just as easily.

I just feel like Swift has a bit too low of a bar for adding new features, which leads to a lot of nice things, but also a lot of functionality bloat. I can look at Go written 10 years ago and it'll largely be the same as how it'd be written today; with Swift, it's night and day. I built the aforementioned app in 2014 to 2017 (mostly the first year), and there's so much I don't recognize.

I think one of the things that bothered me the most where I feel they sort of "jumped the shark" is with ViewBuilders. It looks like code, acts like it 99% of the time, but it isn't. Does that `var body: some View { ... }` return a view anywhere? No, and it'll break if you try! It's a whole different concept to admittedly offer a very nice experience emnulating React with idempotent views.

But still, it's awfully strange that this works:

    struct IntroView: View {
      @State private var text = "Yes"
      var body: some View {
        VStack {
          Text(text)
          Button("Toggle") {
            text = text == "Yes" ? "No" : "Yes"
          }
        }
      }
    }

But this does not, because it's not _actually_ code.

    struct IntroView: View {
      @State private var text = "Yes"
      var body: some View {
        VStack {
          Text(text)
          Button("Toggle") {
            text = text == "Yes" ? "No" : "Yes"
          }
          print("debugging line")
        }
      }
    }


Except it is. var body is a variable declaration. some View {…} is nested closures with a single statement each so the return keyword is implied. Just add return in your second example


SwiftUI's ViewBuilder isn't part of the Swift language. It's a macro implemented in Swift.

Now whether macros themselves were a good idea or not, that's an entirely different question.


Right. But it strikes me as one of those things that really helps new developers become productive quickly -- to then, it looks just like code, they probably don't realize it's not code.

But once your app hits a certain size, the abstraction will inevitably leak somewhere, and now you'll need to step back, and learn all about macros and viewbuilders to be able to fix your issues.

Probably worth it! They enable such a wonderful way of drawing a UI. But it's not a zero-cost abstraction, and I like that Go has eschewed _anything_ like that at all. There's no IEnumerable with yield return that compiles into a state matchine. No ViewBuilder that's a macro, not code. There's no Ruby-like adding methods at runtime (Model.find_by_bar_code???). It's all just plain code right in front of you.

They both have their strengths. I think Go made the right trade-off for simple high-throughput microservices, as they're trivial to understand and will never surprise you. And Swift made the right tradeoffs for a UI—-it would be painful to write it in Go.

My reaction as someone who used Swift extensively in 2015, quit, and now came back in 2024 is "wow, did they add too many features? that's a lot of quirks to learn." FWIW I don't feel the same way about TypeScript or C#.

It just feels at first glance like Swift let every feature in the door. And now we've got an awfully complex and laggy compiler in my experience, especially when you run into one of the type inferencing pitfalls leading to an exponential algorithm. Go explicitly made fast compilation a design goal and lost a lot of features to it.


It is code though. It’s a bunch of nested closures with an implicit return


Result builders aren't macros.

@Test, #require and #expect are just macros. You can expand them if you want to see what they do (or just look at the swift-testing code itself).

Perhaps I'm just used to Python unit testing with similar decorators. Presumably, if you need to pass in two arguments, you'd either pass arguments: an array of tuples or a tuple of arrays for combinatorial testing.


> How do I know it runs the test for each item in the arguments array

I mean the APIs aren’t magic; you can “inspect macro” to see what code is generated at compile time which boils down to something similar to the Go code with better ergonomics.


I don't know if I'd agree better ergonomics in this case, since you lose a lot. What if you wanted to load your test cases from a CSV? e.g. you had a two column CSV with 1,000 words, first column mixed case, second case lowercase. They're mixed languages, with unicode oddities sprinkled in. So it really is worth testing against such a corpus. In Go, I could simply open a csv checked into the codebase and use it for my test cases. I'm sure it's possible, but you probably have to break way from the macro (which I argue doesn't add anything) and take a completely different approach. In Go, it's JUST CODE.

Again, I really like Swift (besides xcode performance at times… ugh!). It's possible to find flaws in both languages yes still like them. Swift knocks Go out of the water in so many ways. But I'm scarred from ruby on rails magic growing and growing until you had to be an expert in the magic to write code, when the point was for the magic to make it easier.


Is there a specific one you’re objecting to in 6?


Since you asked:

The provided `drinkable` example I think is pretty bad and it's very surprising to me that this is a headline feature.

  protocol Drinkable: ~Copyable {
    consuming func use()
  }

  struct Coffee: Drinkable, ~Copyable { /\* ... */ }
  struct Water: Drinkable { /* ... \*/ }

  func drink(item: consuming some Drinkable & ~Copyable) {
    item.use()
  }

  drink(item: Coffee())
  drink(item: Water())

Here we have a drink() function that either accepts something `Copyable` OR non-Copyable (uh, I mean, `~Copyable`) and either consumes it...or doesn't? That seems to me like a fountain for logic errors if the function behaves completely differently with the same signature (which is, in fact, explicitly labeled `consuming`). It seems like it should not just compile if you try to call this with a `Copyable` type like Water, but it does.

The syntax for representing this complex and weird concept of "maybe consuming" being `consuming some Drinkable & ~Copyable` is also just totally gross. Why are we using bitwise operation syntax for some weird and logically incoherent kludge? We cannot apply these & and ~ operators indiscriminately, and they do not mean the same thing that they logically mean in any other context, but this function definition definitely implies that they do.


The issue is that `~Copyable` is basically an anti-protocol.

With a generics definition like `some Drinkable` you are _restricting_ the set of suitable types (from any type to only the ones implementing `Drinkable`) which then _expands_ the available functionality (the method use() becomes available). From the perspective of type `Water`, it's conformance to `Drinkable` expands the functionality.

The language designers then get in a pickle if some functionality was assumed to exist for all types (e.g. `Copyable`)! By "conforming" to `~Copyable` you are _removing_ functionality. The type can NOT be copied which was assumed to be universally true before. Now, a generics definition like `some ~Copyable` actually _expands_ the set of suitable types (because Copyable types can be used as if they were non-copyable) and reduces the available functionality. It's the inverse of a regular protocol!

It becomes extra confusing if you combine `some Drinkable & ~Copyable` where `Drinkable` and `~Copyable` work in opposite directions.

This problem also exists in Rust. `Sized` is a trait (aka protocol) that basically all normal types implement, but you can opt-out by declaring `!Sized`. Then, if you actually want to include all types in your generics, you need to write `?Sized` (read: Maybe-Sized).


Here’s my take. I haven’t used this feature yet so I haven’t dug in too deep.

drink() takes a Drinkable. A Drinkables can be non-copyable.

Copyable is the default, so it has to mark itself as accepting non-copyables.

Coffee is non-copyable. Water doesn’t say which means it’s copyable (the default).

You can use a copyable anywhere you’d use a non-copyable since there is no restriction. So since drink can take non-copyables it can also use copyables.

I’m guessing the function definition has to list non-copyable otherwise it would only allow copyable drinks since the default is all variables are copyable.

“consuming some” means the function takes over the ownership of the non-copyable value. It’s no longer usable in the scope that calls drink().

For the copyable value I’m not sure but since they can be copied I could see that going either way.

On syntax:

Yeah it’s a bit weird, but there was a big debate about it. They wanted something easy to read and fast to use. NotCopyable<Drinkable> is really clear but typing it over and over would get real old.

~ is not special syntax. My understand is “~Copyable” is the name of the type. You can’t just put ~ in front of anything, like ~Drinkable. But since that’s the syntax used for bitwise negation it’s pretty guessable.

& is existing syntax for multiple type assertions. You can see the evolution in this Stack Overflow answer:

https://stackoverflow.com/a/24089278

Seems to read like C to me. It has to be Drinkable and not Copyable.

Like I said I haven’t gotten to use this yet, but it seems like a nice improvement. And I know it’s a step in the path towards making it easier to do safe asynchronous programming, object lifetimes, and other features.


Yeah after thinking about it a bit more it does make more sense to me. The primary gap I had was, as you allude here:

> You can use a copyable anywhere you’d use a non-copyable since there is no restriction.

Effectively copyable always conforms to non-copyable, just not the other way around.

And the compiler effectively automatically notates literally everything with Copyable, so you need the explicit (& ~Copyable) in the function definition so you're still able to define functions within a ~Copyable protocol that have Copyable semantics.

It's very in the weeds, and I still don't like it (I would have preferred NotCopyable since the ~, especially next to the &, directly implies something like bitwise operators), but I guess custom ownership is itself very in the weeds and you will have to think about it hard no matter what approach is taken. I would have expected custom ownership to be fundamentally incompatible with Swift, but clearly it's here; I should probably read about it more so I have a more clear understanding.

(I also didn't realize & was extant syntax).


Yeah, I’ll admit it’s hard to get your head around. I had to think about it a couple of times just writing that explanation.

It took me a couple of minutes to figure out why it was in the function definition. I guess it had to be but that wasn’t obvious to me at all at first.

> And the compiler effectively automatically notates literally everything

Right. Just like how all classes in Java extend Object even though you don’t have to literally write it.

I believe they’re still working on a more Rust-like borrowing system, but I could be wrong. I know this helped them implement parts of the standard library much better because they could make assumptions that you can’t make with copyable objects.

I do get your point about just calling it NotCopyable. I don’t actually know why they settled on the name they did, I didn’t ever try to look that up. Maybe it’s because it’s a special thing that requires compiler machinery and there’s no way for you to make an equivalent?


The title sort of implies this is intentional or privileged to Apple, while it rather seems more like just a bug.

I also wish people would post the FB numbers and the details of their report when they say they've reported things like this.


Devil's advocate would say: They could do this and make it look like a bug that never gets fixed in order to avoid backlash. How it gets achieved is flexible if the goal is met.


Why would they be afraid of backlash on such an obscure, technical feature? They never were in the past and are expected to take controversial technical decisions by now. And by “now”, I mean in the last 30-odd years.


Yeah, if it was intentional, it would probably be a hard-coded, encrypted URL. Some devices are starting to do that to get around ad blocking.


Good thing you can still see the domain over the network if you control the network.


You can’t control anything if they do DNS over HTTPS to a hardcoded IP they control and cert pin so you can’t MITM the connection, can you?


That's what a firewall is for.


If the pinned cert is stored on some kind of ROM chip you could probably rewrite it to replace it with your own cert.


You can at the very least block traffic to the hardcoded IP.


Sure, but then DNS breaks on the device and it's useless. Might as well just hit it with a hammer.

> Once you need to be in the apple developer program to build and run from source or something, that’ll be a legitimate nightmare. But we’re nowhere near that yet.

This is the case for building and running things with restricted entitlements and system extensions.

Unless you disable system integrity protection entirely, which locks you out of your purchased App Store software, DRM content, etc.


>which locks you out of your purchased App Store software, DRM content

Also false. But Apple's glad you believe in that.


It does lock you out of iOS apps, Apple Pay, and 4K streaming of DRM content [1]. But that's not so bad I suppose.

[1] https://github.com/cormiertyshawn895/RecordingIndicatorUtili...


I was mistaken; I was conflating Permissive Security with SIP. Permissive Security does have those limitations.


You can no longer disable system integrity protection.



Source?


The release notes for Sequoia.


Nope. Not the enterprise release notes or the security content notes either.


What I'm referencing is that they removed `spctl --master-disable`. This was referenced in release notes that I read upon upgrading, and testing it on my own system confirms it is gone.

Looks like there might be a way of achieving the same thing through the GUI? https://www.reddit.com/r/MacOSBeta/comments/1e2xlcg/disablin...


That's Gatekeeper, not System Integrity Protection. You can disable SIP with `csrutil` by rebooting into the macOS recovery environment.


> It’s reasonable to think that the other — “Open System Settings” - allows one to make the change permanently.

It does not. All it does is what it says on the tin- open System Settings.


Before yesterday's Sequoia update, this was a daily permission prompt.

In a vacuum, I don't consider this to be an extremely poor decision; the system picker has a mostly-sane interface. Apps that capture the screen currently have an extremely long leash, and the hole for an app that decided to become malicious and start (for example) exfiltrating capture data is... extremely wide.

However, for apps that took pains prior to macOS Sequoia in offering a reasonable and privacy-focused consent interface for screen capture (that doesn't use the picker, that is), this prompt is insulting and a slap in the face. Apple did not do a good job with its developers with the introduction of this 'feature'.

Furthermore, the system picker lacks important functionality (can't capture apps without currently visible windows, can't do 'everything-except-these-apps' captures) and its configurations have no time-to-live, meaning an app would have to use the picker every single time it starts up, or else get this prompt. This really undermines the picker and the prompt's effectiveness, and it's disappointing to see something well-intentioned be self-defeating in this way.

In general, ScreenCaptureKit is one of the most poorly managed and poorly functioning APIs Apple has introduced in recent years. It offers solid functionality, but every release introduces a legion of new bugs and performance regressions. I dunno if someone key to this APIs success quit or what, but it really needs to be fixed up by someone with actual care for the details.


Apple could just make it obvious when something is recording the screen. If there is an orange dot what it means is a mystery, sometimes there is no further info in control center so you have to guess was it some recent discord call or you got pwned. Simply fixing that would address a lot of the problem. Is that better in Sequoia?


Of course. Draw a red rectangle around the screen, invert the menu bar, whatever.

Instead, they are copying the “accept cookies” / Windows Vista policy of nagging the user to the point of oblivion.


an icon appears in the menu bar when an app is capturing the screen. clicking it says which app it is.


That already exists, right? I’m guessing they are concerned it’s too subtle.


No, what I meant doesn't exist in current macOS. I record screen often so I just checked:) There's an abstract orange dot on control center and often no further info if you expand control center. Sometimes the dot is there even though I didn't record. Which app did it? Who knows.


I think that would be a lot easier to target as an exploit than the engineering they would have already done to the broader solution of permissions

Especially if left unmonitored/updated


What are the issues the author alludes to about AppX packages sucking? Looks like the new Fluent stuff wants you to use those. Curious to know what pain points exist there.


This is a bit of a tangential rant, so I apologize. But I recently tried to update Death Stranding, the flagship AAA game that Apple landed on macOS and in the App Store. The game itself is 77.5 GB; it had required a little over 150 GB to install. I shrugged that off at the time; they haven't figured out how to decompress on the fly, whatever.

I had about 50GB free on my (1 TB M1 Max) machine at the time of the update, and the App Store told me I didn't have enough free space to update. I balked and looked at the update description, which was just "Various minor bug fixes." The update size was...75 GB. They expect me to re-download the entire game for various minor bug fixes.

(I would "just blame the developer" here, but Apple clearly invested a lot into having this game available on macOS, in the App Store, and got Hideo Kojima to show up at WWDC (a full year ago, remember) and brag about how nice the entire experience is. Some of Apple's engineers probably worked directly on this port.)

It then sunk in that I didn't just need 75 GB of free space. I needed 150 GB. The App Store will completely download and completely decompress the entire game before replacing it. That is the patching process for the game. You need 231 GB free at all times on your machine to have and update a 77GB game.

This is completely insulting given Apple's storage prices, and the fact that the App Store does not let you install apps on an external drive.

Apple clearly doesn't get it. They act like they're nominally putting in the effort, but then it's still just completely half-assed even when things are played exactly like they want.


There's like, two guys at Apple who care about gaming. Everyone once in a while they manage to convince marketing to say something about it and rope in a couple more devs to half-assedly hammer out some tickets before they can go back to their normal tasking. There's no traction internally to taking PC gaming seriously at Apple.


From what I’ve heard there are a ton more than 2. But none of them have the power.


You don't need indoor hobbies when the weather is this nice.


Boldly typed


It's the warmest first week of June in a while in Cupertino, so I have no idea what you're talking about with regards to nice weather.


Well, Apple employees don't actually live in Cupertino.

(I once went to a city council meeting where residents showed up and complained that new housing might allow Apple employees to live there and that they might be "too poor". Probably would be too.)


If you're not living in Cupertino you're not doing enough spontaneous collaboration


Wow. Truly that's bizarre.

Clearly the App Store was never meant for distribution of 75 GB games, it's clearly meant for mostly <~1 GB packages.

I'm surprised they even allow huge apps like that in the first place, and don't have e.g. a 5 GB hard limit for usability reasons. They should have a limit if they're not going to support patch upgrades.

Who the heck has 231 GB free on their Mac internal SSD? Almost nobody. Why would the developer even distribute this via the App Store at all, for such a miniscule user base?


Xcode used to be absolutely massive, being 10GB+ a few years back. They seemed to have done a better job at making it smaller.


> I had about 50GB free on my (1 TB M1 Max) machine at the time of the update, and the App Store told me I didn't have enough free space to update. I balked and looked at the update description, which was just "Various minor bug fixes." The update size was...75 GB. They expect me to re-download the entire game for various minor bug fixes.

It is an unfortunate truth that one must now bear in mind, that games are for all intents and purposes enterprise software. They are as critical to profitability for the companies involved as enterprise software. They are large, distributed applications which are planned, budgeted, and staffed much like enterprise software. Accordingly, there is little to no concern for performance except when it's absolutely critical: the rendering pipeline and the netcode, for instance. For things like updates, where it would be easy to make some optimizations to reduce download size, those optimizations will not be taken. So you will redownload the entire game, including all of the uncompressed audio clips, every time someone changes a byte somewhere and it's shipped as an update.


Steam updates are often in the order of kilobytes. Clearly it can be done.


It can be done, and Valve are old-school gamedev bros who clearly are interested in making it happen for games on their platform. But not every shop is like that, and in particular I wince when I contemplate updating a PS4 game...


You need to buy a new mac so you can pay $200 per 256gb of storage.


This is a problem even for macOS updates, ever since they moved to the sealed system volume in macOS 11. When you update macOS, it downloads the entire OS and installs it to a separate APFS snapshot. The infuriating thing about this is that the sealed system volume should actually make it easier to provide reliable delta updates, but instead they used it as an excuse to remove them.

And of course Apple's always treated app updates as "download entire new copy of app to separate container and relaunch", ever since day one of the iOS App Store. This too could be handled with APFS snapshots.

I really wish Apple - and the rest of the industry - would stop being so damned allergic to delta updates. It's infuriating knowing how much damned engineering effort, say, Google put into shipping deltas on Chrome, and then everyone else is "just download two copies of every app while you're updating them, bandwidth and storage is free if we don't pay for them".


I am pretty sure App Store updates are smarter than that. For example Xcode delta installs have taken far less space (though they take a lot longer) if you grab them from the App Store.


I just rember back when my internet was 512 kbps down and iTunes would have a minor bugfix update over >100 MB which would take over an hour.

I'd just not update iTunes ever.


The goals of this project and the goals of N64 emulation are fairly different.

The goal of N64 emulators (generally) is to accurately recreate the behavior of the original console.

Conversely, there is a large crowd of people who just want to play SM64, OOT and MM with a bevy of "graphical enhancements" like 60fps, widescreen, texture packs, randomizers, etc.

For these people, the fact that these games originally ran on the N64 is practically irrelevant. In fact, it's a hindrance; The N64's graphical pipeline is cumbersome to emulate and actively stands in the way of modern 'enhancements.'

This project is more aimed at giving the games themselves modern treatments, removing the N64-centric aspects of the games from the equation entirely.


Honestly SM64 with new graphics in those demo videos looks worse than the original game. But Gamecube games running in an emulator look amazing because they use a lot more vector graphics, which the emu can scale up and apply antialiasing to.

What interests me more about recompilation is it can produce very efficient binaries instead of having to emulate everything. And also makes it easier to add new features.


Yeah, SM64 is weird because a lot of '3d' objects used billboarding, it was actually just a flat object always facing the camera, so they don't scale up well. Anything spherical (chain chomp for example).

Hilarious proof: https://imgur.com/gallery/Xw4tN#FoRAGl7

The characters in Mario Kart 64 were like that too, they just prerendered a heap of different angles and inserted the appropriate one.

With recompilation we could change those things, but I'm not sure how much work it would be and how many people who care to play those games would want to alter them that much.

Early 3d games will always have those rough edges sadly.


Also a lot of bitmaps on textures and stuff. It's fine though, it's a classic 3D game, no need to renovate it.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: