{"id":18756245,"url":"https://github.com/simonberner/memorize21","last_synced_at":"2025-09-01T13:31:55.556Z","repository":{"id":44888185,"uuid":"394762056","full_name":"simonberner/memorize21","owner":"simonberner","description":"My first iOS App ever! Available on the AppStore! 🥳 (Swift, SwiftUI, Particle System, SwiftLint, StoreKit2, fastlane)","archived":false,"fork":false,"pushed_at":"2023-04-10T11:11:56.000Z","size":9596,"stargazers_count":6,"open_issues_count":6,"forks_count":1,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-11-07T17:56:24.319Z","etag":null,"topics":["fastlane","in-app-purchase","ios","ipados","storekit2","swift","swiftlint","swiftui","xctest"],"latest_commit_sha":null,"homepage":"https://apps.apple.com/app/memorize21/id1587918694","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/simonberner.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","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}},"created_at":"2021-08-10T19:34:51.000Z","updated_at":"2024-03-16T07:29:21.000Z","dependencies_parsed_at":"2024-11-07T17:47:14.721Z","dependency_job_id":"57f3ddbe-a9da-4766-821d-cd7cad7af645","html_url":"https://github.com/simonberner/memorize21","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonberner%2Fmemorize21","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonberner%2Fmemorize21/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonberner%2Fmemorize21/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simonberner%2Fmemorize21/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simonberner","download_url":"https://codeload.github.com/simonberner/memorize21/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":231693385,"owners_count":18411894,"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":["fastlane","in-app-purchase","ios","ipados","storekit2","swift","swiftlint","swiftui","xctest"],"created_at":"2024-11-07T17:35:45.731Z","updated_at":"2024-12-29T01:58:56.510Z","avatar_url":"https://github.com/simonberner.png","language":"Swift","funding_links":["https://www.buymeacoffee.com/simonberner"],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=center\u003eMemorize21\u003c/h1\u003e\n\nMemorize21 is a classical [Memory Game](https://en.wikipedia.org/wiki/Matching_game) and is my very first iOS Development project.\n\n\u003cp align=\"center\"\u003e\n    \u003ca href=\"https://en.wikipedia.org/wiki/IOS\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/iOS-16.2+-blue.svg?style=for-the-badge\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://en.wikipedia.org/wiki/IPadOS\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/iPadOS-16.2+-blue.svg?style=for-the-badge\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://www.swift.org/\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Swift-5.8-brightgreen.svg?style=for-the-badge\u0026logo=swift\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/xcode/swiftui\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/SwiftUI-blue.svg?style=for-the-badge\u0026logo=swift\u0026logoColor=black\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://developer.apple.com/xcode\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Xcode-14.3-blue.svg?style=for-the-badge\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://mastodon.green/@simonberner\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Contact-@simonberner-orange?style=for-the-badge\" alt=\"mastodon.green/@simonberner\" /\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://gitmoji.dev\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/gitmoji-%20😜%20😍-FFDD67.svg?style=for-the-badge\" alt=\"Gitmoji\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://github.com/conventional-commits/conventionalcommits.org\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/Conventional%20Commits-📝-lightgrey.svg?style=for-the-badge\" alt=\"Conventional Commits\"\u003e\n    \u003c/a\u003e\n    \u003ca href=\"https://opensource.org/licenses/MIT\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/license-MIT-black.svg?style=for-the-badge\" /\u003e\n    \u003c/a\u003e\n\u003c/p\u003e\n\n---\n\n## Contents\n* [Compatibility](#compatibility)\n* [Screens](#screens)\n* [Tech Stack](#tech-stack)\n* [Used Tools](#used-tools)\n* [Architecture](#architecture)\n* [Code comments](#code-comments)\n* [App Store](#app-store)\n* [Learning Journey](#learning-journey)\n* [Fastlane](#fastlane)\n* [Contribution Guidelines](#contribution-guidelines)\n* [Credits](#credits)\n* [Release Notes](#release-notes)\n\n---\n\n## Compatibility\n- As of version 3 of the App, only iOS/iPadOS 16.2+ are supported!\n\n## Screens\n![Memorize21](Memorize21/memorize21.png)\n![Memorize21](Memorize21/memorize21-go.gif)\n\n## Tech Stack\n- [Xcode 14.3](https://developer.apple.com/xcode/)\n- [Swift 5.8](https://swift.org/)\n- [SwiftUI](https://developer.apple.com/xcode/swiftui/)\n- [StoreKit2](https://developer.apple.com/storekit/)\n\n## Used Tools\n- [SwiftLint](https://github.com/realm/SwiftLint)\n\n## Architecture\n### Architectural Design Pattern\n- [MVVM](https://matteomanferdini.com/mvvm-pattern-ios-swift/)\n\n## Code comments\nIf you have the time to look at my code, you will notice an unusual amount of additional code comments which I have put along the lines. These comments are for my own learning and documentation purposes and probably would not appear in such a way in production code.\n\n## App Store\nThis App is available on the [App Store](https://apps.apple.com/app/memorize21/id1587918694), ready and free to be installed on your personal iPhone or iPad 😉\n\n## Beta Testing using TestFlight\nPast and upcoming beta versions of my App are available for Testing on [TestFlight](https://testflight.apple.com/)! DM me on [Twitter](https://twitter.com/simonbernerdev) if you want to test an upcoming release and I will send you an invite.\n\n## Feature Requests \u0026 Bugs\nFeature Request and Bugs can be reported by opening a [new issue](https://github.com/simonberner/memorize21/issues). I might have a look at them 😁\n\n## Learning Journey\nI am reflecting my whole iOS Development learning journey in one giant XMind MindMap. Rather than day by day journal entries, you will find all the different topics I have faced on my way so far. You will find the MindMap in this repo 😉\n### SwiftUI\n- [Kickstart](https://matteomanferdini.com/swiftui/)\n### MVVM\n- MVVM helps you to respect the separation of concerns design principle\n#### View\n- Is completely decoupled from the Model\n- Is reactive and always reflects the current state of the model\n- (The struct) Is created and thrown away all the time. Only the 'var body' sticks around for a very long time\n- Don't need any state of their own\n- Supposed to be \"stateless\" and drawing what the current state of the Model is\n- Is a Self-referencing protocol and cannot be used as a normal type\n- Use @State purposely and sparingly\n- @State is a \"source of truth\" so it is better not something which belongs to the Model\n- @State is only used to give temporary state to a var\n- @State vars are marked private because no one else can access them anyway\n- @State var will cause the View to rebuild its body anytime the data where the @State var is pointing to, changes.\n(This is like an @ObservedObject but on a random piece of data (instead of a ViewModel).)\nIn the new version of the body, the @State var will continue to point the the data in the heap.\n- @State makes a space for the var in the heap because the View struct is read-only\n#### ViewModel\n- Interoperates the Model for the View\n#### Model\n- The model layer is the foundation of an app's architecture. \n- Holds the permanent state of the app\n### Gradient\n- [SwiftUI has a built-in way to apply gradient color](https://sarunw.com/posts/gradient-in-swiftui/)\n### Dates\n- [Date](https://developer.apple.com/documentation/foundation/date)\n- [Working with dates](https://www.hackingwithswift.com/books/ios-swiftui/working-with-dates)\n- [Dates, DateComponents and Formatting](https://learnappmaking.com/swift-date-datecomponents-dateformatter-how-to/)\n- [Dates in Swift](https://medium.com/codex/working-with-dates-in-swift-9f50390bbc81)\n### Access Control\n- [Swift Access Control](https://docs.swift.org/swift-book/LanguageGuide/AccessControl.html)\n- Make things private by default and change them afterwards when you see that you need to access them from other place in your code\n- internal (this is default): it can be accessed from anywhere in your code\n### @escaping\n- [Escaping Closures](https://docs.swift.org/swift-book/LanguageGuide/Closures.html#ID546)\n- [What is @escaping in Swift?](https://www.donnywals.com/what-is-escaping-in-swift/#:~:text=In%20short%2C%20%40escaping%20is%20used,compiler%20that%20this%20is%20intentional.)\n### Shape\n- All shapes are also Views, they inherit from the View protocol\n- [SwiftUI Doc with examples](https://swiftontap.com/shape)\n- [Built-in shapes](https://www.hackingwithswift.com/quick-start/swiftui/swiftuis-built-in-shapes)\n- The func fill() is a generic function where the 'S' is a don't care (but since there's a 'where S: ShapeStyle', it becomes a \"care a little bit\").\n### Markdown comments\n- [How to add Markdown comments to your code](https://www.hackingwithswift.com/example-code/language/how-to-add-markdown-comments-to-your-code)\n- [Documentation Comment Syntax](https://github.com/apple/swift/blob/main/docs/DocumentationComments.md)\n### Property Wrapper\n- [What is it?](https://docs.swift.org/swift-book/LanguageGuide/Properties.html#ID617)\n- [SwiftUI property wrappers](https://www.hackingwithswift.com/quick-start/swiftui/all-swiftui-property-wrappers-explained-and-compared)\n### View Modifier\n- [Z index](https://www.hackingwithswift.com/quick-start/swiftui/how-to-change-the-order-of-view-layering-using-z-index)\n### Animation\n- Only changes can be animated. This includes the following three things:\n    - ViewModifier arguments\n    - Shapes\n    - The comings and goings (existence or not) of Views in the UI\n- Animation is showing the user changes that have already happened\n- [How to create simple animations](https://steelkiwi.com/blog/how-to-create-simple-swiftui-animations/)\n- Implicit Animations\n    - Less important kinds of animation\n    -  Golden rule: only animates view modifiers for views which are already on screen\n    - View that are coming on screen or going of screen, can be animated with the .transition modifier\n    - All ViewModifier arguments that precede the animation modifier will always be animated.\n    - Eg. whenever scary and upsideDown changes, the opacity/rotationEffect will be animated:\n    - Without .animation(), the changes to opacity/rotation would appear instantly (not animated) on screen.\n    - Important: ViewModifiers after the animation modifier, will NOT be animated!\n    - .animation modifier does not work on containers\n\n```swift\nText(\"Hello World\")\n    .opacity(scary ? 1 : 0)\n    .rotationEffect(Angle.degrees(upsideDown ? 180 : 0))\n    .animation(Animation.easeInOut)\n```\n\n- Explicit Animations\n    - Is much more of a common way of doing animations\n    - Are independent of implicit animations when used in combination\n    - Only animates shapes and view modifiers\n    - Are used for user 'intent functions'\n    - We don't attach a modifier to a view, instead we ask SwiftUI to animate the precise change we want to make.\n- Transitions\n    - A transition determines how a view appears/disappears on the screen.\n    - Transitions have their own animations: Transition.scale.animation()\n    - Are good for making the comings and goings of views looking smooth\n    - There are about 4 precanned transitions which are used the most\n    - AnyTransition is a typed erased transition\n    - [Advanced Transitions](https://swiftui-lab.com/advanced-transitions/)\n- References:\n    - [How to create an explicit animation](https://www.hackingwithswift.com/quick-start/swiftui/how-to-create-an-explicit-animation)\n    - [Implicit and explicit animations](https://sirkif.hashnode.dev/explore-animation-in-swiftui-part-1)\n    - [Basic Animations and Transitions](https://www.appcoda.com/learnswiftui/swiftui-animation.html)\n### Property Observers\n- [Property observers in Swift](https://www.hackingwithswift.com/sixty/7/3/property-observers)\n### @State and @Binding\n- [SwiftUI: “@State” and “@Binding”](https://medium.com/if-let-swift-programming/swiftui-state-and-binding-5d45ed33f323)\n- [Reactive Programming with Bindings](https://learnappmaking.com/binding-swiftui-how-to/)\n### Text and Images\n- [How to insert images into text](https://www.hackingwithswift.com/quick-start/swiftui/how-to-insert-images-into-text)\n### NavigationView\n- [The Complete Guide to NavigationView in SwiftUI](https://www.hackingwithswift.com/articles/216/complete-guide-to-navigationview-in-swiftui)\n### Fonts\n- The system default font of a Text is: San Francisco\n- Have a look at [Google Fonts](https://fonts.google.com/) for choosing your custom fonts\n- For adding custom fonts in SwiftUI have a look at: [How to add custom fonts in SwiftUI](https://betterprogramming.pub/custom-fonts-in-swiftui-d529de69131d)\n### Tools\n- [Integrate SwiftLint into your Xcode project](https://github.com/realm/SwiftLint#xcode)\n    - [Docs](https://realm.github.io/SwiftLint/)\n    - [Configuration](https://github.com/realm/SwiftLint/#configuration)\n    - [How to clean up your code formatting with SwiftLint](https://www.hackingwithswift.com/articles/97/how-to-clean-up-your-code-formatting-with-swiftlint)\n    - [SwiftLint for Swift Packages](https://blog.timac.org/2021/1003-swiftlint-for-swift-packages/)\n### TestFlight\n- [Sean Allen: How to upload and distribute an App](https://www.youtube.com/watch?v=DLvdZtTAJrE)\n- [Are you an App Store developer who is tired of clicking through the \"compliance\" question every time you submit a build?](https://twitter.com/danielpunkass/status/1517507834778370049)\n- [Complying with Encryption Export Regulations](https://developer.apple.com/documentation/security/complying_with_encryption_export_regulations)\n- [Automating the encryption compliance check](https://developer.apple.com/documentation/bundleresources/information_property_list/itsappusesnonexemptencryption)\n### Versioning\n- [Intro to Semantic Versioning](https://www.geeksforgeeks.org/introduction-semantic-versioning/)\n- [Semantic Versioning 2.0.0](https://semver.org/)\n### In-app purchase\n- A StoreKit configuration file lets us mock and create some fake products\n- Testing: We can simulate a failed transaction by selecting the TipStoreKitConfiguration file, then via Editor -\u003e Fail Transactions or Enable Interrupted Purchase\n\n## Fastlane\nThe fastest steps which I took to setup fastlane for this project were the following:\n1. Install fastlane: ```brew install fastlane```\n2. In the project directory: ``fastlane init``` and follow the instructions\n(Don't use 'fastlane init swift' yet, as it is still in beta and just creates a mess at the moment.\nSee also https://docs.fastlane.tools/getting-started/ios/fastlane-swift/)\n3. Have a look at my [Ruby file](Memorize21/fastlane/Fasfile)\n4. Create an App specific password in your Apple account: https://appleid.apple.com/account/manage -\u003e Security\n5. Run lanes with e.g: ```fastlane beta```\n### Troubleshooting\n- In case that you run out of 'Distribution' certificates, check the Certificates section in your Apple Developer account and delete unused certificates.\n\n## Contribution Guidelines\n### Unit-Testing\nFollow the [Arrange, Act and Assert Pattern](https://automationpanda.com/2020/07/07/arrange-act-assert-a-pattern-for-writing-good-tests/) for Unit Testing.\n- Arrange inputs and targets:\n    - Does the test require any objects or special settings?\n    - Does it need to prep a database?\n    - Does it need to log into a web app?\n- Act on target behavior:\n    - This step should call the function/method/API, or whatever needs to be tested.\n    - It should focus on the target behavior.\n- Assert expected outcomes:\n    - This step should elicit some sort of response.\n    - Then the response should be judged for correctness.\n### UI-Testing\nFollow the [Given-When-Then style](https://martinfowler.com/bliki/GivenWhenThen.html) to write the UI-Tests from a user perspective.\n### Code Comments\n- Include \"[self documenting code](https://www.youtube.com/watch?v=1NEa-OcsTow).\"\n- Additionally, include descriptive comments in order to improve readability.\n    -Aim for comments that allow other programmers to understand your code without having to work through your entire classes/methods.\n### Pull Requests\n- Pull requests should be kept to a reasonable size.\n- Pull requests should be descriptive, answer the following questions:\n    - What was the problem?\n    - What did you do to improve it?\n    - How do you know it is a working solution?/What tests did you do?\n    - Why do you believe this is the right solution?\n    - Why is this the best solution for the problem?\n- Review/proofread your pull request before submission.\nFollowing this [guide](https://www.youtube.com/watch?v=_sfzAOfY8uc).\n\n## Extra: Xcode shortcuts\n- CMD + B: build the project\n- CMD + R: build and run the App in the simulator\n- CMD + SHIFT + K: clean build folder\n- CMD + SHIFT + O: open quick search\n- CMD + SHIFT + J: highlight the selected open file in the navigator\n- CMD + SHIFT + L: open up the library (Snippets, Media, Colors, SF Symbols)\n\n## Credits\n- [Tunde Adegoroye](https://www.youtube.com/@tundsdev) for the TipJar (with StoreKit2) part\n\n\u003chr\u003e\n\u003cp align=\"center\"\u003e\nMade with a 🙂 \u003ca href=\"https://simonberner.dev\"\u003eSimon Berner\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003ca href=\"https://www.buymeacoffee.com/simonberner\" target=\"_blank\"\u003e\u003cimg src=\"https://cdn.buymeacoffee.com/buttons/v2/default-blue.png\" alt=\"Buy Me A Coffee\" style=\"height: 60px !important;width: 217px !important;\" \u003e\u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonberner%2Fmemorize21","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimonberner%2Fmemorize21","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimonberner%2Fmemorize21/lists"}