-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from allaboutapps/feature/debug-view-package
Feature/debug view package
- Loading branch information
Showing
22 changed files
with
815 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
.DS_Store | ||
/.build | ||
/Packages | ||
xcuserdata/ | ||
DerivedData/ | ||
.swiftpm/configuration/registries.json | ||
.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata | ||
.netrc | ||
|
||
Package.resolved |
8 changes: 8 additions & 0 deletions
8
.swiftpm/xcode/package.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>IDEDidComputeMac32BitWarning</key> | ||
<true/> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
// swift-tools-version: 5.9 | ||
// The swift-tools-version declares the minimum version of Swift required to build this package. | ||
|
||
import PackageDescription | ||
|
||
let package = Package( | ||
name: "DebugView", | ||
defaultLocalization: "en", | ||
platforms: [.iOS(.v16)], | ||
products: [ | ||
.library( | ||
name: "DebugView", | ||
targets: ["DebugView"] | ||
), | ||
], | ||
targets: [ | ||
.target( | ||
name: "DebugView" | ||
), | ||
.testTarget( | ||
name: "DebugViewTests", | ||
dependencies: ["DebugView"] | ||
), | ||
] | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,173 @@ | ||
# debugview-ios | ||
# Debug View iOS | ||
|
||
The Debug Package lets you easily add values meant for debugging purposes and showing them anywhere in your app. | ||
|
||
## Getting Started | ||
|
||
`DebugController` is used to add and store values meant for debugging purposes and `DebugScreen` (SwiftUI) or `DebugCorrdinator` (if you are using UIKit based apps with the Coordinator pattern) is used to display them. | ||
|
||
## Creating Items | ||
### Values | ||
|
||
To create static or dynamic values (either `String` or your own Type which conforms to `CustomDebugStringConvertible` or `CustomStringConvertible`), use either of the initializers of the `DebugValue` struct. | ||
|
||
#### Static Values | ||
|
||
```swift | ||
DebugValue( | ||
id: "staticValue", | ||
label: "Static Value", | ||
staticValue: "Your static value" | ||
) | ||
``` | ||
> ###### Notes | ||
> Static values are evaluated when the `DebugValue` is instantiated. | ||
#### Dynamic Values | ||
|
||
```swift | ||
DebugValue( | ||
id: "dynamicValueAutoClosure", | ||
label: "Dynamic Value Auto Closure", | ||
value: String(Int.random(in: 1 ... 6)) | ||
) | ||
``` | ||
|
||
or: | ||
|
||
```swift | ||
DebugValue( | ||
id: "dynamicValueClosure", | ||
label: "Dynamic Value Closure", | ||
value: { | ||
String(Int.random(in: 1 ... 6)) | ||
} | ||
) | ||
``` | ||
|
||
> ###### Notes | ||
> Dynamic values are wrapped in a closure with `@autoclosure` or your own closure, and evaluated when the `DebugScreen` is shown. Every time the `DebugScreen` is shown again, values are re-evaluated. | ||
### Buttons | ||
|
||
To create buttons which perform a custom action, use the `DebugButton` struct. | ||
|
||
```swift | ||
DebugButton( | ||
id: "button", | ||
label: "The button label", | ||
action: { | ||
// your custom action | ||
} | ||
) | ||
``` | ||
|
||
#### Notes | ||
> Make sure that your `DebugValue` or `DebugButton`s `id`s are unique, as they are used for diffing and identifying the item. | ||
## Sections | ||
|
||
Items are grouped by sections, making the information easier to read. Items are added to sections when inserting them into the `DebugController`. | ||
|
||
```swift | ||
DebugSection( | ||
id: "app", | ||
label: "App" | ||
) | ||
``` | ||
|
||
> ###### Notes | ||
> Make sure that your `id`s are unique, as they are used for diffing and identifying the section. | ||
## Adding Items | ||
|
||
Add items to the `DebugController` by calling either of these functions: | ||
|
||
- `addButton(_ button: DebugButton, toSection section: DebugSection)` | ||
- `addValue(_ value: DebugValue, toSection section: DebugSection)` | ||
|
||
> ###### Notes | ||
> - Adding item with the same `id` twice will override the item in the `DebugController`s store. | ||
> - Only items added with any of the above-mentioned methods are shown in the `DebugScreen`. | ||
> - Sections and Values are ordered by time of insert. This means they will appear in the order they were added to the `DebugController`. | ||
## Showing the Debug Screen | ||
|
||
For SwiftUI based apps: | ||
|
||
```swift | ||
DebugScreen( | ||
controller: debugController, | ||
appearance: debugAppearance | ||
) | ||
|
||
``` | ||
|
||
For UIKit based apps, wrap the `DebugScreen` in a `UIHostingController`. | ||
|
||
### Debug Screen Appearance | ||
The `DebugScreen`s appearance can be configured via the `DebugScreenAppearance` struct. | ||
|
||
```DebugScreenAppearance(tintColor: .red)``` | ||
|
||
## Defaults | ||
|
||
The following `DebugValue`s exist by default: | ||
|
||
```swift | ||
public extension DebugValue { | ||
static let appVersion = DebugValue( | ||
id: "appVersion", | ||
label: "Version", | ||
value: Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String | ||
) | ||
|
||
static let appBuildNumber = DebugValue( | ||
id: "appBuildNumber", | ||
label: "Build Number", | ||
value: Bundle.main.infoDictionary?["CFBundleVersion"] as? String | ||
) | ||
|
||
static let appBundleId = DebugValue( | ||
id: "bundleId", | ||
label: "Bundle Identifier", | ||
value: Bundle.main.bundleIdentifier | ||
) | ||
|
||
static let userLocale = DebugValue( | ||
id: "locale", | ||
label: "Locale", | ||
value: Locale.autoupdatingCurrent.identifier | ||
) | ||
|
||
static let deviceOSVersion = DebugValue( | ||
id: "deviceOSVersion", | ||
label: "OS Version", | ||
value: "\(UIDevice.current.systemName) \(UIDevice.current.systemVersion)" | ||
) | ||
|
||
static let deviceOSModel = DebugValue( | ||
id: "deviceOSModel", | ||
label: "Model", | ||
value: UIDevice.current.model | ||
) | ||
|
||
static let pushNotificationsRegistered = DebugValue( | ||
id: "pushNotificationsRegistered", | ||
label: "Push Notifications registered", | ||
value: UIApplication.shared.isRegisteredForRemoteNotifications | ||
) | ||
} | ||
``` | ||
|
||
The following `DebugSection`s exist by default: | ||
|
||
```swift | ||
public extension DebugSection { | ||
static let app = DebugSection(id: "app", label: "App") | ||
static let user = DebugSection(id: "user", label: "User") | ||
static let device = DebugSection(id: "device", label: "Device") | ||
static let pushNotifications = DebugSection(id: "pushNotifications", label: "Push Notifications") | ||
} | ||
|
||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import Foundation | ||
|
||
/// A controller that handles debug items for the Debug feature. | ||
public class DebugController { | ||
// MARK: - Interface | ||
|
||
public init() {} | ||
|
||
/// Adds a button to the debug store at the corresponding section | ||
public func addButton(_ button: DebugButton, toSection section: DebugSection) { | ||
add(item: .button(button), toSection: section) | ||
} | ||
|
||
/// Adds a value to the debug store at the corresponding section | ||
public func addValue(_ value: DebugValue, toSection section: DebugSection) { | ||
add(item: .value(value), toSection: section) | ||
} | ||
|
||
// MARK: - Private | ||
|
||
private func add(item: DebugItem, toSection section: DebugSection) { | ||
if !containers.contains(where: { $0.section.id == section.id }) { | ||
containers.append(.init(section: section)) | ||
} | ||
|
||
guard let container = containers.first(where: { $0.section.id == section.id }) | ||
else { return } | ||
|
||
// if the item already exists, remove it to override it | ||
if container.items.contains(where: { $0.id == item.id }) { | ||
container.items.removeAll(where: { $0.id == item.id }) | ||
} | ||
|
||
container.items.append(item) | ||
} | ||
|
||
private var containers = [DebugContainer]() | ||
|
||
// MARK: Helpers | ||
|
||
/// Renders sections and values for the debug screen. | ||
func renderDebugSections() -> [FinalDebugContainer] { | ||
containers.map(FinalDebugContainer.init) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
import Foundation | ||
import SwiftUI | ||
|
||
public struct DebugScreen: View { | ||
// MARK: Init | ||
|
||
private let sections: [FinalDebugContainer] | ||
private let appearance: DebugScreenAppearance | ||
|
||
public init( | ||
controller: DebugController, | ||
appearance: DebugScreenAppearance | ||
) { | ||
self.sections = controller.renderDebugSections() | ||
self.appearance = appearance | ||
} | ||
|
||
// MARK: Body | ||
|
||
public var body: some View { | ||
List { | ||
ForEach(sections) { section in | ||
Section { | ||
ForEach(section.items) { item in | ||
viewFor(item: item) | ||
} | ||
} header: { | ||
sectionHeader(section: section) | ||
} | ||
} | ||
shareAllButton | ||
} | ||
.tint(appearance.tintColor) | ||
} | ||
|
||
// MARK: Helpers | ||
|
||
@ViewBuilder | ||
private func viewFor(item: FinalDebugItem) -> some View { | ||
switch item { | ||
case .value(let value): | ||
FinalDebugValueView(value: value) | ||
case .button(let button): | ||
Button(action: { | ||
button.action() | ||
}, label: { | ||
Text(button.label) | ||
}) | ||
} | ||
} | ||
|
||
private func sectionHeader(section: FinalDebugContainer) -> some View { | ||
HStack(alignment: .center, spacing: .zero) { | ||
Text(section.section.label) | ||
|
||
Spacer() | ||
|
||
ShareLink( | ||
item: section.shareableText, | ||
label: { | ||
Image(systemName: "square.and.arrow.up") | ||
} | ||
) | ||
} | ||
} | ||
|
||
private var shareAllButton: some View { | ||
ShareLink( | ||
item: sections.shareableText, | ||
label: { | ||
HStack { | ||
Image(systemName: "square.and.arrow.up") | ||
Text("Share All") | ||
} | ||
} | ||
) | ||
} | ||
} | ||
|
||
// MARK: - Previews | ||
|
||
#Preview { | ||
DebugScreen( | ||
controller: DebugController.createTestController(), | ||
appearance: DebugScreenAppearance(tintColor: .green) | ||
) | ||
} | ||
|
||
private extension DebugController { | ||
static func createTestController() -> DebugController { | ||
let controller = DebugController() | ||
controller.addValue( | ||
DebugValue( | ||
id: "version", | ||
label: "Version", | ||
staticValue: "1.0.0" | ||
), | ||
toSection: .app | ||
) | ||
|
||
controller.addValue( | ||
DebugValue( | ||
id: "diceRoll", | ||
label: "Dice Rolled", | ||
value: String(Int.random(in: 1 ... 6)) | ||
), | ||
toSection: .app | ||
) | ||
|
||
controller.addButton( | ||
DebugButton( | ||
id: "button", | ||
label: "Button", | ||
action: { | ||
print("Button pressed") | ||
} | ||
), | ||
toSection: .user | ||
) | ||
|
||
return controller | ||
} | ||
} |
Oops, something went wrong.