DEV Community

Cover image for WatchOS (Swift): Horoscope Application
Andres Lopez
Andres Lopez

Posted on

WatchOS (Swift): Horoscope Application

In this tutorial an application for WatchOS will be made, based on consulting a REST API of horoscopes.

Requirements

  • Xcode 10.2 or higher
  • Mac OS Mojave or higher
  • IPhone simulator
  • Apple Watch Simulator

Creation of the project

Open XCode and create a new WatchOS project.

xcode

The Include Notification Scene option is unchecked and the location where the project is saved is selected.

xcode

First screen

xcode

Drag a table element.

xcode

Two labels are added inside the table:

  • First label with the following alignment properties.

xcode

  • Second label with the following alignment properties.

xcode

The Row element of the table is assigned the identifier with the same name Row.

xcode

A Row class is created that inherits from NSObject and we link the corresponding outlets.


import WatchKit

class Row: NSObject {
    @IBOutlet weak var lblIcon: WKInterfaceLabel!

    @IBOutlet weak var lblZodiac: WKInterfaceLabel!
}

Enter fullscreen mode Exit fullscreen mode

The code of the Interface Controller is as follows:


import WatchKit
import Foundation


class InterfaceController: WKInterfaceController {

    @IBOutlet weak var table: WKInterfaceTable!


    let zodiac = [
        ("♈", "Aries"),
        ("♉", "Taurus"),
        ("♊", "Gemini"),
        ("♋", "Cancer"),
        ("♌", "Leo"),
        ("♍", "Virgo"),
        ("♎", "Libra"),
        ("♏", "Scorpio"),
        ("♐", "Sagittarius"),
        ("♑", "Capricorn"),
        ("♒", "Aquarius"),
        ("♓", "Pisces")
    ]

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)

        // Configure interface objects here.


        table.setNumberOfRows(zodiac.count, withRowType: "Row")

        for (index, sign) in zodiac.enumerated() {
            guard let row = table.rowController(at: index) as? Row else { return }
            row.lblIcon.setText(sign.0)
            row.lblZodiac.setText(sign.1)
        }
    }

    override func table(_ table: WKInterfaceTable, didSelectRowAt rowIndex: Int) {
        print(zodiac[rowIndex].1)

        let context = ["sing": zodiac[rowIndex].1]

        presentController(withName: "Results", context: context)
    }

    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
    }

    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }

}

Enter fullscreen mode Exit fullscreen mode

Model

The following structure is used for the response model:


import Foundation

struct Horoscope: Codable {
    var currentDate: String
    var description: String

    enum CodingKeys: String, CodingKey {
        case currentDate = "current_date", description
    }
}

Enter fullscreen mode Exit fullscreen mode

Second Screen

It is responsible for consuming the API, its interface is very simple and is built only with tags, which work very similar to iOS.

xcode

The Results Controller code is the following:


import WatchKit
import Foundation


class ResultsController: WKInterfaceController {

    @IBOutlet weak var lblSing: WKInterfaceLabel!
    var sing: String?

    @IBOutlet weak var lblDate: WKInterfaceLabel!

    @IBOutlet weak var lblDescription: WKInterfaceLabel!

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)

        // Configure interface objects here.

        guard let context = context as? [String: String] else { return }
        guard let sing = context["sing"] else { return }

        lblSing.setText(sing)

        DispatchQueue.global(qos: .userInteractive).async {
            self.fetchData(sing: sing.lowercased())
        }
    }

    func fetchData(sing: String) {
        guard let url = URL(string: "https://aztro.sameerkumar.website/?sign=\(sing)&day=today") else { return }

        print("https://aztro.sameerkumar.website/?sign=\(sing)&day=today")
        var urlRequest = URLRequest(url: url)
        urlRequest.httpMethod = "POST"


        URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
            if let data = data {
                self.parse(data)
            } else {
                print("vacio")
            }
        }.resume()
    }

    func parse(_ contens: Data) {
        let decoder = JSONDecoder()

        guard let result = try? decoder.decode(Horoscope.self, from: contens) else { return }

        DispatchQueue.main.async {
            self.lblDate.setText(result.currentDate)
            self.lblDescription.setText(result.description)
        }
    }

    override func willActivate() {
        // This method is called when watch view controller is about to be visible to user
        super.willActivate()
    }

    override func didDeactivate() {
        // This method is called when watch view controller is no longer visible
        super.didDeactivate()
    }
}

Enter fullscreen mode Exit fullscreen mode

The application running

xcode

xcode

Conclusions

Consuming a REST API in WatchOS is relatively easy and very similar to how it is done in iOS. You can find the project code in the following github repository.

Oldest comments (4)

Collapse
 
calo0012 profile image
ςคгɭ๏ร ɭ๏קєչ

Nice tutorial!! :O

Collapse
 
andreslopezrm profile image
Andres Lopez

Thanks!!!!

Collapse
 
daskwer profile image
Daskwer

I love reading about my zodiac sign in the morning, and thanks to apps like this, it only gets easier zodiacsigns-horoscope.com/angel-nu... . It's fascinating to learn about something like this, especially when you study and work in IT. So I should try to do this. I went to work here only thanks to the horoscope because I believe in the magic of numbers and stars. And if I constantly interact with numbers, it will be very cool. And it's true because I earn a lot of money now.

Collapse
 
andreslopezrm profile image
Andres Lopez

Thanks!!!!