Categories
Uncategorized

Xcode 11 Tutorial For Beginners

In this series, you will learn everything you need to know to get started with Xcode, quickly & easily.

The whole article was updated for Xcode 11, including the new main feature, SwiftUI.

What you will learn in this part of the series:

  • What Xcode is and what you need it for
  • How Xcode is structured and what you need for running it
  • Tips for an efficient usage of Xcode

What is Xcode used for and what do I need for running it?

Xcode is Apple’s in-house IDE (integrated development environment). When you develop iOS apps, it is the software you work with most often. Therefore, it is very important to have a profound knowledge of Xcode and to master the basics from scratch.

In Xcode you set up the user interface of your app, organize and write the code that makes your app run. Xcode also offers you the possibility to run and test your app on a virtual simulator on your Mac (and of course on a real iOS device).

Fortunately, Xcode is available completely free of charge in the Mac App Store. On Windows PC’s Xcode is not available. Although it may be possible to run OSX on a virtual machine (“Hackintosh”), i strongly recommend you to buy a Mac if you don’t have one yet. This is more than useful in the long run if you want to develop iOS apps.

Xcode is Apple’s in-house IDE for the development of iOS apps. It is free, but only available for Mac.

Creating a Xcode Project and choosing the User Interface

To get in touch with the IDE, just open Xcode and click on “Create a new Xcode Project” (btw: “Playgrounds”, the option below creating a new project, is an option to test new concepts and ideas quickly and easily). Next, click on “Single View App” and then on “Next” and give your app any name you want.

At this point, you can choose which User Interface mode you want to work with. You can either use SwiftUI or Storyboards.

  • Storyboards: Until now, the most common way to build iOS apps was to use storyboards. Simplified, you created the UI by dragging and dropping, arranging and constraining elements and by connecting them to your code.
By working with storyboards you create the UI by dragging and dropping, arranging and constraining elements and by connecting them to your code.
  • SwiftUI: SwiftUI is THE new feature that got shipped with Xcode 11. It’s a completely new way to build iOS apps and follows a declarative approach, meaning that with SwiftUI you describe how your interface should look like and what it should do. To get started with SwiftUI, check out this quick introduction to this new framework and make sure you read our free tutorials!
SwiftUI follows a declarative syntax approach, meaning that we describe in code how our interface should look

Should I choose Storyboards or SwiftUI?

When you are new into iOS development, you are probably struggling whether you should work with SwiftUI or stay with Storyboards. To give you some advice, we wrote a an article for, that might be helpful.

Unterstanding the Interface 💡

To understand the basic layout of Xcode, create a new Single View app by using SwiftUI, as we described above. Don’t worry understanding the basics of Xcode is not depended on the User Interface mode you choose, so even if you want to work with Storyboards, starting with this example is fine.

What you see now may feels a bit overwhelming. Don’t worry, this feeling is perfectly fine and will pass when you understand the basics of how Xcode is constructed.

Fortunately, the interface is designed pretty straight forward. The interface you see now basically consists of 5 sections:

The five main sections: Toolbar(1), Navigator Area(2), Editor Area(3), Utility Area(4), Debug Area(5)

The Toolbar 🛠

Xcodes toolbar
The right three buttons of the toolbar allows you to show/hide the different areas

The right three buttons of the toolbar allows you to show/hide the different areas

The toolbar allows you to access the basic Xcode settings (don’t mix this up with your app projects settings) and perform several operations. On the left side of the toolbar you can select the device on which you want to run your app, for instance on any simulator. The field in the middle tells you when Xcode is working on something. The area on the right is responsible for the Xcode view modes, more on that later.

The Navigator Area 🔍

The Navigator Area helps you finding your way around your project and organize your code and resources. By default the “Project Navigator” is selected, probably the most important mode of the navigator. Here are the different parts of your app’s code listed. The more complex your app becomes, the more files your project will contain. To keep track, you can create „groups“ (Xcodes name for folders) and move the files as you like. Where you are placing files within your project usually has no effect on the logic of your code or the behavior of the app.

The Navigation Area helps you to organize your app project and to find your way around it

When you click on a file, it will open in the Editor Area where you can, you guessed it, edit it. If not already done, click on “ContentView.swift” to display this file in the Editor Area. Swift files are the heart of every iOS App. In these you write the code that makes your app run.

The Editor Area ✍️

Here you write the code and compose the interface of your app. The appearance of the Editor Area depends on which file type is opened, especially and on whether you are working with SwiftUI or storyboards.

The Utility Area 🧰

Similar to the Editor Area, the appearance of the Utility Area depends on what file type you have just selected. Here you can access, for instance meta data, references, etc. of files or/and their components. This area is especially important when editing storyboards.

Especially the Utilty Area often confuses beginners, because the use of this area depends on the particular situation. But the more you work with Xcode the more you get a feeling for it. You’ll see, it’s much easier than it looks at first sight, I promise!

The Debug Area 👷‍♀️

When you run your app, you will find all relevant information about errors etc. that Xcode provides you. This area becomes very important when it comes to finding and fixing errors and bugs of your app.

Often the output is very long, so you can use the filter to easily find certain output.

Basic Shortcuts

If you begin working with Xcode more often, you will notice that different things like frequently showing and hiding different areas can inferiore your workflow. In order to save time you should remember and use at least the most important shortcuts. Soon you will be memorizing them and save a lot of time. Here are some first, important ones to start with:

  • ⌘+0: Show/Hide Navigator Area
  • ⌘+⌥+0: Show/Hide Utility Area
  • ⌘+SHIFT+Y: Show/Hide Debug Area

Shortcuts are enormously helpful to optimize your workflow and save time

Conclusion 🎊

Congratulations! You now know the basics of Xcode.

Don’t feel insecure if you are a little overwhelmed. You’ll see, the more you deal with it, the more comprehensible everything becomes.

“The secret of getting ahead is getting started.”

Mark Twain

Do not hesitate to ask in the comments if anything is unclear. We will reply as soon as possible (:

Categories
Uncategorized

Core Data and SwiftUI – Saving, retrieving, updating and deleting persistent data

Hello and welcome to a new tutorial! Today we will learn how to use the Core Data framework to store and manage persistent data. The integration of Core Data into SwiftUI projects is surprisingly easy. By creating a useful app for a small pizza restaurant, we’re going to talk through all basic CRUD operations (Create, Read, Update and Delete Data) used in Core Data.

In this tutorial, we will explore:

  • How Core Data and SwiftUI work together
  • Creating and updating Core Data objects
  • How to update views when stored data gets updated
  • Using SwiftUI property wrappers for fetching Core Data objects

We will create a simple app for a pizza restaurant that waiters can use to take and manage orders.

The finished app will look like this:

Setting up Core Data in SwiftUI 🛠

To get started, open Xcode 11 and create a new Single view app and choose SwiftUI as the User Interface mode. Make sure that you check the “Use Core Data” box. This will automatically set up the initial Core Data implementation for our app!

When your project is created, you see that Xcode automatically generated a .xcdatamodeld file for us. Inside this file, we can visually design our model for managing persistent data as you’ll see in a moment.

Xcode also generated some interesting lines of code for us. Click on the SceneDelegate.swift file and take a look at the scene function inside the SceneDelegate class. The scene function handles booting up our initial view, which is the ContentView by default. Because we check marked “Use Core Data” when creating our SwiftUI project, Xcode applied some modifications to this function.

Take a look at this line of code within the scene function:

let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext

This function retrieves the managed object context for us. A managed object context is like an in-memory scratchpad. It’s the place where objects are created, fetched, updated, deleted, and saved back to the persistent store of the device where the app runs on.

Let’s jump to the next line of code:

let contentView = ContentView().environment(\.managedObjectContext, context)

This line initializes the ContentView which is then set as the root view in the lines below. So far, so good. But what does this environment thing do?

Before our ContentView gets launched as the root view, it feeds the environment’s managedObjectContext key with our context we just created. The environment is the place where system-wide settings are stored in, e.g Calendar, Locale, ColorScheme and now also our created context (our app’s scratchpad). Each of this setting has its own key, in our case, it’s the .managedObjectContext key.

Now every view in our app can use the “context scratchpad” to retrieve, update and store objects from. We just have to use the managedObjectContext environment key for accessing it, as you will see later on.

Don’t worry if you are not familiar with this, the only thing you need to remember is that we can use the managedObjectContext now for fetching and saving our orders and you’ll see how easy it is in a moment.

Defining our data model 🛠

As said, in the project navigator, you can see a .xcdatamodeld file. In this file, we create and manage the entities of our CoreData data model for our SwiftUI app. In case you are not familiar with core data entities: You can think of an entity as a representation of a class, and an attribute, as a property of that class. The only entity we need for our app is for holding the different orders. Create the entity by clicking on the large plus sign at the bottom and then double-click on the created Entity and rename it to Order.

We need to know the following information about each order: The type of pizza the customer ordered, how many slices he wants to eat and the number of the table the customer is sitting at. Each order should also have an unique id and a status attribute for keeping track of whether the order is already completed. For the id we use the UUID type (this automatically creates a unique id for us), for the numberOfSclices we select Integer16 and for the rest String.

But let’s hold on a second and reconsider choosing String as the status attribute’s type. The order’s status should only be “Pending”, “Preparing” and “Completed”. Wouldn’t be using an enum the better choice for this? Unfortunately, we can’t create and use an enum inside the .xcdatamodeld file itself. But as said, by creating and designing the Order entity, Core Data created a corresponding class under the hood. We can access and modify this class by clicking on the Order entity, going to the Xcode toolbar and selecting Editor-“Create NSObjectManagedSubclass”.

After creating the subclass, Xcode generated two files for us. The Order+CoreDataClass.swift file holds the class itself and the Order+CoreDataProperties.swift contains its properties inside an extension.

After we created our data model’s subclass, we need to tell Xcode that the data model is no longer defined by the visual builder in our .xcdatamodeld file only, but manually defined by the corresponding subclass we just created. To do this, open the .xcdatamodeld file, click on the Order entity and open the data model inspector. Then choose “Manual/None” as the codegen mode.


At this point, we can remove the question marks from the String-type properties since we don’t want them to be optionals. We can also adopt the Identifiable protocol (this will make it easier for us the use Order instances inside the ContentView’s List) and since we have an id property we already conform to this.

extension Order: Identifiable {

    //...
    @NSManaged public var pizzaType: String
    @NSManaged public var status: String
    @NSManaged public var tableNumber: String

}

Below the OrderExtension we can declare our Status enum with the three different cases.

enum Status: String {
    case pending = "Pending"
    case preparing = "Preparing"
    case completed = "Completed"
}

If we now try to use the Status enum as the status’ data type, we will get an error.

You see that @NSManagedObject properties can’t be used with enums directly. But how else can we save the status of an order in Core Data? Here’s a workaround: We go ahead with creating an NSManaged status property but not of our Status type. Instead it should be a String. Next, we add another, regular variable called orderStatus. Because it’s not a NSManaged property, it can be of the type Status. We assign a setter and getter to our orderStatus, so that when this property is set, it will also set the NSManaged property accordingly and by using a getter, we try to convert the status string to an Status case when retrieving it.

extension Order: Identifiable {

    //...
    @NSManaged public var status: String
    @NSManaged public var tableNumber: String
    
    var orderStatus: Status {
        set {status = newValue.rawValue}
        get {Status(rawValue: status) ?? .pending}
    }

}

Awesome, we’re now finished with creating the Core Data model for our SwiftUI app!

Composing our UI 🎨

Important: Before moving on with composing our ContentView, we need to make sure that its preview can access the managedObjectContext as well. Otherwise the SwiftUI preview will fail when why try to implement CoreData functionality inside it. To do this, we repeat the steps as we did in the SceneDelegate.swift file in the beginning:

struct ContentView_Previews: PreviewProvider {
    
    static var previews: some View {
        
        let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
        
        return ContentView().environment(\.managedObjectContext, context)
    }
}

Now our ContentView preview is able to manage CoreData requests!


The ContentView of our pizza restaurant app should contain a list of all orders already taken which the corresponding waiter can manage. Since we can’t store any data yet, we are using only a test list for now.

struct ContentView: View {
    var body: some View {
        List {
            Text("Sample order")
        }
    }
}

We also want to add a navigation bar to our app. To do so, we wrap our List into a NavigationView and use the .navigationBarTitle modifier.

NavigationView {
            List {
                Text("Sample order")
            }
                .navigationBarTitle("My Orders")
        }

The navigation bar should contain a button the waiter can use to add a new order.

List {
     Text("Sample order")
            }
                .navigationBarTitle("My Orders")
                .navigationBarItems(trailing: Button(action: {
     print("Open order sheet")
                }, label: {
    Image(systemName: "plus.circle")
                        .resizable()
                        .frame(width: 32, height: 32, alignment: .center)
                }))

The preview canvas should look like this so far:

When we tap on the button we want to open a second view. For this, we create a new SwiftUI file and name it OrderSheet. We want to display the OrderSheet as a modal view. To do this, we add a State to our ContentView to control when the OrderSheet should be displayed.

struct ContentView: View {
@State var showOrderSheet = false
    var body: some View {
       //...
    }
}

To display the OrderSheet as a modal view, we use the .sheet modifier.

List {
    Text("Sample order")
            }
//...
                .sheet(isPresented: $showOrderSheet) {
OrderSheet()
                }

Whenever the showOrderSheet state is true the OrderSheet overlays the ContentView. Now we can toggle the showOrderSheet State from our navigation bar button.

.navigationBarItems(trailing: Button(action: {
                    self.showOrderSheet = true
                }, label: {
                    Image(systemName: "plus.circle")
                        .resizable()
                        .frame(width: 32, height: 32, alignment: .center)
                }))


For our OrderSheet view’s body, we’ll be using the Form view in SwiftUI and a picker with the different pizza options. The number of slices that the customer wishes to order will be represented by a stepper. Finally, we use a text field where the user can write the table number for the order.

We want to save the data after the user taps on the “Add Order” button.

For the OrderSheet’s UI, you can use copy & paste the following code:

struct OrderSheet: View {
       
    let pizzaTypes = ["Pizza Margherita", "Greek Pizza", "Pizza Supreme", "Pizza California", "New York Pizza"]
    
    @State var selectedPizzaIndex = 1
    @State var numberOfSlices = 1
    @State var tableNumber = ""
    
    var body: some View {
        NavigationView {
            Form {
                Section(header: Text("Pizza Details")) {
                    Picker(selection: $selectedPizzaIndex, label: Text("Pizza Type")) {
                        ForEach(0 ..< pizzaTypes.count) {
                                Text(self.pizzaTypes[$0]).tag($0)
                        }
                    }
                    
                    Stepper("\(numberOfSlices) Slices", value: $numberOfSlices, in: 1...12)
                }
                
                Section(header: Text("Table")) {
                    TextField("Table Number", text: $tableNumber)
                        .keyboardType(.numberPad)
                    
                }
                
                Button(action: {
                    print("Save the order!")
                }) {
                    Text("Add Order")
                }.navigationBarTitle("Add Order")
                
            }
        }
    }
}

The OrderSheet’s preview should now look like this:

Saving orders 🆕

Great, we’re done with our app’s UI, but nothing gets saved and persisted yet. To change this we need to access to the managed object context first to persistently save a created order. Since, as we saw in the beginning, the managed object context is injected in our environment, we can simply access it by using the @Environment property wrapper inside our OrderSheet above its States.

@Environment(\.managedObjectContext) var managedObjectContext

Bug-Alert 🐞: Unfortunately there’s a bug in the current version of SwiftUI. Since we injected into the SceneDelegate’s scene function, the managed object context should be accessible globally by all views by using the corresponding environment property. However, doing this inside popover views like our OrderSheet won’t work properly. What we have to do is to pass the managedObjectContext that gets initialised inside the scene function downwards to our OrderSheet. Thus, we have to use the @Environment property inside our ContentView, too …

@Environment(\.managedObjectContext) var managedObjectContext
    
@State var showOrderSheet = false
//…

… and pass it to the OrderSheet inside the .sheet modifier:

.sheet(isPresented: $showOrderSheet) {
    OrderSheet().environment(\.managedObjectContext, self.managedObjectContext)
                }

Now that our OrderSheet has access to the device’s “scratchpad” we are ready to create an Order instance inside our “Add order Button”. But first, we want to make sure, that the tableNumber String is not empty by using a guard statement.

Button(action: {
guard self.tableNumber != "" else {return}
let newOrder = Order(context: self.managedObjectContext)
                    newOrder.pizzaType = self.pizzaTypes[self.selectedPizzaIndex]
                    newOrder.orderStatus = .pending
                    newOrder.tableNumber = self.tableNumber
                    newOrder.numberOfSlices = Int16(self.numberOfSlices)
                    newOrder.id = UUID()
                }) {
Text("Add Order")
                }

Then we’re trying to save the created order. If that fails, we print the corresponding error.

//...
newOrder.id = UUID()
do {
  try self.managedObjectContext.save()
  print("Order saved.")
 } catch {
  print(error.localizedDescription)
  }

After the new order got saved, we want to close the OrderSheet modal view. We can do this by inserting the following environment property into our OrderSheet.

@Environment (\.presentationMode) var presentationMode

By referring to this property we can manually close the modal view:

do {
try self.managedObjectContext.save()
print("Order saved.")
self.presentationMode.wrappedValue.dismiss()
} catch {
print(error.localizedDescription)
}

Okay, let’s run our app to see if that works. Note that the preview canvas isn’t able to simulate Core Data’s functionality. Therefore, we need to run the app in the regular simulator. Click on the navigation bar button and fill out the OrderSheet form. Then click on “Add Order”. We saved the created order and dismisses the OrderSheet. However, our ContentView’s List is still displaying its sample row.

Fetching and displaying stored orders 📖

To change this, our ContentView needs to read out the saved orders. Achieving this functionality is quite simple by using a @FetchRequest. Below the ContentView’s environment property insert the following lines of code:

@FetchRequest(entity: Order.entity(),
                  sortDescriptors: [],
                  predicate: NSPredicate(format: "status != %@", Status.completed.rawValue))
    
    var orders: FetchedResults<Order>

The @FetchRequest permanently reads out the persistent storage for fetching stored orders from it. With the predicate argument, we filter out all orders already completed since we don’t want them to display in our ContentView’s List. The @FetchRequest then passes the retrieved orders to the orders property. Whenever we save a new order the @FetchRequest will notice and add it to the orders data set. Similar to the State functionality, this causes the ContentView to rebuild itself.

Now we’re ready to display the fetched data inside our List, like this:

List {
                ForEach(orders) { order in
                    HStack {
                        VStack(alignment: .leading) {
                            Text("\(order.pizzaType) - \(order.numberOfSlices) slices")
                                .font(.headline)
                            Text("Table \(order.tableNumber)")
                                .font(.subheadline)
                        }
                        Spacer()
                        Button(action: {print("Update order")}) {
                            Text(order.orderStatus == .pending ? "Prepare" : "Complete")
                                .foregroundColor(.blue)
                        }
                    }
                }
            }

Hint: The reason why we use a ForEach loop inside the List instead of inserting the orders data set in the List itself, will become clear when it comes to deleting orders.

When we run our app again, we see that our @FetchRequest successfully retrieves the just saved order from the persistent storage.

Updating orders 🔄

The button on each row’s right side can be used to update the status of the particular order. When we add a new order, its status is pending. Therefore the button reads “Prepare”. When the user taps on the button we want to update the status to preparing and the button should read “Complete”. When the user taps again, we want the order’s status to be completed, which causes the @FetchRequest to filter the order out.

To implement this functionality, we add the following function below our ContentView’s body.

func updateOrder(order: Order) {
let newStatus = order.orderStatus == .pending ? Status.preparing : .completed
        managedObjectContext.performAndWait {
            order.orderStatus = newStatus
try? managedObjectContext.save()
        }
    }

We can call the updateOrder function from our row’s button with passing the particular order instance:

Button(action: {self.updateOrder(order: order)}) {
Text(order.orderStatus == .pending ? "Prepare" : "Complete")
                                .foregroundColor(.blue)
                        }

Now we can run the app and tap on the “Prepare” button to mark the currently pending order as prepared. If we then click on “Complete”, the Order will be filtered out and eventually removed from our List.

Deleting orders from the persistent storage 🗑

Deleting stored data is almost as simple as updating it. All we have do is to delete the particular object from the managed object context, and then, since the @FetchRequest will automatically detect that the object was deleted, it will update our ContentView accordingly and remove the row from the table with a nice default animation.

To let the user delete rows we add the .onDelete modifier to the ForEach row. We can’t apply this modifier to Lists, this is why we use a ForEach loop inside the List.

ForEach(orders) { order in
                    //...
                }
                    .onDelete { indexSet in
                        for index in indexSet {
                            self.managedObjectContext.delete(self.orders[index])
                        }
                    }

The .onDelete modifier detects the row(s) the user wants to delete by swiping and uses there index/indices to remove the corresponding order entries from the managedObjectContext.

If we run the application now, we can see that we can easily delete the order by swiping a row.


Conclusion 🎊

That’s it, we’re finished with our small pizza restaurant app! We learned how we can use Core Data in SwiftUI to store data persistently. We talked through all basic CRUD operations: Creating, reading, updating and deleting data. We also understood what a managedObjectContext is and how we can fetch stored data by using SwiftUI’s @FetchRequest.

We’ve uploaded the whole source code of this app to GitHub.

mIf you liked this tutorial, feel free to check out our Mastering SwiftUI eBook. In this book, we also created a To-Do app by using the mentioned Core Data functionalities!

I hope you enjoyed this tutorial! If you want to learn more about SwiftUI, check out our other tutorials! Also make sure you follow us on Instagram and subscribe to our newsletter to not miss any updates, tutorials and tips about SwiftUI and more!