Place iOS - r/place canvas clone
Tags: appwritehack, swift
Published: 2022-05-07 03:50:58

### Overview of My Submission
Place iOS is r/place canvas clone made with Appwrite and **pure SwiftUI**.
I decide create this project for Appwrite Hackathon to test me in SwiftUI and use majority appwrite Apis. 😃
I'm a super fan of _pixel art_ and this project fit perfect whit it. 🟩🟨🟪

The rules are simple: You can place a pixel every 30 seconds and the canvas will be updated using **realtime**. 

Manage accounts, database, storage, sessions, realtime, functions and more were super simple with appwrite. 😎

#### My approach for create the canvas in SwiftUI

```swift
class CanvasViewModel: ObservableObject {
    
    @Published var documents: [Doc] = [Doc]()
    
    // MARK: - Properties
    let canvasWidth: Int = 256
    let canvasHeight: Int = 256
    
    /// Pixel per pixels 👀
    /// if canvasPixelFactor is high every pixels looks better (no antialiasing) but the performance in older devices will be compromised... Int(UIScreen.main.scale) + (3-12) works good
    let canvasPixelFactor: Int = Int(UIScreen.main.scale) + 3
    
    /// Computed canvas size
    var canvasWidthComputed: CGFloat {
        return CGFloat(canvasWidth * canvasPixelFactor)
    }
    
    var canvasHeightComputed: CGFloat {
        return CGFloat(canvasWidth * canvasPixelFactor)
    }
    .
    .
    .
}
```

[Fetch pixels](https://github.com/chuiizeet/place-ios/blob/cae4d1119154c5d402a69f4e85f64af0bd55a7bb/reddit-place/ViewModel/CanvasViewModel.swift#L53) from storage and database, then create image

```swift
private func createImage(completionHandler: @escaping (UIImage?, String?) -> Void){
        
        guard !documents.isEmpty else { fatalError() }
        
        guard canvasWidth > 0 && canvasHeight > 0 else { fatalError() }

        
        DispatchQueue.global(qos: .utility).async {
            let width = self.canvasWidth * self.canvasPixelFactor
            let height = self.canvasHeight * self.canvasPixelFactor
            let rgbColorSpace =     CGColorSpaceCreateDeviceRGB()
            let bitmapInfo =        CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedLast.rawValue) // premultipliedLast or .none
            let bitsPerComponent =  8
            let bitsPerPixel =      32
                        
            var pixels = [Pixel]()
            
            if self.pixelsArray.isEmpty {
                
                for _ in 0..<width {
                    for _ in 0..<height {
                        pixels.append(Pixel(r: 255, g: 255, b: 255, a: 255))
                    }
                }
                
                // Colorize
                for doc in self.documents {
                    let x = doc.x + 1
                    let y = doc.y + 1
                    
                    let startXPx = x > 0 ? Int((x-1) * self.canvasPixelFactor) : 1
                    let endXPx = Int(x * self.canvasPixelFactor)
                    
                    let startYPx = y > 0 ? Int((y-1) * self.canvasPixelFactor) : 1
                    let endYPx = Int(y * self.canvasPixelFactor)
                    
                    for i in startXPx..<endXPx{
                        for j in startYPx..<endYPx{
                            let offset: Int = (Int(self.canvasWidthComputed) * j) + i
                            pixels[offset] = Pixel(hexString: doc.hex)
                        }
                    }
                }
                
                .
                .
                .
                .
    }
```

#### Bot

Also taking advantage of appwrite SDK's, i made a [Bot](https://github.com/chuiizeet/place-ios/tree/master/bot) in python for create pixel art in the canvas.🤖

The bot converts _Sprites_ (or any image) in a valid image for draw in the canvas, the bot uses _secret_ enviroment variable such we provided previously in [colorPixel](https://github.com/chuiizeet/place-ios/tree/master/appwrite/colorPixel) function, thus, will skip the delay. 😎


![Bot demo](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/w4sxfhc80brl48cz0jwj.png)
 
#### Dependencies

[Appwrite Apple SDK](https://github.com/appwrite/sdk-for-apple): For manage, accounts, sessions, database, storage, functions, realtime... ⚡


[Drops](https://github.com/omaralbeik/Drops): For create notifications.
[Popovers](https://github.com/aheze/Popovers): For create pop  menus.
[SwiftyBeaver](https://github.com/SwiftyBeaver/SwiftyBeaver): For logging.

### Submission Category: 

Mobile Moguls

### Link to Code

[Note]: # (Our markdown editor supports pretty embeds. Try this syntax: `{% embed link_to_your_repo %}` to share a GitHub repository)
{% embed https://github.com/chuiizeet/place-ios %}

### Additional Resources / Info

#### Onboarding
_Func fact:_ In the last page, the third guy has an appwrite t-shirt.

![Onboarding](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jleoqrp8crwtc2kcxu34.png)



#### Canvas View

For navigate in the canvas view you can _zoom in_, _zoom out_ , _drag_ and _reset_.

_Func fact:_ The allowed colors is my favorite palette when i did pixel art: [PICO-8](https://lospec.com/palette-list/pico-8) (only i was made a little change in white color to _#FFFFFF_)

![Canvas](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/j9rx9ou19938kh9cfvkq.png)



#### Authentication

Only members are allowed to place pixels.

![Auth](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/095qmy6fu3xu3zqrt4b4.png)

#### Settings

For log out, change zoom factor and information.

_Fun fact:_ Tha app support Dark Mode. 😝🌙

![Settings](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/oi5t0zfy8i0xazshyib8.png)


#### Notifications

I use drops for put cool notifications. 🔔

![Notifications](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1mkggyu77506i0reihl7.png)

#### Videos

- App demostration
{% embed https://youtu.be/4agoxM4Np-s %}

- Bot demostration
{% embed https://youtu.be/Mi7Gq45gks0 %}

### Conclusion
This project was super fun to do. 
Thanks for read and special thanks to Appwrite community for solves my questions ever. 🙏