Mark Moeykens' Journal

WEATHER APP

:spiral_calendar: Day 3

  • I did find a way to optimize the Combine for doing city lookups! It’s much better now and more efficient as the Combine operator I use now will cancel all previous requests to lookup cities if it’s still processing.
  • Got everything connected so when you select a city you looked up, it’ll update your list of cities.

Trouble

  • Ran into trouble with the main UI. The problem is I didn’t plan it out well enough. I didn’t have a clear idea of exactly how I was going to organize all the cities, view a city’s weather, and delete a city.
  • I also ran into a possible Xcode bug with Combine but it’ll take more work to verify it with a sample project that a friend of mine at Apple wants me to create. Ugh. Do I really want to take the time to do this?

2 Likes

I’m sure you will figure the Combine issue out :wink: Who else if not you?
Quick question: Why are you using a GeometryReader around the atmosphere image?

Thanks, Florian!

Yeah, the Combine issue was fixed. It was actually an error on my part by decoding into the wrong object. The problem was Xcode couldn’t tell me that. It would just show this weird error when trying to compile.

Why the GeometryReader?

Great question. That image is larger than the size of that device. And when it’s larger it pushes out the containing ZStack.
This will mess up the other views.
The GeometryReader is kind of a hack to prevent the image from pushing out the ZStack.
Now I’m not saying this is the best way but at this time I couldn’t find a better way.

2 Likes

Oh wow, that is very interesting.
Would adding a frame of UIScreen.main.bounds to the image do the same thing?
If yes I think that’s a cleaner architecture, the GeometryReader looks really hacky to me haha.

WEATHER APP

:spiral_calendar: Day 4

I learned quite a bit yesterday and feel I worked on this entirely too long because of so many problems I ran into.

Some of the problems I had to figure out while working with a Paging TabView:

Problem

  • When adding a new city, I was trying to navigate to the new city BEFORE the API finished returning the weather data. This caused an “index out of bounds” error.

Solution

Problem

  • I wanted to navigate to the new city I just added. City weather is inside the Paging TabView. So how do I do this?

Solution

  • I had to add a selection @State property that I could change programmatically that is bound to the TabView. I change this property when the city lookup sheet is closed.

@State private var citySelection: Int = 0

TabView(selection: $citySelection) {

Problem

  • Deletion proved a challenge. The actual deleting was fine. It was updating the UI. Originally, I would remove the item from the array and depend on the UI to update automatically. Which it did. But then crashed.
    Apparently, the TabView is implementing a UICollectionView behind the scenes and I’d get an NSInternalInconsistencyException.

Solution

  • Someone suggested I add a.id(UUID()) to my TabView. While this did work, the app crashed again when swiping.
    I think the problem was a new UUID() was getting created if the view refreshed. This caused the TabView to recreate again when swiped too. So I set the id to a stable value that ONLY changed after deleting. Causing the TabView to be refreshed (recreated).
    Was this the best answer? Probably not. I think this is a bug. But with a new version of Xcode coming I’m not going to file a Feedback until I try the new version to see if it’s already been fixed.

Problem

  • Originally I had the side-scrolling hourly temperatures below the TabView. And then when I swiped in the TabView, I detected the selection changed and updated the hourly temperature values.
    This was very error-prone once I got into adding, deleting, and refreshing the TabView.

Solution

  • I moved the hourly temperatures INSIDE the TabView to keep the values synced to the city.
    I want to change the UI around a little though. Maybe later.

More to do

  • The deletion of cities will take a little more testing to make sure it’s solid.
  • I need a Settings view to change from Farenheit to Celcius. The logic is there, I just need a place to change it.
1 Like

WEATHER APP

:spiral_calendar: Day 5

  • If I deleted a city and then added a new one, the index got messed up within the TabView. I reorder the index values after deleting a city. This helps with programmatic navigation.
  • Added progress view when first loading.
  • Added Settings view to switch between Fahrenheit and Celcius.
  • Added app icon.
  • Clean up code, added comments on where to find more info on the Combine parts in my book.
  • Tweaked UI, adjusted some padding, added background to hourly temps.

It’s done! Woo hoo!

2 Likes

Dude that design is smooth! I’m discovering just how tricky the design layouts can be just at the concept level. Yours looks great!

1 Like

Hey, thank you so much, Pagasi!

It looks better than the standard weather app heheh

1 Like

Thanks, Chris!

Your app looks great! Just to know how you did the data fetching with Swift Combine I bought you book :grinning: https://www.bigmountainstudio.com/combine/z7bnj :innocent: :drooling_face:

1 Like

Thank you for your support, Ralph! :green_heart: If you look in the code comments you’ll find references to specific parts of the book. Should get you pointed in the right direction to learn more in the book examples. :+1:

1 Like

Thank you for writing & publishing the book :blush: It‘s a great help to learn Combine!

1 Like

NBA STATS APP

So excited to do this again!
(My own reference to the challenge requirements.)

:spiral_calendar: Day 1

  • No coding, just research. Not much time today.
  • Setup account with sportsdata.io and started the free trial
  • Created an Xcode project in Xcode 13 beta 2. Could be taking a chance here. :smiley:
  • Found the API for scores
  • Found the API endpoint for scores by date (to get yesterday’s data)
  • Found the API endpoint for schedules (it’s on the same page, so that’s handy)

Team Images

  • Looks like there’s a different endpoint called “Teams (All)” to get all the team images and color palettes?
  • Could be interesting to use these colors in the UI.

Problems

  • Thinking about how to cache the team info so I only need to get it one time. Thinking Core Data is a good solution, but man, I haven’t used Core Data for anything at work so I never really used it. Not sure how much time I want to invest in learning it.
  • Anything I start to learn usually turns into a separate reference product which is very time-consuming. So first, I’m going to research quicker solutions and maybe have to learn Core Data after. This will be a good challenge for sure!

Core data is pretty easy to get up and running, Chris also has some awesome videos on this!!

2 Likes

hahaha… “…turns into a separate reference product…”
I can’t wait.

2 Likes

NBA STATS APP

:spiral_calendar: Day 2

Day of learning!

  • Watched Chris’s video (thanks for your suggestion too, @mikaelacaron)
  • Created a separate project to learn Core Data and play around with it
  • Tried my hardest NOT to start a book but I need to jot down some notes!

Apple Pages is a good place to keep notes.

So I started a new “notebook”. Which may look like a book but it’s not…yet.

(Seriously, I have too much to do right now so this really is just a notebook.)

2 Likes

I can see you fighting the urge to make another book. “The force is too powerful Luke…”

1 Like

NBA STATS APP

:spiral_calendar: Day 3

  • Got the app navigation layout. I’m going simple with a tab view.
  • Got the API working to get today and yesterday scores.
  • Then I was working on showing the future schedule and ran into a roadblock trying to decode the DateTime in the JSON. The error says:

Expected date string to be ISO8601-formatted.

I think Apple’s definition of iso needs a time zone. So I created a custom date format so I could decode with the help of this site:

extension DateFormatter {
    static let apiDateFormat: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss"
        formatter.calendar = Calendar(identifier: .iso8601)
        formatter.timeZone = TimeZone(secondsFromGMT: 0)
        formatter.locale = Locale(identifier: "en_US_POSIX")
        return formatter
    }()
}

This date format matches the API’s date format.

Then I had to use this date format for the JSONDecoder() so I built a custom class and a static property:

class JsonDecoder {
    static var forApiDateFormat: JSONDecoder {
        get {
            let decoder = JSONDecoder()
            decoder.dateDecodingStrategy = .formatted(DateFormatter.apiDateFormat)
            return decoder
        }
    }
}

Besides that, I got:

  • The APIs working
  • Minimal data showing on all 3 screens

So that’s a pretty good win for me.

1 Like

NBA STATS APP

:spiral_calendar: Day 4

Man, I hate to say this but I think I’m going to have to throw in the towel on this challenge!
There is a lot of exploratory research I want to do to make this work such as:

  • Explore using the NSCache object
  • Explore persisting with a file
  • Explore persisting using Core Data

I think these topics could be a lot of fun to play with but then I look at other priorities on my plate right now:

  • Updating 4 books
  • House shopping
  • Family time
  • Recording videos

I’m overbooked!

So I didn’t make it very far. :disappointed:

My plan was to call the Teams (All) API endpoint and store all the team data locally. This API gets me this data:

struct TeamDO: Decodable, Identifiable {
    let id: Int
    var city = ""
    var name = ""
    var primaryColor = ""
    var secondaryColor = ""
    var tertiaryColor = ""
    var imageUrl = ""

    enum CodingKeys: String, CodingKey {
        case id = "TeamID"
        case city = "City"
        case name = "Name"
        case primaryColor = "PrimaryColor"
        case secondaryColor = "SecondaryColor"
        case tertiaryColor = "TertiaryColor"
        case imageUrl = "WikipediaLogoUrl"
    }
}

So I wanted to persist them and then when I got score info, I could access this local data and get the full team names, logos, colors.

Well, good luck to everyone else on the challenge!

1 Like