-
Notifications
You must be signed in to change notification settings - Fork 298
Documentation: Tutorial for Apple #217
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b7e83ec
ceac345
b8dfd8d
edc79d8
97c1536
41db635
148330f
06b5263
d19cf57
6d8396b
5502966
bc74ac5
6e2d5c0
823c3e3
de91e6a
1155d2b
bc585cb
fb724e4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,18 +1,34 @@ | ||
| --- | ||
| layout: tutorial | ||
| title: Coming soon | ||
| title: Build an habit tracker with SwiftUI | ||
| description: Learn to build an Apple app with no backend code using an Appwrite backend. | ||
| framework: Apple | ||
| category: Mobile and native | ||
| step: 1 | ||
| draft: true | ||
| back: /docs | ||
| --- | ||
|
|
||
| Improve the docs, add this guide. | ||
| **Habit tracker**: an app to keep track of your habit. | ||
| In this tutorial, you will build Habit tracker with Appwrite and SwiftUI. | ||
|
|
||
| We still don't have this guide in place, but we do have some great news. | ||
| The Appwrite docs, just like Appwrite, is completely open sourced. | ||
| This means, anyone can help improve them and add new guides and tutorials. | ||
| {% only_dark %} | ||
|  | ||
| {% /only_dark %} | ||
| {% only_light %} | ||
|  | ||
| {% /only_light %} | ||
|
|
||
| If you see this page, **we're actively looking for contributions to this page**. | ||
| Follow our contribution guidelines, open a PR to [our Website repo](https://github.com/appwrite/website), and collaborate with our core team to improve this page. | ||
| # Concepts {% #concepts %} | ||
|
|
||
| This tutorial will introduce the following concepts: | ||
|
|
||
| 1. Setting up your first project | ||
| 2. Authentication | ||
| 3. Databases and collections | ||
gewenyu99 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| 4. Queries and pagination | ||
|
|
||
|
|
||
| # Prerequisites {% #prerequisites %} | ||
|
|
||
| 1. Basic knowledge of Swift and SwiftUI. | ||
| 2. Have [Xcode](https://developer.apple.com/download/all/?q=Xcode) installed on your computer. | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| --- | ||
| layout: tutorial | ||
| title: Create app | ||
| description: Create and app with Appwrite Cloud and Xcode. | ||
| step: 2 | ||
| --- | ||
|
|
||
| # Setup new project {% #new-project %} | ||
|
|
||
| Open Xcode and select **Create new project**, then select app as template for iOS and click **Next**. Fill out all of the required information, such as Product Name, Organization identifier, Interface (SwiftUI), and click **Next**. Select the folder where you want to set up the project and click **Create**. | ||
|
|
||
|  | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # Add the Appwrite SDK {% #appwrite-sdk %} | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| To add the Appwrite SDK for Apple as a dependency, open the **File** menu and click **Add Package Dependencies**. | ||
|
|
||
| In the **Package URL** search box, enter `https://github.com/appwrite/sdk-for-apple`. | ||
|
|
||
| Once the SDK is found, select **Up to Next Major Version** as your **Dependency Rule** and click **Add Package**. | ||
|
|
||
| When dependency resolution is complete, click **Add Package** again to add the SDK package to your target. | ||
|
|
||
|  | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| --- | ||
| layout: tutorial | ||
| title: Set up Appwrite | ||
| description: Import and configure a project with Appwrite Cloud and Xcode. | ||
| step: 3 | ||
| --- | ||
|
|
||
| # Create project {% #create-project %} | ||
|
|
||
| Head to the [Appwrite Console](https://cloud.appwrite.io/console). | ||
|
|
||
| {% only_dark %} | ||
|  | ||
| {% /only_dark %} | ||
| {% only_light %} | ||
|  | ||
| {% /only_light %} | ||
|
|
||
| If this is your first time using Appwrite, create an account and create your first project. | ||
|
|
||
| Then, under **Add a platform**, add an **Apple app**. Choose any of **iOS**, **macOS**, **watchOS** or **tvOS** as your Apple platform. If you are creating a multi-platform app, you can add more platforms later. | ||
|
|
||
| Add your app's **product name** and **bundle identifier**, your bundle identifier is the one entered when creating an Xcode project. For existing projects, you should use the **bundle identifier** from your project files **Identity** section. | ||
|
|
||
| {% only_dark %} | ||
|  | ||
| {% /only_dark %} | ||
| {% only_light %} | ||
|  | ||
| {% /only_light %} | ||
|
|
||
| # Setup Database {% #setup-database %} | ||
|
|
||
| In Appwrite, data is stored as a collection of documents. Create a collection in the [Appwrite Console](https://cloud.appwrite.io/) to store our habits. | ||
|
|
||
| # Create database {% #create-database %} | ||
|
|
||
| Head to your [Appwrite Console](https://cloud.appwrite.io/console/) and create a database and name it `Habit-SwiftUI`. | ||
| Optionally, add a custom database ID. | ||
|
|
||
| # Create collection {% #create-collection %} | ||
| Create a collection and name it `habits`. Optionally, add a custom collection ID. | ||
|
|
||
| Navigate to **Attributes** and create attributes by clicking **Create attribute** and add the following attributes. | ||
|
|
||
| | Field | Type | Required | Size | Min | Max | Default Value | | ||
| |----------------|----------|----------|------|-----|-----|---------------| | ||
| | userId | String | Yes | 250 | | | | | ||
| | title | String | Yes | 250 | | | | | ||
| | description | String | No | | | | | | ||
| | icon | String | No | 200 | | | calendar | | ||
| | goals | Integer | Yes | | 1 | 10 | 1 | | ||
| | goalCompleted | Integer | Yes | | 0 | 10 | 0 | | ||
| | startDate | DateTime | No | | | | | | ||
| | endDate | DateTime | No | | | | | | ||
|
|
||
| Attributes define the structure of your collection's documents. Enter **Attribute key** and **Size**. For example, `title` and `100`. | ||
|
|
||
| Navigate to **Settings** > **Permissions** and add a new role **All Users**. | ||
| Check the **CREATE**, **UPDATE**, **DELETE** and **READ** permissions, so anyone can create and read documents. | ||
|
|
||
| Create a swift file name `Database` in the `Shared/Constant` folder. We will use this file to store the database id and collection id from Appwrite as enum. | ||
|
|
||
| ```swift | ||
| import Foundation | ||
|
|
||
| enum Database: String { | ||
| case habit = "[DATABASE_ID]" | ||
| } | ||
|
|
||
|
|
||
| enum DatabaseCollections : String { | ||
| case habits = "[COLLECTION_ID]" | ||
| } | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,157 @@ | ||
| --- | ||
| layout: tutorial | ||
| title: Manage service and view models | ||
| description: Manage Appwrite wervice using Appwrite Apple SDK and SwiftUI application View Model. | ||
| step: 4 | ||
| --- | ||
|
|
||
| # Appwrite service {% #appwrite-service %} | ||
|
|
||
| Create a new file AppwriteService.swift inside `Shared/Services` folder and add the following code to it, replacing [YOUR_PROJECT_ID] with your project ID. The purpose of this file is to initialize Appwrite SDK and create necessary methods needed for both authentication and read/write from documents. | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I like that clarification was added here 👍🏽 |
||
|
|
||
| ```swift | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| import Foundation | ||
| import Appwrite | ||
| import JSONCodable | ||
|
|
||
| class AppwriteService { | ||
| var client: Client | ||
| var account: Account | ||
| var database: Databases | ||
|
|
||
| public init() { | ||
| self.client = Client() | ||
| .setEndpoint("https://cloud.appwrite.io/v1") | ||
| .setProject("[YOUR_PROJECT_ID]") | ||
| self.account = Account(client) | ||
| self.database = Databases(client) | ||
| } | ||
|
|
||
|
|
||
| public func getDocs<T>(_ db: Database, _ collection: DatabaseCollections, queries: [String]? = nil) async throws -> DocumentList<T> { | ||
| try await database.listDocuments<T>( | ||
| databaseId: db.rawValue, | ||
| collectionId: collection.rawValue, | ||
| queries: queries, | ||
| nestedType: T.self | ||
| ) | ||
| } | ||
|
|
||
| public func insertDoc(_ db: Database, _ collection: DatabaseCollections, data: Any) async throws { | ||
| _ = try await database.createDocument( | ||
| databaseId: db.rawValue, | ||
| collectionId: collection.rawValue, | ||
| documentId: ID.unique(), | ||
| data: data | ||
| ) | ||
| } | ||
|
|
||
|
|
||
| public func updateDoc(_ db: Database, _ collection: DatabaseCollections, _ id: String, data: Any) async throws { | ||
| _ = try await database.updateDocument( | ||
| databaseId: db.rawValue, | ||
| collectionId: collection.rawValue, | ||
| documentId: id, | ||
| data: data | ||
| ) | ||
| } | ||
|
|
||
|
|
||
| public func removeDoc(_ db: Database, _ collection: DatabaseCollections, _ id: String, data: Any) async throws { | ||
| _ = try await database.deleteDocument( | ||
| databaseId: db.rawValue, | ||
| collectionId: collection.rawValue, | ||
| documentId: id | ||
| ) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just noticed what likely happened; a step is missing. Sorry about that. I have updated step 3 to include that part |
||
| } | ||
|
|
||
| } | ||
| ``` | ||
|
|
||
| # Snackbar service {% #snackbar-service %} | ||
|
|
||
| In order to manage error toast, we need a view model to handle and dispatch error messsgae to our UI. Create a new file SnackbarService.swift inside `Shared/Services` folder and add the following code to it. | ||
|
|
||
| ```swift | ||
| import SwiftUI | ||
|
|
||
| struct SnackBarState: Identifiable, Equatable { | ||
| static func == (lhs: SnackBarState, rhs: SnackBarState) -> Bool { | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| lhs.id == rhs.id | ||
| } | ||
|
|
||
| let id = UUID() | ||
| let hasError: Bool | ||
| let error: Error | ||
| } | ||
|
|
||
| class SnackBarService: ObservableObject { | ||
Mujhtech marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| @Published private (set) var snackBarState: SnackBarState? | ||
|
|
||
| @MainActor | ||
| func displayError(_ error: Error) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Very interesting way of showing errors. I definitely have to try this out sometime 😃 |
||
| snackBarState = nil | ||
| snackBarState = SnackBarState(hasError: true, error: error) | ||
| DispatchQueue.main.asyncAfter(deadline: .now() + 4.0, execute: { | ||
| withAnimation(.easeOut(duration: 0.3)) { | ||
| self.snackBarState = nil | ||
| } | ||
| }) | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| Add the following code to SnackbarView.swift inside `Shared/Views` folder. | ||
|
|
||
| ```swift | ||
| import SwiftUI | ||
|
|
||
| struct SnackbarView: View { | ||
| @State private var isAnimating: Bool = false | ||
| @State var text: String | ||
| @State var isError: Bool = true | ||
|
|
||
| var body: some View { | ||
| HStack { | ||
| Text(text) | ||
| .foregroundColor(.white) | ||
| .lineLimit(1...2) | ||
| .multilineTextAlignment(.leading) | ||
| .padding() | ||
| } | ||
| .background(isError ? .red : Color.accentColor) | ||
| .cornerRadius(10) | ||
| .padding(.top, 15) | ||
| .opacity(isAnimating ? 1 : 0) | ||
| .offset(y: isAnimating ? -20 : 20) | ||
| .onAppear { | ||
| withAnimation(.easeOut(duration: 0.3)) { | ||
| isAnimating.toggle() | ||
| } | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| # Habit model {% #habit-model %} | ||
|
|
||
| Add the following code to HabitModel.swift inside `Shared/Models` folder. | ||
|
|
||
| ```swift | ||
| import Appwrite | ||
| import Foundation | ||
|
|
||
|
|
||
|
|
||
| struct HabitModel: Codable, Identifiable { | ||
| let id: String | ||
| let userId: String | ||
| let title: String | ||
| let description: String? | ||
| let goals: Int? | ||
| let goalCompleted: Int? | ||
| let icon: String | ||
| let startDate: String? | ||
| let endDate: String? | ||
| } | ||
| ``` | ||

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give me originals of all your screenshots taking in a 1400x900 view port using DPR:3.0 on your browser's developer tools.
We will have our design team edit the screenshots to fit our style.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mujhtech please provide original images for all screenshots.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those are the screenshots @gewenyu99