Swift UI social network

Ivan Garcia
6 min readAug 9, 2020

--

During the last weekend, I decided to start playing with Swift UI and I found a great post by Esra Kadah for building an Instagram like travel app, which I found really awesome, and for the sake of this post I’m going to reuse the same design but with a slightly difference, I’m going to use swift UI.

While working with swift UI I found great advantages like developing and debugging really fast, it’s really easy to learn(even easier if you have an iOS background), having the same standard for building UI elements and the live preview is something from another world. However, we need to be careful while working on the contentViews because while I was working with the first List, after a couple of hours, the number of lines was more than 200. I finished refactoring my view with reusable views and separating it in different and smaller UI elements.

GitHub source code

The source code can be found: here

To get started, the Sketch designs I used include the 4 screens above (popular posts screen, profile screen, post detail screen, and new posts screen). You can download the Sketch file here.

The Gif below shows the whole interaction with the Swift UI app I built which I’m going to describe deeply:

Project Structure

The project structure it’s really simple, we have an entities directory including the one and only Entity we need which is the Post.

In addition, you can also find a UI directory that contains 6 directories embedded (Button, Detail, SegmentedControl, PostList, NewPost, Profile, and TabBar).

Popular Posts Screen

This screen includes at the top a segmented view for selecting 3 options; popular, new and follow, as well as a List view with the image of the post, the user name, and number of likes.

struct PostList : View {var posts: [Post] = mockedPosts@State var selected = 0var body: some View {List {    ForEach(posts) { post in        ZStack {            PostCell(post: post)                NavigationLink(destination: PostDetail(name:       post.name, userName: post.userName, imageName: post.imageName, likes: post.likes, description: post.description, views: post.views, downloads: post.downloads)) {            EmptyView()               }.buttonStyle(PlainButtonStyle())        }    }}.onAppear {    UITableView.appearance().separatorStyle = .none    }}}

The code above belongs to PostList, as you can see, it is just a List which is iterating through the mocked posts and for each of the posts a PostCell is created, the buttonStyle(PlainButtonStyle() is used to remove the List disclosure indicator added automatically. The NavigationLink handles the navigation once the List PostCell is tapped using as a destination the PostDetail.

The result for for the PostList is the following:

Post Detail Screen

The post detail is one of the most simple screens, it just includes 7 required parameters(name, userName, imageName, likes, etc) which are sent from the selection of the Post. Using the Stacks provided by Swift UI, i built this simple screen, using a VStack which is a vertical stack for distributing all the UI elements needed and a Hstack for placing horizontally the name of the owner of the post and the Heart button for giving it a like. The variable named mode was created to handle the back navigation once the back button is pressed. The navigation items; Back button and Menu, where added using the navigationBarItems.

struct PostDetail : View {var name: Stringvar userName: Stringvar imageName: Stringvar likes: Intvar description: Stringvar views: Intvar downloads: Int@Environment(\.presentationMode) var mode: Binding<PresentationMode>var body: some View {VStack(spacing: 15.0) {    Text(userName)        .font(.custom("HelveticaNeue-Medium", size: 16))        .foregroundColor(.black)    Image(imageName)        .resizable()        .scaledToFit()    HStack {        Text(name)            .font(.custom("HelveticaNeue-Medium", size: 16))            .foregroundColor(.black)        Spacer()        Button(action: {        }) {            VStack {            CustomButton()            }        }    }    VStack(spacing: 15.0) {        Text(String("\(likes) Likes - \(views) Views - \(downloads)         Downloads"))
.font(.footnote).frame(maxWidth: .infinity, alignment: .leading)
.font(.custom("HelveticaNeue-Light", size: 14)) .foregroundColor(Color("LightGrayColor")) Text(description) .font(.custom("HelveticaNeue-Light", size: 14)) .multilineTextAlignment(.leading) .foregroundColor(Color(.black)) } Spacer()} .padding() .navigationBarBackButtonHidden(true) .navigationBarItems(leading: Button(action : { self.mode.wrappedValue.dismiss() }){ Image(systemName: "arrow.left").foregroundColor(.gray) }, trailing: Button(action : { }){ Image("Menu").foregroundColor(.gray) }) }}

here is the result:

New Posts Screen

New posts screen it’s also a simple List including a nice UI style.

As you can see in the code below, the NewPostList it’s just a simple List iterating through our mocked posts. For following the same design used in the Sketch file, we don’t need any separator to be displayed, so we use the separatorStyle = .none.

struct NewPostList : View {var posts: [Post] = mockedPostsvar body: some View {    List {        ForEach(posts) { post in        NewPostCell(post: post)    }}.onAppear {    UITableView.appearance().separatorStyle = .none}}

The NewPostCell

This UI element was created using a VStack that embeds all the images, text, and buttons required.

There is an Hstack for the profile image, a Vstack including the name of the user who owns the post and the time, and finally an arrow.

Moreover, I added the image of the post to the Vstack, and new a Hstack including 3 UIelements (heart, comment, and direct message icons). I also added a Divider() that provides the gray line and a new Hstack for the Name of the User, the heart with the number of likes.

struct NewPostCell: View {let post: Postvar body: some View {    VStack(spacing: 15.0) {        HStack {            Image(post.profileImageName)            VStack {                Text(post.name)                    .font(.custom("HelveticaNeue-Bold", size: 14))                    .frame(maxWidth: .infinity, alignment: .leading)                Text(String("\(post.time)min ago"))                    .font(.custom("HelveticaNeue-Regular", size: 12))                    .foregroundColor(Color("LightGrayColor"))                    .frame(maxWidth: .infinity, alignment: .leading)            }           Spacer()            Image("ic_down")        }.frame(maxWidth: .infinity, alignment: .leading)        Image(post.imageName)            .resizable()            .scaledToFit()        HStack(spacing: 22.0) {            Image("HeartBig")            Image("Actions")            Image("destinationicon")        }.frame(maxWidth: .infinity, alignment: .leading)        Divider()        VStack (spacing: 10.0) {            HStack {                Text(post.userName)                    .font(.custom("HelveticaNeue-Bold", size: 14))                Spacer()                CustomButton()                Text(String("\(post.likes) Likes"))                   .font(.custom("HelveticaNeue-Regular", size: 14))                   .foregroundColor(Color("DarkGrayColor"))            }.frame(maxWidth: .infinity, alignment: .leading)            Text(post.description)                .font(.custom("HelveticaNeue-Regular", size: 14))                .foregroundColor(Color("LightGrayColor"))            HStack {                Image("ic_downLight")                    Text(String("See \(post.comments) comments"))                        .font(.custom("HelveticaNeue-Regular", size: 12))                        .foregroundColor(Color("LightGrayColor"))            }.frame(maxWidth: .infinity, alignment: .leading)        }.frame(maxWidth: .infinity, alignment: .leading)      }.padding()   }}

Result:

Profile Screen

The most important part from the profile screen it’s the CollectionView, this collection includes the pictures posted by the user arranged into a 2 x n collection. As you can see it is really simple, I’m iterating the mocked information named data and displaying the Images two by two.

struct CollectionView: View {    let data: DataModel    var body: some View {        VStack {            HStack {                ForEach(0..<2) { items in                    Spacer()                    Image(self.data.imageName)                    Spacer()                }            }        }    }}

result:

Where to go from here?

For my next stories i plan to emphasize on 2 cool components that i created for this social network project named SegmentedControlView and TabBarView

--

--