Zara replica - Part 1
Tags: Swiftui, iOS, iOS App Development, Mobile Apps, Mobile App Development

This is part one of many in which we are going to create an e-commerce app inspired by the ZARA mobile app…

At WWDC 20, apple said that it is now possible to build an entire app in swift UI… Although this statement is valid for the majority of apps, there are still features and functionalities that need the power of UIKit to be implemented correctly. In my humble opinion, UIKit is still relevant to this day, and it will not disappear anytime soon. So if you want to be a flexible iOS developer, you will need to know both frameworks.

In this series, we will use a bit of UIKit to implement a feature that would be hard to implement with only swift UI.

To follow along, you need:

Xcode 12 or greater

MacOS Catalina or greater

Swift 5.0 or greater

iOS 14 or greater

Open Xcode

Create a new Xcode project

Select App and click next

Name your app and make sure the user interface is Swift UI

Last, click Finish.

Or you can get the starter project at https://liquidcoder.com/course/zara-replica , you will need a free account to get it.

Let’s first create a bunch of folders and files that we will use throughout this series, and we will keep adding on them as we go.

To follow along, you need download the starter project source code linked above, and when you finish, try to compare with the final project.

Create the following folders in your root folder: Views, Utils, Extensions, Data, Screens, UIKit

Extensions

Add the following in the Extensions folder:

Color+.swift

import SwiftUI



extension Color {

static var background = Color("background")

static var text = Color("text")

}

The above code will work if you’ve downloaded the initial project code, otherwise you will need to add the 2 colors in the Assets.xcassets folder.

View+.swift

import SwiftUI



extension View {

func thinFont() -> some View {

self.font(.system(size: 20, weight: Font.Weight.thin, design: Font.Design.default))

}



func lightFont() -> some View {

self.font(.system(size: 12, weight: Font.Weight.light, design: Font.Design.default))

}



func boldFont() -> some View {

self.font(.system(size: 15, weight: Font.Weight.bold, design: Font.Design.default))

}



func textColor() -> some View {

self.foregroundColor(.text)

}



func reverseTextColor() -> some View {

self.foregroundColor(.background)

}



func bgColor() -> some View {

self.background(Color.background)

}



func reverseBgColor() -> some View {

self.background(Color.text)

}

}

In case you are not familiar with extensions in swift, I will link some resources to help you out. In short, this will allow us to call the above functions as modifiers, because they are modifiers. We might add more files into this folder as we go.

Buttons

Now, in the Views folder, add another folder named Buttons. Next, add the following in that folder. The following are different styles of buttons we will use throughout this series.

CloseButton.swift

import SwiftUI



struct CloseButton: View {



var onDismiss = {}



var body: some View {

Button(action: onDismiss, label: {

Image(systemName: "xmark")

.thinFont()

.textColor()

})

}

}



struct CloseButton_Previews: PreviewProvider {

static var previews: some View {

CloseButton()

}

}



Preview

BorderedButton.swift

struct BorderedButton: View {

var text: String

var action = {}



var body: some View {

Button(action: action, label: {

Text(text)

.boldFont()

.textColor()

.padding(.horizontal, 30)

.padding(.vertical, 10)

.border(Color.text, width: 1)

})

}

} struct BorderedButton_Previews: PreviewProvider {

static var previews: some View {

BorderedButton(text: "ADD")

}

}

Preview

Preview

IconButton.swift

struct IconButton: View {

var icon: String

var action = {}



var body: some View {

Button(action: action, label: {

Image(systemName: icon)

.lightFont()

.textColor()

})

}

} struct IconButton_Previews: PreviewProvider {

static var previews: some View {

IconButton(icon: "square.and.arrow.up")

}

}

Preview

Preview

FilledButton.swift

import SwiftUI struct FilledButton: View {

var text: String

var action = {}



var body: some View {

Button(action: action, label: {

Text(text)

.boldFont()

.reverseTextColor()

.padding(.horizontal, 30)

.padding(.vertical, 10)



})

}

} struct FilledButton_Previews: PreviewProvider {

static var previews: some View {

FilledButton(text: "LOG IN")

.reverseBgColor()

}

}

Preview

NakedButton.swift

import SwiftUI struct NakedButton: View {

var text: String

var alignment: Alignment = .center

var action = {}



var body: some View {

Button(action: action, label: {

Text(text)

.lightFont()

.textColor()

.padding(.vertical, 10)

.frame(maxWidth: .infinity, alignment: alignment)

})

}

} struct NakedButton_Previews: PreviewProvider {

static var previews: some View {

NakedButton(text: "Have you forgotten your password?")

}

}

Preview

LargeNakedButton.swift

import SwiftUI



struct LargeNakedButton: View {

var text: String

var action = {}



var body: some View {

Button(action: action, label: {

Text(text)

.font(.system(size: 30, weight: Font.Weight.black, design: Font.Design.default))

.foregroundColor(.text)

.frame(maxWidth: .infinity, alignment: .leading)

})

}

}



struct LargeNakedButton_Previews: PreviewProvider {

static var previews: some View {

LargeNakedButton(text: "MY INFORMATION")

}

}

Preview

In the Screens folder, add the following:

Login Screen

In the Screens folder, add the following:

LoginScreen.swift

struct LoginScreen: View {



var onDismiss = {}



@State private var email = ""

@State private var password = ""





var body: some View { // 1

VStack{

// 2

HStack {

CloseButton(action: onDismiss)

Spacer()

}



Spacer()



// 3

VStack(spacing: 20) {

VStack {

TextField("Email", text: self.$email)

Divider()

}



VStack {

SecureField("Password", text: self.$email)

Divider()

}



FilledButton(text: "LOG IN", action: onDismiss)

.frame(maxWidth: .infinity)

.reverseBgColor()



NakedButton(text: "Have you forgotten your password?")

}

Spacer()



// 4

VStack{

BorderedButton(text: "CHAT", action: {})

NakedButton(text: "Don't have an account? Register")

}

}.padding()

}

} struct LoginScreen_Previews: PreviewProvider {

static var previews: some View {

LoginScreen()

}

}

Explanation

I first a declare a closure that will be called when the onDismiss the close button or the login button are tapped. I also declare 2 state properties that will hold the email and password. I put everything inside a VStack because the UI will flow from top to bottom. We then create the horizontal invisible navbar that will only contain the CloseButton and a Spacer . The Spacer is used to push the CloseButton to the left edge of the screen. Next, We create the form inside a VStack again , and surround it with 2 Spacer to center it vertically. With swiftUI 2.0, there's no need to handle the keyboard as the form will get out of the way when one starts typing. Last, we create the footer which is a simple VStack