Skip to content

Commit

Permalink
Added button initializers
Browse files Browse the repository at this point in the history
  • Loading branch information
Rspoon3 committed Feb 22, 2024
1 parent ba8ff2b commit e3d872f
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 1 deletion.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

### Added

- None
- Added initializers for SwiftUI Button.

### Changed

Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,17 @@ Label("MyText", systemSymbol: .cCircle)
Label(LocalizedStringKey("my.text"), systemSymbol: SFSymbol.eCircleFill)
```

... and an initializer for `SwiftUI.Button`:

```swift
Button("MyText", systemSymbol: .cCircle){}
Button(LocalizedStringKey("my.text"), systemSymbol: SFSymbol.eCircleFill){}
Button("MyText", systemSymbol: .cCircle, role: .cancel){}
Button(LocalizedStringKey("my.text"), systemSymbol: SFSymbol.eCircleFill, role: .cancel){}
Button("MyText", systemSymbol: .cCircle, role: .cancel, intent: intent)
Button(LocalizedStringKey("my.text"), systemSymbol: SFSymbol.eCircleFill, intent: intent)
```

... and an initializer for `UIApplicationShortcutItem`:

```swift
Expand Down
8 changes: 8 additions & 0 deletions SFSafeSymbols.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
636A46C928002819007E6028 /* SFSymbol+AllSymbols+2.1.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A46B928002819007E6028 /* SFSymbol+AllSymbols+2.1.swift */; };
636A46CA28002819007E6028 /* SFSymbol+AllSymbols+2.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636A46BA28002819007E6028 /* SFSymbol+AllSymbols+2.0.swift */; };
636EBFD427EB2ABC005CFF4C /* SymbolWithLocalizations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 636EBFD327EB2ABC005CFF4C /* SymbolWithLocalizations.swift */; };
949BD15D2B74155100A10AB7 /* SwiftUIButtonExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949BD15C2B74155100A10AB7 /* SwiftUIButtonExtensionTests.swift */; };
949BD15F2B74156800A10AB7 /* SwiftUIButtonExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 949BD15E2B74156800A10AB7 /* SwiftUIButtonExtension.swift */; };
B00FBD8E285E146400A7878B /* LocalizationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B00FBD8D285E146400A7878B /* LocalizationTests.swift */; };
B01C186D2B55D9B300AC4288 /* SFSymbol+5.0.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01C18652B55D9B300AC4288 /* SFSymbol+5.0.swift */; };
B01C186E2B55D9B300AC4288 /* SFSymbol+5.2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B01C18662B55D9B300AC4288 /* SFSymbol+5.2.swift */; };
Expand Down Expand Up @@ -85,6 +87,8 @@
636A46B928002819007E6028 /* SFSymbol+AllSymbols+2.1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SFSymbol+AllSymbols+2.1.swift"; sourceTree = "<group>"; };
636A46BA28002819007E6028 /* SFSymbol+AllSymbols+2.0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SFSymbol+AllSymbols+2.0.swift"; sourceTree = "<group>"; };
636EBFD327EB2ABC005CFF4C /* SymbolWithLocalizations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolWithLocalizations.swift; sourceTree = "<group>"; };
949BD15C2B74155100A10AB7 /* SwiftUIButtonExtensionTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIButtonExtensionTests.swift; sourceTree = "<group>"; };
949BD15E2B74156800A10AB7 /* SwiftUIButtonExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SwiftUIButtonExtension.swift; sourceTree = "<group>"; };
B00FBD8D285E146400A7878B /* LocalizationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationTests.swift; sourceTree = "<group>"; };
B01C18652B55D9B300AC4288 /* SFSymbol+5.0.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SFSymbol+5.0.swift"; sourceTree = "<group>"; };
B01C18662B55D9B300AC4288 /* SFSymbol+5.2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SFSymbol+5.2.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -248,6 +252,7 @@
children = (
CCCCCFE8252F61E700C7CE9F /* SwiftUIImageExtension.swift */,
CCCCCFEA252F61E700C7CE9F /* SwiftUILabelExtension.swift */,
949BD15E2B74156800A10AB7 /* SwiftUIButtonExtension.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
Expand Down Expand Up @@ -288,6 +293,7 @@
children = (
CCCCD011252F622B00C7CE9F /* SwiftUIImageExtensionTests.swift */,
CCCCD014252F622B00C7CE9F /* SwiftUILabelExtensionTests.swift */,
949BD15C2B74155100A10AB7 /* SwiftUIButtonExtensionTests.swift */,
);
path = SwiftUI;
sourceTree = "<group>";
Expand Down Expand Up @@ -432,6 +438,7 @@
636A46CA28002819007E6028 /* SFSymbol+AllSymbols+2.0.swift in Sources */,
B0AEA82C2801A8E000A02015 /* SFSymbol+3.3.swift in Sources */,
636A46BC28002819007E6028 /* SFSymbol+2.1.swift in Sources */,
949BD15F2B74156800A10AB7 /* SwiftUIButtonExtension.swift in Sources */,
B0AEA82D2801A8E000A02015 /* SFSymbol+AllSymbols+3.3.swift in Sources */,
B01C18742B55D9B300AC4288 /* SFSymbol+AllSymbols+5.0.swift in Sources */,
B01C18732B55D9B300AC4288 /* SFSymbol+AllSymbols+4.2.swift in Sources */,
Expand All @@ -457,6 +464,7 @@
B0DDAE152637C11400081F1C /* CodableTests.swift in Sources */,
CCCCD016252F622B00C7CE9F /* SwiftUIImageExtensionTests.swift in Sources */,
CC1D812F27F758D200CACDC7 /* TestHelper.swift in Sources */,
949BD15D2B74155100A10AB7 /* SwiftUIButtonExtensionTests.swift in Sources */,
CCCCD064252F804400C7CE9F /* NSImageExtensionTests.swift in Sources */,
CCCCD017252F622B00C7CE9F /* UIApplicationShortcutIconExtensionTests.swift in Sources */,
CCCCD01A252F622B00C7CE9F /* UIImageExtensionTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#if canImport(SwiftUI)

import SwiftUI
import AppIntents

@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
public extension Button where Label == SwiftUI.Label<Text, Image>{

/// Creates a button that generates its label from a string and an `SFSymbol`.
///
/// - Parameters
/// - title: A string that describes the purpose of the button’s action.
/// - systemSymbol: The `SFSymbol` describing this image.
/// - action: The action to perform when the user triggers the button.
init<S>(
_ title: S,
systemSymbol: SFSymbol,
action: @escaping () -> Void
) where S : StringProtocol {
self.init(
title,
systemImage: systemSymbol.rawValue,
action: action
)
}

/// Creates a button that generates its label from a localized string key and an `SFSymbol`.
///
/// - Parameters
/// - titleKey: The key for the button’s localized title, that describes the purpose of the button’s action.
/// - systemSymbol: The `SFSymbol` describing this image.
/// - action: The action to perform when the user triggers the button.
init(
_ titleKey: LocalizedStringKey,
systemSymbol: SFSymbol,
action: @escaping () -> Void
) {
self.init(
titleKey,
systemImage: systemSymbol.rawValue,
action: action
)
}

/// Creates a button with a specified role that generates its label from a string and an `SFSymbol`.
///
/// - Parameters
/// - title: A string that describes the purpose of the button’s action.
/// - systemSymbol: The `SFSymbol` describing this image.
/// - role: An optional semantic role describing the button. A value of nil means that the button doesn’t have an assigned role.
/// - action: The action to perform when the user triggers the button.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
init<S>(
_ title: S,
systemSymbol: SFSymbol,
role: ButtonRole?,
action: @escaping () -> Void
) where S : StringProtocol {
self.init(
title,
systemImage: systemSymbol.rawValue,
role: role,
action: action
)
}

/// Creates a button with a specified role that generates its label from a localized string key and an `SFSymbol`.
///
/// - Parameters
/// - titleKey: The key for the button’s localized title, that describes the purpose of the button’s action
/// - systemSymbol: The `SFSymbol` describing this image.
/// - role: An optional semantic role describing the button. A value of nil means that the button doesn’t have an assigned role.
/// - action: The action to perform when the user triggers the button.
@available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *)
init(
_ titleKey: LocalizedStringKey,
systemSymbol: SFSymbol,
role: ButtonRole?,
action: @escaping () -> Void
) {
self.init(
titleKey,
systemImage: systemSymbol.rawValue,
role: role,
action: action
)
}

/// Creates a button with a specified role that performs an AppIntent and generates its label from a localized string key and an `SFSymbol`.
///
/// - Parameters
/// - titleKey: The key for the button’s localized title, that describes the purpose of the button’s intent.
/// - systemSymbol: The `SFSymbol` describing this image.
/// - role: An optional semantic role describing the button. A value of nil means that the button doesn’t have an assigned role.
/// - intent: The AppIntent to execute.
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
init(
_ titleKey: LocalizedStringKey,
systemSymbol: SFSymbol,
role: ButtonRole? = nil,
intent: some AppIntent
) {
self.init(
titleKey,
systemImage: systemSymbol.rawValue,
role: role,
intent: intent
)
}

/// Creates a button with a specified role that generates its label from a string and an `SFSymbol`.
///
/// - Parameters
/// - title: A string that describes the purpose of the button’s intent.
/// - systemSymbol: The `SFSymbol` describing this image.
/// - role: An optional semantic role describing the button. A value of nil means that the button doesn’t have an assigned role.
/// - intent: The AppIntent to execute.
@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
init(
_ title: some StringProtocol,
systemSymbol: SFSymbol,
role: ButtonRole? = nil,
intent: some AppIntent
) {
self.init(
title,
systemImage: systemSymbol.rawValue,
role: role,
intent: intent
)
}
}
#endif
71 changes: 71 additions & 0 deletions Tests/SFSafeSymbolsTests/SwiftUI/SwiftUIButtonExtensionTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
@testable import SFSafeSymbols

#if !os(watchOS)

import XCTest

#if canImport(SwiftUI)

import SwiftUI
import AppIntents


class ButtonExtensionTests: XCTestCase {
/// Tests, whether the `Label` retrieved via SFSafeSymbols can be retrieved without a crash
func testInit() {
if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) {
for symbol in TestHelper.allSymbolsWithVariants {
print("Testing validity of \"\(symbol.rawValue)\" via Label init")

// If this doesn't crash, everything works fine
let title: String = ""
_ = Button(title, systemSymbol: symbol) {}

let localizedStringKey: LocalizedStringKey = ""
_ = Button(localizedStringKey, systemSymbol: symbol) {}

if #available(iOS 15.0, macOS 12.0, tvOS 15.0, watchOS 8.0, visionOS 1.0, *) {
let title: String = ""
_ = Button(title, systemSymbol: symbol, role: .cancel) {}

let localizedStringKey: LocalizedStringKey = ""
_ = Button(localizedStringKey, systemSymbol: symbol, role: .cancel) {}
}

if #available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *) {
let title: String = ""
_ = Button(title, systemSymbol: symbol, role: .cancel, intent: OrderSoupIntent())

let localizedStringKey: LocalizedStringKey = ""
_ = Button(localizedStringKey, systemSymbol: symbol, role: .cancel, intent: OrderSoupIntent())
}
}
} else {
print("To test the Button initializer, iOS 14, macOS 11.0 or tvOS 14 is required.")
}
}

@available(iOS 17.0, macOS 14.0, tvOS 17.0, watchOS 10.0, *)
struct OrderSoupIntent: AppIntent {
static var title = LocalizedStringResource("Order Soup")
static var description = IntentDescription("Orders a soup from your favorite restaurant.")

init() { }

func perform() async throws -> some IntentResult {
return .result()
}
}
}

#else

class JustFail: XCTestCase {
func justFail() {
XCTFail("SwiftUI should be available when testing.")
}
}

#endif

#endif

0 comments on commit e3d872f

Please sign in to comment.