{"id":15055367,"url":"https://github.com/ra1028/swiftui-hooks","last_synced_at":"2025-04-08T08:12:30.388Z","repository":{"id":37801249,"uuid":"340379286","full_name":"ra1028/swiftui-hooks","owner":"ra1028","description":"🪝 A SwiftUI implementation of React Hooks. Enhances reusability of stateful logic and gives state and lifecycle to function view.","archived":false,"fork":false,"pushed_at":"2024-10-16T02:48:52.000Z","size":2026,"stargazers_count":513,"open_issues_count":1,"forks_count":22,"subscribers_count":11,"default_branch":"main","last_synced_at":"2025-04-08T08:12:20.719Z","etag":null,"topics":["hooks","ios","macos","react","react-hooks","swift","swiftui","tvos","watchos"],"latest_commit_sha":null,"homepage":"https://ra1028.github.io/swiftui-hooks/documentation/hooks/","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ra1028.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"ra1028"}},"created_at":"2021-02-19T13:35:40.000Z","updated_at":"2025-03-11T14:12:14.000Z","dependencies_parsed_at":"2024-10-30T12:14:20.173Z","dependency_job_id":null,"html_url":"https://github.com/ra1028/swiftui-hooks","commit_stats":{"total_commits":93,"total_committers":3,"mean_commits":31.0,"dds":0.06451612903225812,"last_synced_commit":"4d74bb4370a8e00c0bb51067cda601fa66537a92"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2Fswiftui-hooks","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2Fswiftui-hooks/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2Fswiftui-hooks/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ra1028%2Fswiftui-hooks/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ra1028","download_url":"https://codeload.github.com/ra1028/swiftui-hooks/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247801169,"owners_count":20998339,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["hooks","ios","macos","react","react-hooks","swift","swiftui","tvos","watchos"],"created_at":"2024-09-24T21:41:37.695Z","updated_at":"2025-04-08T08:12:30.361Z","avatar_url":"https://github.com/ra1028.png","language":"Swift","readme":"\u003ch1 align=\"center\"\u003eSwiftUI Hooks\u003c/h1\u003e\n\u003cp align=\"center\"\u003eA SwiftUI implementation of \u003ca href=\"https://reactjs.org/docs/hooks-intro.html\"\u003eReact Hooks\u003c/a\u003e.\u003c/p\u003e\n\u003cp align=\"center\"\u003eEnhances reusability of stateful logic and gives state and lifecycle to function view.\u003c/p\u003e\n\u003cp align=\"center\"\u003e\u003ca href=\"https://ra1028.github.io/swiftui-hooks/documentation/hooks\"\u003e📔 API Reference\u003c/a\u003e\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ra1028/swiftui-hooks/actions\"\u003e\u003cimg alt=\"test\" src=\"https://github.com/ra1028/swiftui-hooks/workflows/test/badge.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/ra1028/swiftui-hooks/releases/latest\"\u003e\u003cimg alt=\"release\" src=\"https://img.shields.io/github/v/release/ra1028/swiftui-hooks.svg\"/\u003e\u003c/a\u003e\n  \u003ca href=\"https://developer.apple.com/swift\"\u003e\u003cimg alt=\"Swift5\" src=\"https://img.shields.io/badge/language-Swift5-orange.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://developer.apple.com\"\u003e\u003cimg alt=\"Platform\" src=\"https://img.shields.io/badge/platform-iOS%20%7C%20macOS%20%7C%20tvOS%20%7C%20watchOS%20%7C-green.svg\"\u003e\u003c/a\u003e\n  \u003ca href=\"LICENSE\"\u003e\u003cimg alt=\"license\" src=\"https://img.shields.io/badge/license-MIT-black.svg\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n- [Introduction](#introduction)\n- [Getting Started](#getting-started)\n- [Hooks API](#hooks-api)\n- [Rules of Hooks](#rules-of-hooks)\n- [Building Your Own Hooks](#building-your-own-hooks)\n- [How to Test Your Custom Hooks](#how-to-test-your-custom-hooks)\n- [Context](#context)\n- [License](#license)\n\n---\n\n## Introduction\n\n```swift\nfunc timer() -\u003e some View {\n    let time = useState(Date())\n\n    useEffect(.once) {\n        let timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) {\n            time.wrappedValue = $0.fireDate\n        }\n\n        return {\n            timer.invalidate()\n        }\n    }\n\n    return Text(\"Time: \\(time.wrappedValue)\")\n}\n```\n\nSwiftUI Hooks is a SwiftUI implementation of React Hooks. Brings the state and lifecycle into the function view, without depending on elements that are only allowed to be used in struct views such as `@State` or `@ObservedObject`.  \nIt allows you to reuse stateful logic between views by building custom hooks composed with multiple hooks.  \nFurthermore, hooks such as `useEffect` also solve the problem of lack of lifecycles in SwiftUI.  \n\nThe API and behavioral specs of SwiftUI Hooks are entirely based on React Hooks, so you can leverage your knowledge of web applications to your advantage.  \n\nThere're already a bunch of documentations on React Hooks, so you can refer to it and learn more about Hooks.  \n\n- [React Hooks Documentation](https://reactjs.org/docs/hooks-intro.html)  \n- [Youtube Video](https://www.youtube.com/watch?v=dpw9EHDh2bM)  \n\n---\n\n## Getting Started\n\n### Requirements\n\n|       |Minimum Version|\n|------:|--------------:|\n|Swift  |5.6            |\n|Xcode  |13.3           |\n|iOS    |13.0           |\n|macOS  |10.15          |\n|tvOS   |13.0           |\n|watchOS|6.0            |\n\n## Installation\n\nThe module name of the package is `Hooks`. Choose one of the instructions below to install and add the following import statement to your source code.\n\n```swift\nimport Hooks\n```\n\n#### [Xcode Package Dependency](https://developer.apple.com/documentation/xcode/adding_package_dependencies_to_your_app)\n\nFrom Xcode menu: `File` \u003e `Swift Packages` \u003e `Add Package Dependency`\n\n```text\nhttps://github.com/ra1028/swiftui-hooks\n```\n\n#### [Swift Package Manager](https://www.swift.org/package-manager)\n\nIn your `Package.swift` file, first add the following to the package `dependencies`:\n\n```swift\n.package(url: \"https://github.com/ra1028/swiftui-hooks\"),\n```\n\nAnd then, include \"Hooks\" as a dependency for your target:\n\n```swift\n.target(name: \"\u003ctarget\u003e\", dependencies: [\n    .product(name: \"Hooks\", package: \"swiftui-hooks\"),\n]),\n```\n\n### Documentation\n\n- [API Reference](https://ra1028.github.io/swiftui-hooks/documentation/hooks)\n- [Example apps](Examples)\n\n---\n\n## Hooks API\n\n👇 Click to open the description.  \n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseState\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useState\u003cState\u003e(_ initialState: State) -\u003e Binding\u003cState\u003e\nfunc useState\u003cState\u003e(_ initialState: @escaping () -\u003e State) -\u003e Binding\u003cState\u003e\n```\n\nA hook to use a `Binding\u003cState\u003e` wrapping current state to be updated by setting a new state to `wrappedValue`.  \nTriggers a view update when the state has been changed.\n\n```swift\nlet count = useState(0)  // Binding\u003cInt\u003e\n\nButton(\"Increment\") {\n    count.wrappedValue += 1\n}\n```\n\nIf the initial state is the result of an expensive computation, you may provide a closure instead.\nThe closure will be executed once, during the initial render.\n\n```swift\nlet count = useState {\n    let initialState = expensiveComputation() // Int\n    return initialState\n}                                             // Binding\u003cInt\u003e\n\nButton(\"Increment\") {\n    count.wrappedValue += 1\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseEffect\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useEffect(_ updateStrategy: HookUpdateStrategy? = nil, _ effect: @escaping () -\u003e (() -\u003e Void)?)\n```\n\nA hook to use a side effect function that is called the number of times according to the strategy specified with `updateStrategy`.  \nOptionally the function can be cancelled when this hook is disposed or when the side-effect function is called again.  \nNote that the execution is deferred until after other hooks have been updated.  \n\n```swift\nuseEffect {\n    print(\"Do side effects\")\n\n    return {\n        print(\"Do cleanup\")\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseLayoutEffect\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useLayoutEffect(_ updateStrategy: HookUpdateStrategy? = nil, _ effect: @escaping () -\u003e (() -\u003e Void)?)\n```\n\nA hook to use a side effect function that is called the number of times according to the strategy specified with `updateStrategy`.  \nOptionally the function can be cancelled when this hook is unmount from the view tree or when the side-effect function is called again.  \nThe signature is identical to `useEffect`, but this fires synchronously when the hook is called.  \n\n```swift\nuseLayoutEffect {\n    print(\"Do side effects\")\n    return nil\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseMemo\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useMemo\u003cValue\u003e(_ updateStrategy: HookUpdateStrategy, _ makeValue: @escaping () -\u003e Value) -\u003e Value\n```\n\nA hook to use memoized value preserved until it is updated at the timing determined with given `updateStrategy`.  \n\n```swift\nlet random = useMemo(.once) {\n    Int.random(in: 0...100)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseRef\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useRef\u003cT\u003e(_ initialValue: T) -\u003e RefObject\u003cT\u003e\n```\n\nA hook to use a mutable ref object storing an arbitrary value.  \nThe essential of this hook is that setting a value to `current` doesn't trigger a view update.  \n\n```swift\nlet value = useRef(\"text\")  // RefObject\u003cString\u003e\n\nButton(\"Save text\") {\n    value.current = \"new text\"\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseReducer\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useReducer\u003cState, Action\u003e(_ reducer: @escaping (State, Action) -\u003e State, initialState: State) -\u003e (state: State, dispatch: (Action) -\u003e Void)\n```\n\nA hook to use the state returned by the passed `reducer`, and a `dispatch` function to send actions to update the state.  \nTriggers a view update when the state has been changed.  \n\n```swift\nenum Action {\n    case increment, decrement\n}\n\nfunc reducer(state: Int, action: Action) -\u003e Int {\n    switch action {\n        case .increment:\n            return state + 1\n\n        case .decrement:\n            return state - 1\n    }\n}\n\nlet (count, dispatch) = useReducer(reducer, initialState: 0)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseAsync\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useAsync\u003cOutput\u003e(_ updateStrategy: HookUpdateStrategy, _ operation: @escaping () async -\u003e Output) -\u003e AsyncPhase\u003cOutput, Never\u003e\nfunc useAsync\u003cOutput\u003e(_ updateStrategy: HookUpdateStrategy, _ operation: @escaping () async throws -\u003e Output) -\u003e AsyncPhase\u003cOutput, Error\u003e\n```\n\nA hook to use the most recent phase of asynchronous operation of the passed function.  \nThe function will be performed at the first update and will be re-performed according to the given `updateStrategy`.  \n\n```swift\nlet phase = useAsync(.once) {\n    try await URLSession.shared.data(from: url)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseAsyncPerform\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useAsyncPerform\u003cOutput\u003e(_ operation: @escaping @MainActor () async -\u003e Output) -\u003e (phase: AsyncPhase\u003cOutput, Never\u003e, perform: @MainActor () async -\u003e Void)\nfunc useAsyncPerform\u003cOutput\u003e(_ operation: @escaping @MainActor () async throws -\u003e Output) -\u003e (phase: AsyncPhase\u003cOutput, Error\u003e, perform: @MainActor () async -\u003e Void)\n```\n\nA hook to use the most recent phase of the passed asynchronous operation, and a `perform` function to call the it at arbitrary timing.  \n\n```swift\nlet (phase, perform) = useAsyncPerform {\n    try await URLSession.shared.data(from: url)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003eusePublisher\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc usePublisher\u003cP: Publisher\u003e(_ updateStrategy: HookUpdateStrategy, _ makePublisher: @escaping () -\u003e P) -\u003e AsyncPhase\u003cP.Output, P.Failure\u003e\n```\n\nA hook to use the most recent phase of asynchronous operation of the passed publisher.  \nThe publisher will be subscribed at the first update and will be re-subscribed according to the given `updateStrategy`.  \n\n```swift\nlet phase = usePublisher(.once) {\n    URLSession.shared.dataTaskPublisher(for: url)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003eusePublisherSubscribe\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc usePublisherSubscribe\u003cP: Publisher\u003e(_ makePublisher: @escaping () -\u003e P) -\u003e (phase: AsyncPhase\u003cP.Output, P.Failure\u003e, subscribe: () -\u003e Void)\n```\n\nA hook to use the most recent phase of asynchronous operation of the passed publisher, and a `subscribe` function to subscribe to it at arbitrary timing.  \n\n```swift\nlet (phase, subscribe) = usePublisherSubscribe {\n    URLSession.shared.dataTaskPublisher(for: url)\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseEnvironment\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useEnvironment\u003cValue\u003e(_ keyPath: KeyPath\u003cEnvironmentValues, Value\u003e) -\u003e Value\n```\n\nA hook to use environment value passed through the view tree without `@Environment` property wrapper.  \n\n```swift\nlet colorScheme = useEnvironment(\\.colorScheme)  // ColorScheme\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cCODE\u003euseContext\u003c/CODE\u003e\u003c/summary\u003e\n\n```swift\nfunc useContext\u003cT\u003e(_ context: Context\u003cT\u003e.Type) -\u003e T\n```\n\nA hook to use current context value that is provided by `Context\u003cT\u003e.Provider`.  \nThe purpose is identical to use `Context\u003cT\u003e.Consumer`.  \nSee [Context](#context) section for more details.  \n\n```swift\nlet value = useContext(Context\u003cInt\u003e.self)  // Int\n```\n\n\u003c/details\u003e\n\nSee also: [React Hooks API Reference](https://reactjs.org/docs/hooks-reference.html)  \n\n---\n\n## Rules of Hooks\n\nIn order to take advantage of the wonderful interface of Hooks, the same rules that React hooks has must also be followed by SwiftUI Hooks.  \n\n**[Disclaimer]**: These rules are not technical constraints specific to SwiftUI Hooks, but are necessary based on the design of the Hooks itself. You can see [here](https://reactjs.org/docs/hooks-rules.html) to know more about the rules defined for React Hooks.  \n\n\\* In -Onone builds, if a violation against this rules is detected, it asserts by an internal sanity check to help the developer notice the mistake in the use of hooks. However, hooks also has `disableHooksRulesAssertion` modifier in case you want to disable the assertions.  \n\n### Only Call Hooks at the Function Top Level\n\nDo not call Hooks inside conditions or loops. The order in which hook is called is important since Hooks uses [LinkedList](https://en.wikipedia.org/wiki/Linked_list) to keep track of its state.  \n\n🟢 **DO**\n\n```swift\n@ViewBuilder\nfunc counterButton() -\u003e some View {\n    let count = useState(0)  // 🟢 Uses hook at the top level\n\n    Button(\"You clicked \\(count.wrappedValue) times\") {\n        count.wrappedValue += 1\n    }\n}\n```\n\n🔴 **DON'T**\n\n```swift\n@ViewBuilder\nfunc counterButton() -\u003e some View {\n    if condition {\n        let count = useState(0)  // 🔴 Uses hook inside condition.\n\n        Button(\"You clicked \\(count.wrappedValue) times\") {\n            count.wrappedValue += 1\n        }\n    }\n}\n```\n\n### Only Call Hooks from `HookScope` or `HookView.hookBody`\n\nIn order to preserve the state, hooks must be called inside a `HookScope`.  \nA view that conforms to the `HookView` protocol will automatically be enclosed in a `HookScope`.  \n\n🟢 **DO**\n\n```swift\nstruct CounterButton: HookView {  // 🟢 `HookView` is used.\n    var hookBody: some View {\n        let count = useState(0)\n\n        Button(\"You clicked \\(count.wrappedValue) times\") {\n            count.wrappedValue += 1\n        }\n    }\n}\n```\n\n```swift\nfunc counterButton() -\u003e some View {\n    HookScope {  // 🟢 `HookScope` is used.\n        let count = useState(0)\n\n        Button(\"You clicked \\(count.wrappedValue) times\") {\n            count.wrappedValue += 1\n        }\n    }\n}\n```\n\n```swift\nstruct ContentView: HookView {\n    var hookBody: some View {\n        counterButton()\n    }\n\n    // 🟢 Called from `HookView.hookBody` or `HookScope`.\n    @ViewBuilder\n    var counterButton: some View {\n        let count = useState(0)\n\n        Button(\"You clicked \\(count.wrappedValue) times\") {\n            count.wrappedValue += 1\n        }\n    }\n}\n```\n\n🔴 **DON'T**\n\n```swift\n// 🔴 Neither `HookScope` nor `HookView` is used, and is not called from them.\n@ViewBuilder\nfunc counterButton() -\u003e some View {\n    let count = useState(0)\n\n    Button(\"You clicked \\(count.wrappedValue) times\") {\n        count.wrappedValue += 1\n    }\n}\n```\n\nSee also: [Rules of React Hooks](https://reactjs.org/docs/hooks-rules.html)  \n\n---\n\n## Building Your Own Hooks\n\nBuilding your own hooks lets you extract stateful logic into reusable functions.  \nHooks are composable since they serve as a stateful functions. So, they can be able to compose with other hooks to create your own custom hook.  \n\nIn the following example, the most basic `useState` and `useEffect` are used to make a function provides a current `Date` with the specified interval. If the specified interval is changed, `Timer.invalidate()` will be called and then a new timer will be activated.  \nLike this, the stateful logic can be extracted out as a function using Hooks.  \n\n```swift\nfunc useTimer(interval: TimeInterval) -\u003e Date {\n    let time = useState(Date())\n\n    useEffect(.preserved(by: interval)) {\n        let timer = Timer.scheduledTimer(withTimeInterval: interval, repeats: true) {\n            time.wrappedValue = $0.fireDate\n        }\n\n        return {\n            timer.invalidate()\n        }\n    }\n\n    return time.wrappedValue\n}\n```\n\nLet's refactor the `Example` view at the beginning of the README using this custom hook.  \n\n```swift\nstruct Example: HookView {\n    var hookBody: some View {\n        let time = useTimer(interval: 1)\n\n        Text(\"Now: \\(time)\")\n    }\n}\n```\n\nIt's so much easier to read and less codes!  \nOf course the stateful custom hook can be called by arbitrary views.  \n\nSee also: [Building Your Own React Hooks](https://reactjs.org/docs/hooks-custom.html)  \n\n---\n\n## How to Test Your Custom Hooks\n\nSo far, we have explained that hooks should be called within `HookScope` or `HookView`. Then, how can the custom hook you have created be tested?  \nTo making unit testing of your custom hooks easy, SwiftUI Hooks provides a simple and complete test utility library.  \n\n`HookTester` enables unit testing independent of UI of custom hooks by simulating the behavior on the view of a given hook and managing the result values.  \n\nExample:  \n\n```swift\n// Your custom hook.\nfunc useCounter() -\u003e (count: Int, increment: () -\u003e Void) {\n    let count = useState(0)\n\n    func increment() {\n        count.wrappedValue += 1\n    }\n\n    return (count: count.wrappedValue, increment: increment)\n}\n```\n\n```swift\nlet tester = HookTester {\n    useCounter()\n}\n\nXCTAssertEqual(tester.value.count, 0)\n\ntester.value.increment()\n\nXCTAssertEqual(tester.value.count, 1)\n\ntester.update()  // Simulates view's update.\n\nXCTAssertEqual(tester.value.count, 1)\n```\n\n---\n\n## Context\n\nReact has a way to pass data through the component tree without having to pass it down manually, it's called `Context`.  \nSimilarly, SwiftUI has `EnvironmentValues` to achieve the same, but defining a custom environment value is a bit of a pain, so SwiftUI Hooks provides Context API that a more user-friendly.  \nThis is a simple wrapper around the `EnvironmentValues`.  \n\n```swift\ntypealias ColorSchemeContext = Context\u003cBinding\u003cColorScheme\u003e\u003e\n\nstruct ContentView: HookView {\n    var hookBody: some View {\n        let colorScheme = useState(ColorScheme.light)\n\n        ColorSchemeContext.Provider(value: colorScheme) {\n            darkModeButton\n                .background(Color(.systemBackground))\n                .colorScheme(colorScheme.wrappedValue)\n        }\n    }\n\n    var darkModeButton: some View {\n        ColorSchemeContext.Consumer { colorScheme in\n            Button(\"Use dark mode\") {\n                colorScheme.wrappedValue = .dark\n            }\n        }\n    }\n}\n```\n\nAnd of course, there is a `useContext` hook that can be used instead of `Context.Consumer` to retrieve the provided value.  \n\n```swift\n@ViewBuilder\nvar darkModeButton: some View {\n    let colorScheme = useContext(ColorSchemeContext.self)\n\n    Button(\"Use dark mode\") {\n        colorScheme.wrappedValue = .dark\n    }\n}\n```\n\nSee also: [React Context](https://reactjs.org/docs/context.html)  \n\n---\n\n## Acknowledgements\n\n- [React Hooks](https://reactjs.org/docs/hooks-intro.html)\n- [Flutter Hooks](https://github.com/rrousselGit/flutter_hooks)\n\n---\n\n## License\n\n[MIT © Ryo Aoyama](LICENSE)\n\n---\n","funding_links":["https://github.com/sponsors/ra1028"],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fra1028%2Fswiftui-hooks","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fra1028%2Fswiftui-hooks","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fra1028%2Fswiftui-hooks/lists"}