PostHog / posthog-ios

PostHog iOS SDK
https://posthog.com/docs/libraries/ios
MIT License
35 stars 41 forks source link

Swift 6 Compatibility and class property warning #127

Closed adilrc closed 5 months ago

adilrc commented 5 months ago

Problem Statement

I'm using your SDK with Xcode 15.3, and I'm getting a warning about a static property not being concurrency-safe. Can you please update the SDK to make it Swift 6 compatible and fix the warning?

Here's the warning I'm getting in PostHogSDK.swift:54: Class property 'shared' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6

Thanks!

Solution Brainstorm

No response

marandaneto commented 5 months ago

@adilrc thanks for reporting, did you change any configuration to get this warning? I am also using Xcode 15.3 but I don't get this warning. (Swift v6 isn't released yet either).

adilrc commented 5 months ago

Yes I do have strict concurrency checks enabled in my package along with Swift 5.10.

.target(
  ...
  swiftSettings: [
    .enableUpcomingFeature("StrictConcurrency")
  ]
)
marandaneto commented 5 months ago

Yep I figured via Xcode project file as well SWIFT_STRICT_CONCURRENCY = complete; I'm just taking a look into it.

dehesa commented 5 months ago

I believe @adilrc made a small typo. The SPM setting is:

.enableExperimentalFeature("StrictConcurrency"),

You can control the type of strict concurrency by following the attribute with = and then the level; i.e. minimal, targeted, complete. The absence of any mean complete. For example:

.enableExperimentalFeature("StrictConcurrency=targeted"),

If I may offer some advice in how to go about removing the concurrency warnings and making PostHogSDK safer:

public class PostHogSDK: NSObject {
  @objc public nonisolated(unsafe) static let shared = PostHogSDK(PostHogConfig(apiKey: ""))
}
marandaneto commented 5 months ago

enabled is only mutated in the setup and close methods which are manually locked with setupLock so it's not racing. Reading enabled won't be an issue.

dehesa commented 5 months ago

Yes, the enabled case is not really a problem because a Boolean is an "Atomic"-like variable. It was just an example. If you would have a similar situation where your write several properties within a lock but the reading is not locked, you can have memory corruptions or incorrect settings. For example:

In any case, this all goes to say that making the whole SDK properly conform to Swift 6 strict concurrency might be hard (not impossible, though). Since we have a zero warning policy in our CI, PostHog SDK is currently not letting us jump into Xcode 15.3. We might have to hardcode some exception for PostHog, but I would rather avoid that. That is why i was suggesting the nonisolated(unsafe) workaround.

marandaneto commented 5 months ago

Yep makes sense, I'm checking how to remove all the warnings without breaking changes, see PR. At some point, we will migrate to Sendable everywhere + async-await approach but we need to be sure that back compatibility won't be a problem anymore, likely when dropping older versions of Swift.

dehesa commented 5 months ago

Seems like a good plan! I briefly checked your PR and it looks like you are applying major changes. For example:

Regardless, my suggestion is to perform minimal changes to the current PostHog version. For the library users, we only have a single warning when accessing PostHog.shared. Why not just silence the compiler in that single instance with nonisolated(unsafe) and make a minor release. Later on you can perform major changes and release as PostHog 4.0?

PostHog

marandaneto commented 5 months ago

Hey @dehesa, good points.

The @MainActor annotations will be reverted, I wanted to get a feeling of how much work would be to fix (or silence) all the warnings in the SDK as well. Some things will need to be marked as @MainActor but not for this patch, reasons are that some iOS classes are marked as @MainActor, for example, UIDevice and UIScreen, and they are used to enrich the events with device context.

I was not aware of the performance boost by marking them as final, thanks for sharing.

It's just a draft PR that I am using to learn more about the new strict concurrency since I have not played with it before, no intention to merge it as it is but thanks for calling it out.

marandaneto commented 5 months ago

@dehesa the problem is that nonisolated(unsafe) is only available in Swift 5.10 right

dehesa commented 5 months ago

You are correct! I believe you can do the following:

#if swift(>=5.10)
@objc public nonisolated(unsafe) static let shared = PostHogSDK(PostHogConfig(apiKey: ""))
#else
@objc public static let shared = PostHogSDK(PostHogConfig(apiKey: ""))
#endif
dehesa commented 4 months ago

Hey @marandaneto. Any chance you can do a hotfix or minor release to remove the warning from our CI?

marandaneto commented 4 months ago

Hey @marandaneto. Any chance you can do a hotfix or minor release to remove the warning from our CI?

sorry the delay, https://github.com/PostHog/posthog-ios/releases/tag/3.3.0