{"id":13828889,"url":"https://github.com/MontanaFlossCo/Flint","last_synced_at":"2025-07-09T06:33:22.336Z","repository":{"id":56911426,"uuid":"120908184","full_name":"MontanaFlossCo/Flint","owner":"MontanaFlossCo","description":"The Flint framework for building apps on Apple platforms using Feature Driven Development","archived":false,"fork":false,"pushed_at":"2023-08-01T16:16:41.000Z","size":2218,"stargazers_count":629,"open_issues_count":73,"forks_count":22,"subscribers_count":20,"default_branch":"master","last_synced_at":"2024-10-21T04:09:49.945Z","etag":null,"topics":["appframework-library","apple","ios","logging","macos","siri","swift","tvos","watchos"],"latest_commit_sha":null,"homepage":"https://flint.tools","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/MontanaFlossCo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","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}},"created_at":"2018-02-09T13:21:18.000Z","updated_at":"2024-05-10T23:43:52.000Z","dependencies_parsed_at":"2024-06-19T06:15:00.171Z","dependency_job_id":"ce787e31-4bbb-45f9-a90f-24aaa69ce42b","html_url":"https://github.com/MontanaFlossCo/Flint","commit_stats":{"total_commits":245,"total_committers":5,"mean_commits":49.0,"dds":0.01632653061224487,"last_synced_commit":"1b3acf6d59a7404ac60cc84a4c9908d54d11b8cb"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MontanaFlossCo%2FFlint","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MontanaFlossCo%2FFlint/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MontanaFlossCo%2FFlint/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/MontanaFlossCo%2FFlint/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/MontanaFlossCo","download_url":"https://codeload.github.com/MontanaFlossCo/Flint/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225492420,"owners_count":17482869,"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":["appframework-library","apple","ios","logging","macos","siri","swift","tvos","watchos"],"created_at":"2024-08-04T09:03:17.962Z","updated_at":"2024-11-20T08:31:17.196Z","avatar_url":"https://github.com/MontanaFlossCo.png","language":"Swift","funding_links":[],"categories":["Uncategorized","Swift"],"sub_categories":["Uncategorized"],"readme":"\u003ca href=\"https://flint.tools\"\u003e\u003cimg src=\"https://flint.tools/assets/img/logo-dark-on-white.svg\" width=\"230\" alt=\"Flint framework logo image\"\u003e\u003c/a\u003e\n\n![latest commit](https://img.shields.io/github/last-commit/MontanaFlossCo/Flint.svg)\n![carthage compatible](https://img.shields.io/badge/carthage-compatible-brighttgreen.svg?style=flat)\n![cocoapods compatible](https://img.shields.io/badge/cocoapods-compatible-brighttgreen.svg?style=flat)\n![swift 4.1-5](https://img.shields.io/badge/swift-4.1_to_5.1-blue.svg?style=flat)\n![xcode 9.3-10.2](https://img.shields.io/badge/Xcode-9.3_to_11-blue.svg?style=flat)\n\n[![Bitrise](https://img.shields.io/bitrise/5f6a9a733386ecc8/master.svg?label=Bitrise%3A%20iOS%20Xcode%2010.2\u0026token=2aHeIvCuFPtM5FEs6fw9dg)](https://app.bitrise.io/app/5f6a9a733386ecc8)\n[![Bitrise](https://img.shields.io/bitrise/b97567c7372ecf34/master.svg?label=Bitrise%3A%20macOS%20Xcode%2010.2\u0026token=4t1YoquY5eR5aLhUU8VIxQ)](https://app.bitrise.io/app/b97567c7372ecf34)\n[![Bitrise](https://img.shields.io/bitrise/c86c83980db3d3e2/master.svg?label=Bitrise%3A%20tvOS%20Xcode%2010.2\u0026token=_oUyYJHNFWKvLUsUcGLyfA)](https://app.bitrise.io/app/c86c83980db3d3e2)\n[![Bitrise](https://img.shields.io/bitrise/0433151c78298a0f/master.svg?label=Bitrise%3A%20watchOS%20Xcode%2010.2\u0026token=O2jRB64hjFBROc-qfrLRig)](https://app.bitrise.io/app/0433151c78298a0f)\n\nFlint is a framework for building apps for Apple platforms out of Features and Actions using the power of Swift.\n\nYour app's Features are enabled based on runtime constraints; system permissions, OS version or in-app purchases. It takes your Actions and provides enhanced logging, automatic analytics events, NSUserActivity integration for Handoff, Search and Siri prediction, URL handling, Siri Shortcuts support, user activity timelines for debugging and much more.\n\nIt uses coding conventions much like web application development frameworks like [Rails](https://rubyonrails.org). However it uses the static compilation and associated type features of Swift to provide enhanced safety and code completion. \n\nThe result is apps that are more robust and more polished, with less boilerplate and better decoupling without requiring a massive architectural shift or specific UI/model approach. \n\nWe made Flint because we want people to build great apps for Apple platforms that make the most of native platform capabilities with less hassle. \n\n🏠 [flint.tools](https://flint.tools) is the official web site, with guide \u0026 API docs and blog\n\n💬 [Get help on the FlintCore Slack](https://join.slack.com/t/flintcore/shared_invite/enQtMzUwOTU4NTU0OTYwLWMxYTNiOTNjNmVkOTM3ZDgwNzZiNzJiNmE2NWUyMzUzMjg3ZTg4YjNmMjdhYmZkYTlmYmI2ZDQ5NjU0ZmQ3ZjU)\n\n✉️ [Subscribe to the Flint newsletter](http://eepurl.com/dGW5Uj)\n\n🐦 [Follow @flintframework on Twitter](https://twitter.com/flintframework)\n\n📖 [View the documentation](https://flint.tools/manual) or [help us improve it with a pull request](https://github.com/MontanaFlossCo/Flint-Documentation)\n\n🔬 [View the FlintDemo-iOS sample project](https://github.com/MontanaFlossCo/FlintDemo-iOS)\n\n🎧 [Listen to the iDeveloper podcast interview](http://ideveloper.co/podcast187/) where project lead [Marc Palmer](https://twitter.com/marcpalmerdev) explains the motivation and ideas behind Flint\n\n## The basics\n\n**Features** are what your app can do. Features in Flint conform to the `Feature` protocol:\n\n```swift\nimport FlintCore\n\nclass DocumentManagementFeature: Feature {\n    static let description = \"Create, Open and Save documents\"\n\n    static let openDocument = action(DocumentOpenAction.self)\n\n    static func prepare(actions: FeatureActionsBuilder) {\n        actions.declare(openDocument)\n    }\n }\n```\n\nThey typically have one or more actions, and some have sub-features.\n\n**Actions** are what your users can do with your features, like \"Open a document\", \"Close a document\", \"Share a document\". Actions are types conforming to `Action` that are then declared on features as in the above example, taking an `input` and a `presenter` that are types you define:\n\n```swift\nimport FlintCore\n\nfinal class DocumentOpenAction: Action {\n    typealias InputType = DocumentRef\n    typealias PresenterType = DocumentPresenter\n\n    static var description = \"Open a document\"\n    \n    static func perform(context: ActionContext\u003cDocumentRef\u003e,\n                        presenter: DocumentPresenter,\n                        completion: Completion) -\u003e Completion.Status {\n        presenter.openDocument(context.input)\n        return completion.completedSync(.success)\n    }\n}\n```\n\nOnce you define actions, Flint can observe when your app performs them. This unlocks many\nbehaviours like automatic `NSUserActivity` and Siri integration, analytics tracking and improved debug logging.\n\nHowever, because Flint can also knows how to **invoke your actions** for a given input, it can handle all the different app entry points for you too, including app or deep-linking URLs and continued activities including Handoff, Spotlight, Siri Suggestions.\n[Read more in the Features \u0026 Actions guide](https://flint.tools/manual/guides/features_and_actions).\n\nWhat about features that require in-app purchases or certain system permissions? **Conditional Features** support constraints.\nThese can include platforms, OS versions, system permissions, in-app purchases and more. Thanks to Swift your code can’t\nperform actions of conditional features unless you also handle the case where the feature is not currently available.\n\n```swift\nimport FlintCore\n\nlet premiumSubscription = AutoRenewingSubscriptionProduct(name: \"💎 Premium Subscription\",\n                                                          description: \"Unlock the Selfietron!\",\n                                                          productID: \"SUB0001\")\n\npublic class SelfieFeature: ConditionalFeature {\n    public static var description: String = \"Selfie Posting\"\n\n    public static func constraints(requirements: FeatureConstraintsBuilder) {\n      // Allow the user to turn this on/off themselves\n      requirements.userToggled(defaultValue: true)\n      \n      // Require isEnabled to return `true` at runtime\n      requirements.runtimeEnabled()\n      \n      // Require a purchase for this feature to be enabled\n      requirements.purchase(premiumSubscription)\n      \n      // Require these permissions before the feature's actions can be used\n      requirements.permissions(.camera,\n                               .photos,\n                               .location(usage: .whenInUse))\n    }\n\n    ...\n}\n```\n\nFeatures that require multiple permissions or one of many purchase options are easily accommodated, and Flint will [help you build a first class permissions onboarding](https://flint.tools/manual/guides/conditional_features) UI to maximise the number of users that can use your feature.\n\nWhen you need to perform an action from a conditional feature, you are forced to first check if the feature is available and handle the case where it is not:\n\n```swift\nif let request = DocumentSharingFeature.share.request() {\n    request.perform(withInput: document, presenter: presenter)\n} else {\n    showPremiumUpgradeOrPermissionAuthorisations()\n}\n```\n\nThis makes your code cleaner and safer. Everybody on the team can see which code is internally feature-flagged or requires a\npurchase, and which permissions your app requires.\n\nSee the [programming guide for Conditional Features](https://flint.tools/manual/guides/conditional_features.md) for more details.\n\n## Handling URLs\n\nTo handle incoming URLs all you need to do is define an action – a type that conforms to the `Action` protocol, and add it to a `Feature`\nthat has one or more URL routes for the action.\n\nConsider the common case of handling a user sign-up confirmation link sent by e-mail. The URL will contain a token and the app should\nopen when it is tapped, verify the token and then show the \"You signed in!\" screen.\n\n```swift\nimport FlintCore\n\nclass UserAccountManagementFeature: Feature, URLMapped {\n    static let description = \"User sign-up, sign in and sign out\"\n\n    static let confirmAccount = action(ConfirmAccountAction.self)\n \n    static func prepare(actions: FeatureActionsBuilder) {\n        actions.declare(confirmAccount)\n    }\n       \n    // 💥 Use `routes` to define the URLs and actions\n    static func urlMappings(routes: URLMappingsBuilder) {\n        routes.send(\"account/confirm\", to: confirmAccount)\n    }\n}\n```\n\nOnce you add the custom URL scheme to your `Info.plist` and/or an associated domain to your entitlements, your app would then\ninvoke the \"confirm account\" action when it is asked to open URLs like:\n\n* `your-app://account/confirm`\n* `https://yourappdomain.com/account/confirm`\n\nThere's support for multiple mappings per action, multiple URL schemes and multiple associated domains, so legacy URLs are no problem.\nThere's a little [glue code to add](https://github.com/MontanaFlossCo/Flint-Documentation/blob/master/guides/routes.md) to your app\ndelegate and to set up your UI when the action comes in.\n\nThe action type `ConfirmAccountAction` is not shown here, for brevity. See the [Features and Actions](https://github.com/MontanaFlossCo/Flint-Documentation/blob/master/guides/features_and_actions.md) guide for full details.\n\nOf course you can easily perform this same action from code in your app if required:\n\n```swift\nUserAccountManagementFeature.confirmAccount.perform(withInput: confirmationToken, presenter: presenter)\n```\n\nIf you need to, you can create URLs that link to these mapped actions using [`Flint.linkCreator`](https://github.com/MontanaFlossCo/Flint/blob/master/FlintCore/Core/Flint.swift). \n\nSee the [programming guide for Routes](https://flint.tools/manual/guides/routes.md) for more details.\n\n## Automatic Handoff and Siri Suggestions support\n\nApple's `NSUserActivity` is used extensively for telling the system what the user is currently doing, to integrate Handoff between devices\nSiri app suggestions, some Spotlight Search integration as well as deep linking. All too often people don't implement this, because of the\nchallenges of executing arbitrary actions in your app when the user chooses an activity.\n\nFlint can do this automatically for you, with zero effort if your Action also supports URL routes.\n\n```swift\nimport FlintCore\n\nfinal class DocumentOpenAction: Action {\n    typealias InputType = DocumentRef\n    typealias PresenterType = DocumentPresenter\n\n    static var description = \"Open a document\"\n    \n    // 💥 Just tell Flint what activity types to use\n    static var activityEligibility: Set\u003cActivityEligibility\u003e = [.perform, .handoff]\n    \n    static func perform(context: ActionContext\u003cDocumentRef\u003e,\n                        presenter: DocumentPresenter, \n                        completion: Complettion) -\u003e Completion.Status {\n        // … do the work\n    }\n}\n```\n\nThis is all you have to do, aside from add `NSUserActivityTypes` to your `Info.plist` and list the activity IDs automatically generated by Flint. \n\nYou can of course customise the attributes of the `NSUserActivity` if you want to, by defining a `prepare(activity:for:)` function.\nSee the [Activities guide](https://github.com/MontanaFlossCo/Flint-Documentation/blob/master/guides/activities.md).\n\nSee the [programming guide for Activities](https://flint.tools/manual/guides/activities.md) for more details.\n\n## Track analytics events when users do things\n\nMost apps end up having to do some kind of analytics reporting to get an idea of what your users are actually doing. An analytics event is\ntypically an event ID and a dictionary of keys and values. Flint makes emitting these easy and consistent, using any analytics service you want.\nEven your own home-spun backend. \n\nSo when your marketing people say they want their analytics reporting system to show them when people open documents, you simply set\nthe `analyticsID` property on the action, and Flint's `AnalyticsReporting` component will automatically pick it up whenever that action is\nperformed, passing it to your analytics provider.\n\n```swift\nimport FlintCore\n\nfinal class DocumentOpenAction: Action {\n    typealias InputType = DocumentRef\n    typealias PresenterType = DocumentPresenter\n\n    static let description = \"Open a document\"\n    \n    // 💥 Enable analytics with just one property!\n    static let analyticsID = \"user-open-document\"\n    \n    static func perform(context: ActionContext\u003cDocumentRef\u003e, \n                        presenter: DocumentPresenter, \n                        completion: Completion) -\u003e Completion.Status {\n        // … do the work\n    }\n}\n```\n\nOf course you can customise the dictionary of data passed to the Analytics provider by defining an `analyticsAttributes()` function.\n\nSee the [programming guide for Analytics](https://flint.tools/manual/guides/analytics.md) for more details.\n\n## Getting started\n\nFlint supports Carthage and Cocoapods. See the [Getting Started guide](https://flint.tools/manual/guides/getting_started)\n\n## Find out more\n\nAll this is just the tip of the iceberg. Flint has much more to offer and through the use of protocols almost everywhere, has many extension\nand customisation points so that you aren't locked in to anything like a specific analytics provider.\n\nIf you want to see a sample project that uses Flint, there is the  [FlintDemo-iOS][] project here on Github. You can browse that to get an\nidea of how a real app might use Flint.\n\n[View all the documentation](https://flint.tools/manual/)\n\n## Philosophy\n\nWe are all-in on Swift but we don’t want to be smartypants who can’t read our own code weeks later. We take a few advanced Swift \ngeatures that make great things possible: Protocol Oriented Programming, some generics and a very small amount of associated types.\n\nWe deliberately avoid the more oblique patterns because we want this framework to be very accessible and easy for everybody to reason\nabout, irrespective of the paradigm they have chosen for their codebase.\n\n## Community and Contributing\n\nWe have a community Slack you can join to get help and discuss ideas. Join at [flintcore.slack.com](https://join.slack.com/t/flintcore/shared_invite/enQtMzUwOTU4NTU0OTYwLWMxYTNiOTNjNmVkOTM3ZDgwNzZiNzJiNmE2NWUyMzUzMjg3ZTg4YjNmMjdhYmZkYTlmYmI2ZDQ5NjU0ZmQ3ZjU).\n\nWe would love your contributions. Please raise Issues here in Github and discuss your problems and suggestions. We look forward to your ideas and pull requests.\n\nFlint is copyright Montana Floss Co. with an [MIT open source licence](LICENSE).\n\n[FlintDemo-iOS]: https://github.com/MontanaFlossCo/FlintDemo-iOS\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMontanaFlossCo%2FFlint","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMontanaFlossCo%2FFlint","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMontanaFlossCo%2FFlint/lists"}