Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make SFSymbol struct #72

Merged
merged 6 commits into from
Nov 10, 2021
Merged

Make SFSymbol struct #72

merged 6 commits into from
Nov 10, 2021

Conversation

StevenSorial
Copy link
Member

Fixes #65

@StevenSorial StevenSorial marked this pull request as ready for review April 23, 2021 19:47
@fredpi
Copy link
Member

fredpi commented Apr 23, 2021

@Stevenmagdy Thanks for starting this! If you need any help with the CI, let me know!

@StevenSorial
Copy link
Member Author

@fredpi yeah, it fails but no logs are shown.

@StevenSorial
Copy link
Member Author

@fredpi All tests have passed.

@fredpi
Copy link
Member

fredpi commented Apr 25, 2021

@Stevenmagdy Thanks again! Just so that you know: I have quite a busy week ahead and will probably only find time to review this in a week or so :)

@fredpi fredpi mentioned this pull request Apr 26, 2021
Copy link
Collaborator

@ddddxxx ddddxxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a quick look. I'll do full review later.

Sources/SFSafeSymbols/Symbols/SFSymbol.swift Outdated Show resolved Hide resolved
@StevenSorial
Copy link
Member Author

StevenSorial commented Apr 29, 2021

As discussed in #65, RawRepresentable and its init(rawValue:) are removed.
Codable methods are added without explicit conformance, Which is the default RawRepresentable behavior. @fredpi @ddddxxx

@StevenSorial StevenSorial requested a review from ddddxxx May 11, 2021 12:23
@fredpi
Copy link
Member

fredpi commented May 12, 2021

As discussed in #65, RawRepresentable and its init(rawValue:) are removed.
Codable methods are added without explicit conformance, Which is the default RawRepresentable behavior. @fredpi @ddddxxx

Maybe it doesn't do harm if we explicitly state conformance even if the RawRepresentable behavior is slightly different? This would mean a (rare) breaking change for some users, but it could be fixed quite easily... What do you think? @Stevenmagdy @knothed @ddddxxx

@knothed
Copy link
Collaborator

knothed commented May 12, 2021

I’d also like to have SFSymbol: Codable as there seems to be no reason against it.

@fredpi
Copy link
Member

fredpi commented May 12, 2021

@Stevenmagdy Thanks again for your great work and sorry for my much delayed review!

Except for the 3 comments I left, everything looks very good to me.

@ddddxxx
Copy link
Collaborator

ddddxxx commented May 13, 2021

I think RawRepresentable carries more significance. As a foundational protocol, RawRepresentable received many special treatments from the compiler (generated Hashable conformance), stdlib (Codable), core libraries (Atomic), system libraries (SwiftUI), and third party libraries (GRDB). And there will be more in the future.

Adding retroactive conformance for a type you don't own to a protocol you don't own is considered bad practice in Swift. So if we don't add SFSymbol: RawRepresentable, nobody else can.

@StevenSorial
Copy link
Member Author

Maybe it doesn't do harm if we explicitly state conformance even if the RawRepresentable behavior is slightly different? This would mean a (rare) breaking change for some users, but it could be fixed quite easily... What do you think? @Stevenmagdy @knothed @ddddxxx

I'm indifferent on this, but I like to follow Apple not conforming to Codable for most (all?) RawRepresentable types.

@fredpi
Copy link
Member

fredpi commented May 14, 2021

I think RawRepresentable carries more significance. As a foundational protocol, RawRepresentable received many special treatments from the compiler (generated Hashable conformance), stdlib (Codable), core libraries (Atomic), system libraries (SwiftUI), and third party libraries (GRDB). And there will be more in the future.

Adding retroactive conformance for a type you don't own to a protocol you don't own is considered bad practice in Swift. So if we don't add SFSymbol: RawRepresentable, nobody else can.

@ddddxxx The issue I see with RawRepresentable conformance is potential ambiguity. Consider a use case where the app caches SFSymbol.a on iOS 13 using the rawValue property. Now, the app reads the cache on iOS 14.2 and checks whether cachedSymbol == SFSymbol.a. Because SFSymbol.a.rawValue equals character from iOS 14.2 on, the check will fail.

Of course this ambiguity also causes issues with Codable so maybe we should not add init(from decoder: Decoder) and func encode(to encoder: Encoder) at all... Or don't even expose rawValue at all.

Actually I'm quite indifferent on this topic, but just don't want to introduce ambiguous behavior (if that's somehow possible without destroying multiple valid use cases while at it).

@fredpi
Copy link
Member

fredpi commented May 14, 2021

@Stevenmagdy I've given you write access, so if you want, you may push your branch to the original repo (instead of your fork) and continue work there

@StevenSorial
Copy link
Member Author

The issue I see with RawRepresentable conformance is potential ambiguity.

I think this ambiguity will always be there as long as, we are conforming to Equatable and\or rawValue is exposed. but this ambiguity should be easily clarified in the README.

My issue with RawRepresentable was the initializer. So as discussed in #65, init(customName:) was exposed to support custom symbols. This was under my assumption that UIimage is using the same init for custom symbols (in the assets). it turns out, it uses init(named:). which is not supported in the UIKit extensions.

So in case, we don't want to introduce support for custom symbols, I think we should conform to RawRepresentable again. init(rawValue:) will still be needed when the user want to define system symbols not defined yet in SFSafeSymbols.(eg. iOS 14.5 includes new symbols but SF Symbols.app 2.2 is not out yet.)

@fredpi @ddddxxx @knothed

Copy link
Collaborator

@ddddxxx ddddxxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that you submitted SymbolEnumCreator/.build/manifest.db, ignore it?

@StevenSorial
Copy link
Member Author

It seems that you submitted SymbolEnumCreator/.build/manifest.db, ignore it?

Are you sure? I think it was actually tracked and I ignored it in this PR.

@fredpi
Copy link
Member

fredpi commented Jun 2, 2021

RawRepresentable

The issue I see with RawRepresentable conformance is potential ambiguity.

I think this ambiguity will always be there as long as, we are conforming to Equatable and\or rawValue is exposed. but this ambiguity should be easily clarified in the README.

I still don't understand why there is any use case for things like Equatable, Hashable, Codable, RawRepresentable... To my understanding, any user that understands the ambiguity won't ever use those, as they just aren't safe.

@ddddxxx wrote the following:

I think RawRepresentable carries more significance. As a foundational protocol, RawRepresentable received many special treatments from the compiler (generated Hashable conformance), stdlib (Codable), core libraries (Atomic), system libraries (SwiftUI), and third party libraries (GRDB). And there will be more in the future.

But are any of these special treatments something one may want to use if one knows about the ambiguity of the rawValue? I really don't want to limit the user, but if there are no safe use cases it would be best to not allow them at all. Am I missing something here, @ddddxxx @Stevenmagdy @knothed?

Custom Symbol Initializer

My issue with RawRepresentable was the initializer. So as discussed in #65, init(customName:) was exposed to support custom symbols. This was under my assumption that UIimage is using the same init for custom symbols (in the assets). it turns out, it uses init(named:). which is not supported in the UIKit extensions.

This is a very important observation, I didn't think about that. So the only use case for a public initializer would be to add symbols that aren't yet available via SFSafeSymbols.

@Stevenmagdy i just want to make sure we make a good decision regarding the RawRepresentable conformance because changing it in a later release may be a major hassle for some users; apart from that, everything looks very good to me 👍

@fredpi fredpi mentioned this pull request Jun 2, 2021
@StevenSorial
Copy link
Member Author

I think v3 might have solved this issue for us. @fredpi

This ambiguity was introduced due to rawValue being determined based on the platform version, mostly because of the renamed symbols.

A user might browse the SF Symbols app, see the character symbol and want to use it in their app

This reason was true in SF Symbols.app <3.0 because it didn't expose old names of a symbol. but v3 solved this issue and now it includes a detailed availability section.

The user now will know that character was introduced in 14.2 and it had an old name.

I think conveniences for accessing the most recent version of a symbol (i.e. #if #available) should be placed in the user code or at least in a separate namespace in SFSafeSymbols, e.g. SFSymbol.compat.character.

Why RawRepresentable?

In addition to the special treatments mentioned by @ddddxxx, Explicit RawRepresentable conformance is meant for types that want to behave like an enumeration, which is the goal for this PR.

@knothed
Copy link
Collaborator

knothed commented Jun 8, 2021

I'm just gonna throw in my opinion here:

Hashable

It makes much sense for SFSymbol to conform to Hashable and Equatable: There may be use cases where the user would filter an array of SFSymbols by equality or would create a Set<SFSymbol>, which requires Equatable and Hashable, respectively.

Hashable explicitly states that the hashValue is not required to be equal across different executions of the program -- perfect for our use case. So there is nothing against support Hashable.

Codable on the other hand doesn't make sense as the Symbol's rawValue isn't stable across different executions of the program, as we've seen with the character example above.

RawRepresentable

The question remains with RawRepresentable: I don't have any reason against it, but I also don't see any particular reason for supporting RawRepresentable.

Explicitly supporting RawRepresentable but not Codable should be warning enough for the user that they shouldn't store said rawValue across program executions – if they still do, it falls out of our responsibility.

But I would like to see a concrete example of why we should support RawRepresentable. Because I can't imagine any real world use case yet. @Stevenmagdy

@StevenSorial
Copy link
Member Author

StevenSorial commented Jun 11, 2021

But I would like to see a concrete example of why we should support RawRepresentable. Because I can't imagine any real world use case yet.

@knothed You are right, There isn't a use case that I can think of. But I think that's also the case with any type conforming to RawRepresentable, including Apple's types.

RawRepresentable in itself doesn't have much significance. it is just used to indicate to the user that it's a type that has enum-like behavior. Even Apple uses it for simple wrapper types like PreviewDevice.

rawValue isn't stable across different executions of the program, as we've seen with the character example above.

I actually agree that we shouldn't conform to RawRepresentable/Codable if that is the case. My argument is based on us getting rid of the #if #available because SF Symbols.app v3 solved the discoverability problem.

@StevenSorial StevenSorial requested review from fredpi and removed request for fredpi July 17, 2021 21:47
@StevenSorial
Copy link
Member Author

Hey @fredpi, What is the status of this PR? I hope it will be merged before the release of iOS 15 🙏

@fredpi
Copy link
Member

fredpi commented Nov 8, 2021

@Stevenmagdy I'm super sorry for the large delay! Life was getting in the way, but that shouldn't count as an excuse for such a long period of inactivity. Looking forward, I'm now committed to merging this and releasing v3 support ASAP, i.e. in the next week.

Looking back at the discussion in this issue, to my understanding it is best to remove RawRepresentableconformance because

rawValue isn't stable across different executions of the program, as we've seen with the character example above.

Or am I missing something again? @knothed @Stevenmagdy

If we can't find a quick solution without discussing this for the next three months, I'd suggest we just merge this. But before, let's see if we can make a quick, informed decision.

@fredpi fredpi mentioned this pull request Nov 8, 2021
@StevenSorial
Copy link
Member Author

StevenSorial commented Nov 8, 2021

because

rawValue isn't stable across different executions of the program, as we've seen with the character example above.

Or am I missing something again? @knothed @Stevenmagdy

Yes, my argument is that we should not make it unstable across executions of the program, and make character only available on iOS >= 14.2 since SFSymbols.app v3.0 solved the discoverability problem for us and now includes the availability in the sidebar, so the user will know that it was called a on earlier iOS versions. Making a convenience function that returns character or a based on the iOS version should be decided by the client, not the library.

This will allow us to conform to RawRepresentable, Equatable, and Hashable without worrying about ambiguity, it will also make the new version 100% source compatible with the older version.

The changes discussed are already implemented in this PR.

@fredpi @knothed I apologize for my poor English, I hope I made my point clear.

@knothed
Copy link
Collaborator

knothed commented Nov 8, 2021

@Stevenmagdy makes sense, but what if Apple decides to rename some symbols in, say, SFSymbols 4?

@StevenSorial
Copy link
Member Author

@Stevenmagdy makes sense, but what if Apple decides to rename some symbols in, say, SFSymbols 4?

We are already supporting that case in the character example, even if apple renames the same symbol again to, say, letter in iOS 16. we would have a symbol in iOS >= 13, character >= 14.2, and letter >= 16, which should be explained in the SFSymbols.app sidebar

@knothed
Copy link
Collaborator

knothed commented Nov 8, 2021

Okay so I‘m fine with either way, conforming to RawRepresentable or not.

@StevenSorial
Copy link
Member Author

@fredpi Should we go ahead and merge this? I will follow it with another PR for the v3 symbols.

@fredpi
Copy link
Member

fredpi commented Nov 10, 2021

@Stevenmagdy Thanks for your explanation, now I get it! I will now merge this. 😃

Thank you again for your great contribution, I really appreciate it!! If you would also follow up with a v3 symbol PR, that'd be great!

By the way: I don't really understand why you think that your English is poor – it's perfectly understandable and probably better than mine!

@fredpi fredpi merged commit 78577b1 into SFSafeSymbols:stable Nov 10, 2021
@StevenSorial StevenSorial deleted the struct-refactor branch November 11, 2021 03:51
@fredpi fredpi mentioned this pull request Nov 22, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

make SFSymbol struct
4 participants