My First App - Grow (Journey from Scratch!)

I hear you, thanks for your suggestions! The spacing above my title on the “Journey” screen won’t be such a big white space, It’s just avoiding the camera bezel which isn’t shown on the design frame. As for the spacing between the “Have a Mac” title and the card, I see what you mean and this might indeed be confusing, I’ll make the spacing smaller :+1:. Originally I put the spacing there to show that the title “Have a Mac” is a subgoal to accomplish and the card contains the plan to achieve it. But it might be better to let go of this idea to avoid confusion.

I’ll let you know if I have a solution for avoiding keyboard! I’m currently fiddling with some code to find a solution.

Indeed, humility comes before a ris, nice saying! And I also think that a simpler design can communicate your idea more effective. Since there is less confusion and less to see, the user’s eye is guided to the right elements and can comprehend and admire the design easier.

Thanks!

Have a good afternoon as well!
-Rune

Update 05/09/2024

First off, I just want to say I’ve had some trouble staying motivated recently; especially with the school year coming to an end and my mind slowly drifting away into the summer relaxation mode (which only lasts until I get a job). :relieved: So, consequently, progress has been slow-going.

Anyways, that’s not to say I haven’t made any progress; most of it has been brainstorming and contemplation. I want to share a sheet I filled out from Apple’s App Workbook (thanks Rune for the suggestion :slightly_smiling_face:); you can find it in the attachments. The major difficulty I am facing right now is how to research my potential users and gather user personas. For several reasons, I do not have a social media account, so it makes things a little difficult in terms of outreach, but I’m sure there are some valid solutions to this.

Excuse me for reiterating this (it’s just I’m gaining a clearer understanding as time goes on): my app is primarily targeted at people who want to learn how to focus on a device that controversially is meant to keep people “multitasking" all the time, and at people who want to free themselves from technology a little bit in general. Obviously, technology has many benefits, so how can we focus on these and get rid of its problems? (This is just a question I’m asking myself as part of my app brainstorming.)

Overall, this seems like an important issue to address. If you think about it, companies profit greatly from users using their devices constantly because say a user downloads Facebook; then they see an add for Spotify and download that too; then they realize they are spending way too much time on their phones and they (ironically) download a focus app… It goes on and on. Okay, maybe that was too dramatic, but I think that conveys my meaning pretty well: technology is often abused. As Steve Jobs said, "Technology is nothing. What’s important is that you have faith in people, that they’re basically good and smart — and if you give them tools, they’ll do wonderful things with them.” So, technology is supposed to be for the people, not the people for technology.

What does this mean for my app, then? Should it be just another app that tries to draw the user in, or should it respect the human being using it and try to give him/her the freedom to use the app by not using it? These are all questions that I am trying to narrow down into a single app idea as a MVP at least for now.

Thanks for reading! Now anyone looking through this will have a better idea of how I think about app creation in its initial stages. I truly enjoy brainstorming my ideas with other people, so thanks for indulging me. :smile:

-Michael

Sounds good! That makes sense. I was guessing there was a reason behind it.

Yes! Please do. There will always be little quirks here and there when coding a new app. Besides, SwiftUI is still rather new.

I so agree with you on the design points.

Talk to you later,
-Michael

Hello Michael,

Great news! I think I’ve found a solution on avoiding the keyboard.
When you want to avoid the keyboard in SwiftUI, you have two options:

  1. Build a custom class, modifier or API to detect and do all the calculations.
  2. Let SwiftUI handle the calculations and just guide SwiftUI to suit your app.

Having experience trying these two options, I’d choose the second option as SwiftUI never calculates wrong, and why should we go through the hassle of building our own code when Apple engineers have prepared one for us?
So we’ll use SwiftUI and just guide it to suit our own app. First I’ll explain how SwiftUI’s keyboard avoidance works. When the keyboard shows, SwiftUI simply increases the bottom safe area to the size of the keyboard, so your view is compressed into the visible space above the keyboard.
This is also why you can find the modifier to ignore this feature as follows:
.ignoresSafeArea(.keyboard)
Note: this only works when your view has enough compressible space (by which I mean view objects like Spacer, Rectangle, …), to show all its content in the space above the keyboard, when your view has to much absolute space, SwiftUI will ignore this modifier.

Taking this into account, we can again choose between two approaches, when you have compressible space in your view layout, you can safely let SwiftUI do its thing, just keep in mind that your view will be compressed into the smaller space above the keyboard when it shows. The only headache with this approach is that there is no direct way of specifying the spacing between two objects, or I didn’t find one :sweat_smile:. And this is where my solution comes into play, I’ve created a custom modifier which lets you specify a maximum frame which the compressible view object can occupy and a minimum frame to which this object can be compressed. The modifier uses SwiftUI’s Layout System to achieve this. Here is my code:

import SwiftUI

struct LimitFrame: Layout, ViewModifier {
    
    var maxWidth: CGFloat?
    var maxHeight: CGFloat?
    var minWidth: CGFloat?
    var minHeight: CGFloat?
    var alignment: Alignment
    
    func body(content: Content) -> some View {
        LimitFrame(maxWidth: maxWidth, maxHeight: maxHeight, minWidth: minWidth, minHeight: minHeight, alignment: alignment) {
            content
        }
    }
    
    func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) -> CGSize {
        guard subviews.count == 1, let content = subviews.first else {
            fatalError("LimitFrame can't have more than 1 subview.")
        }
        var result: CGSize = .zero
        
        // Define proposal with given max values
        let proposalWidth = numberBetween(max: maxWidth, min: minWidth, value: proposal.width ?? proposal.replacingUnspecifiedDimensions().width)
        let proposalHeight = numberBetween(max: maxHeight, min: minHeight, value: proposal.height ?? proposal.replacingUnspecifiedDimensions().height)
        let newProposal: ProposedViewSize = .init(width: proposalWidth, height: proposalHeight)
        
        // Ask required size
        let requiredSize = content.sizeThatFits(newProposal)
        
        // Compare required size with given values
        let width: CGFloat = numberBetween(max: maxWidth, min: minWidth, value: requiredSize.width)
        let height: CGFloat = numberBetween(max: maxHeight, min: minHeight, value: requiredSize.height)
        result = .init(width: width, height: height)
        
        // Return result
        return result
    }
    
    func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout ()) {
        guard subviews.count == 1, let content = subviews.first else {
            fatalError("LimitFrame can't have more than 1 subview.")
        }
        
        // Calculate leading and top constraints
        let dimensions = content.dimensions(in: .init(width: bounds.width, height: bounds.height))
        var xOffset: CGFloat {
            switch alignment.horizontal {
            case .leading:
                return 0
            case .trailing:
                return bounds.width
            default:
                return bounds.width / 2
            }
        }
        var yOffset: CGFloat {
            switch alignment.vertical {
            case.top:
                return 0
            case .bottom:
                return bounds.height
            default:
                return bounds.height / 2
            }
        }
        
        let leading = bounds.minX + xOffset - dimensions[alignment.horizontal]
        let top = bounds.minY + yOffset - dimensions[alignment.vertical]
        
        // Place the content at that position with the bounds as its proposed size
        content.place(at: .init(x: leading, y: top), anchor: .topLeading, proposal: .init(width: bounds.width, height: bounds.height))
    }
    
    func numberBetween(max maxValue: CGFloat?, min minValue: CGFloat?, value: CGFloat) -> CGFloat {
        let maxLimit = maxValue ?? .infinity
        let minLimit = minValue ?? .zero
        
        return min(maxLimit, max(value , minLimit))
    }
}

extension View {
    /// Limits the frame to be within the specified range.
    func limitFrame(maxWidth: CGFloat? = nil, maxHeight: CGFloat? = nil, minWidth: CGFloat? = nil, minHeight: CGFloat? = nil, alignment: Alignment = .center) -> some View {
        modifier(LimitFrame(maxWidth: maxWidth, maxHeight: maxHeight, minWidth: minWidth, minHeight: minHeight, alignment: alignment))
    }
    
    @available(*, deprecated, message: "Please specify at least one parameter other than alignment.")
    func limitFrame(alignment: Alignment = .center) -> some View {
        modifier(LimitFrame(alignment: alignment))
    }
}

If you’d like to learn more about the layout system in SwiftUI, you can watch this video by Paul Hudson, or if you’d like I’d also be happy to break it down for you.

We can also still use the second option, which you’d use when you can’t use or don’t want to use compressible space. You can put your view inside a ScrollView, and SwiftUI will automatically scroll to where the focused text field is shown. This works because ScrollView works with 2 layers, the first layer which it uses to position itself between other view objects, and the second layer in which it renders its child views. The second layer isn’t limited by the bounds of the first layer and therefore won’t be compressed when the keyboard shows, only the first layer will be compressed.

Hope this might help you on a later date, and if you don’t understand something please do say so!

Greetings, Rune

1 Like

Thank you! This is incredible! Okay, I see how this works. When I first heard about keyboard avoidance (the problem you were having earlier), I had to look up what it meant and this helps clarify those assumptions. Thanks for explaining everything so thoroughly.

I see you found a self-coded solution to the problem! I can understand most of the syntax, but am not quite sure what each piece of code does in the overall structure of the keyboard avoidance mechanism. Perhaps you could expand on the purpose of each func? I’m just curious about the role that each function plays in context. (I’ll plan to watch the video too when I get the chance.) :slight_smile:

It makes sense that a ScrollView is one of the most effective ways to avoid compression of the onscreen elements. It’s good to know the different solutions. Interesting layers situation! It makes sense, though.

Have a great day!

-Michael

Of course! I’ll first explain how the layout system in SwiftUI works and then move on to my code, this may provide a useful background.

SwiftUI is works with declarative view code, meaning your code represents the view layout. This is handy because it provides a better overview of how your view looks like and it is easier to learn. This also means that SwiftUI will read your view code and do all tricky calculations of placements and sizing etc. itself. Normally this is great and we don’t have to worry about it, but in this situation we need to understand how SwiftUI approaches this.
The direction in which SwiftUI reads your view code is from parent/container view to child views, it passes a proposed size to the view container in question, which then passes that size to its child view and asks to calculate its own size using the proposed size. The child view isn’t obligated to consider the proposed size when calculating its own size. When the container view got returned the size from the child view, it adapts its own size to hug the size of its child view, and then returns its own size to SwiftUI. This is how SwiftUI determines sizing. We can put this process in a few steps:

  1. Parent/container view receives proposed size.
  2. Parent/container view subtracts any specified spacing from this and passes the result as proposed size to its child view(s) using its sizeThatFits function.
  3. Child view calculates its own size in its sizeThatFits function whether or not taking the proposed size into account and returns its required size.
  4. Parent/container view takes the required size of its child view(s) and adapts it own size to it (also taking any specified spacing into account), and returns its own required size.
  5. SwiftUI uses these sizes to place the views in the available space calling the placeSubviews function on the parent view.

Taking all that into account, we can alter the two mentioned functions by conforming to the Layout protocol.

In the sizeThatFits function, where proposal is the proposed size, subviews a list of each subview in our parent view and cache a way of remembering the result of previous calculations to improve performance, we just check that the proposed size is between the given limit and pass that to our subviews as their proposed size. After the child view has returned its required size, we check again if this size is between the given limit and return it.
Note: The struct is defined as a container in its body, but since I use it as a view modifier, I make sure that there is only one child view.

In the placeSubviews, where bounds is the size for the container, proposal the previously proposed size and subviews and cache serve the same purposes as the sizeThatFits function, I simply place the views with the given bounds. I calculated the position of the child view using the given alignment. This involves some knowledge about Alignment Guides and how SwiftUI works with it, but you don’t have to worry about this as it isn’t relevant to the Layout System in SwiftUI.

I know this may be a bit much info at once, but I hope you could follow my explanation. Should you have any more questions, feel free to ask me.

Have a great evening,
-Rune

Thank you so much! This definitely makes a lot of sense. It’s cool to “look behind the scenes” at the very intentional software engineering that went into SwiftUI. I’ll certainly come back to this when designing my app, so thanks for explaining it.

May you also have a great evening.

-Michael :wink:

Update 05/16/2024

So, I have made several important decisions this past week:

First, after reading a lot of user reviews on the App Store, I decided to utilize many user-appreciated features included in successful focus apps; this has provided me with a solid foundation for what concepts I want to employ now and in the future. These concepts include, but are not limited to, the notification concept, the focus concept (speaking specifically about app blocking and screen time), the to-do concept, and the the progress concept (as seen in the Lotus Flower). Some additional concepts that I might use post-MVP stage are the alternatives concept (basically giving the user suggestions for how they can complete a task without using their device), the AI concept (a lot can be said here), the iCloud concept, and the collaboration concept (basically sharing your rewards with your friends and completing challenges together).

Second, I am now starting to create the designs for the different UI elements in my app. Like my architecture teacher taught me to do, I am first working on the individual pieces before combining them into an entire building (or app interface). The general rule of thumb is Form Follows Function, that way the UX is given priority over the UI, I guess. There is an attachment showing my designs (these are basically all the app elements I have created for Grow). In the final design, I will probably change the colors around a little bit so that everything looks good in dark mode.

Thirdly, by reading through the user reviews, I have also come across terms like ADHD and procrastinating, so that has helped me learn more about the user-side of all this. Many users seem to praise collaborative features and are also (understandably) quite frustrated by an app riddled with bugs. If I can release just a few new features at a time, perhaps I can avoid releasing an app with lots of bugs. There is definitely room for improvement when it comes to the focus apps I have looked into.

Next week I will be a little busy, but I will try my best to make more progress on my app idea.

Have a nice evening!

-Michael :slight_smile:

No problem! Glad I could help.

Update 05/23/2024

This week I have made some refinements to my design. My plan is to use SwiftUI Paths to design the Lotus Flower; this is because using Paths gives me the flexibility to add great animations as well as improving the overall performance as compared to loading an image. Not that many other decisions have been made, but hopefully next week will be more eventful.

-Michael:)

Thanks @Design_Pro Your post is really informative.

Of course! Let me know if you have any questions.

Update 05/30/2024

I’ll let the template that I filled out speak for itself (see attached screenshot). Most of my work has been to (of course) think about the design a bit more. However, above this, I have tried to research my potential users a bit more (me included :slight_smile: ). I think that doing this research is incredibly important, so that’s been my priority recently. Soon I will move on into the Analyze section of the App Development Keynote project provided by Apple engineers.

Honestly, I’m secretly waiting on WWDC24 to make decisions on all the features the Grow app will exhibit. There have been several hints about AI including an official teaser from Tim Cook, so this makes me extremely excited about the potential of using AI in Grow. This seems to be one area where competitors fall short, so there is quite a bit of room for me to explore. No doubt developers will jump right in when the new APIs are announced, but would they really think of adding AI to a focus app?

On the note of AI, I would probably integrate on-device (fingers crossed) AI processing that alerts the user to helpful alternative activities during their break periods depending on weather and their current environment. That’s just me brainstorming, but there are plenty of ways to put AI to work.

The app will start out as a free MVP (unless there is an upfront cost for certain features). Then, if the app gains traction, I can add iCloud syncing and more advanced AI features along with many other helpful things according to user feedback for a little bit of a cost (monthly/yearly/lifetime purchase options). Those are my tentative plans so far.

Take care! I hope you all enjoy your summers and find time to give yourselves a little bit of a break from your busy schedules.

-Michael :wink:

1 Like

Update 06/06/2024

The Grow app is about ready to enter the Prototype stage! In the attached slides, you’ll see the progress from researching competitors to defining the features that will be showcased in the MVP version of Grow. These features are still a bit of a rough sketch and not necessarily a final statement of what will be used in Grow; it’s pretty close, though.

WWDC24 is this coming week, so hang tight for my next update. I’m putting a lot of trust in Apple to deliver on their Artificial Intelligence teasers; if they don’t, I’ll have to turn to ChatGPT probably. That would be unfortunate, though, considering Apple will probably (hopefully) provide free access to all of its AI features to developers as well as world-class security and privacy.

I highly recommend this 9.5 minute video that will tell you the role of AI in our society; there’s a little statement you should look out for…it has to do with the power of companies: https://www.youtube.com/watch?v=HcZ6bq-RVM0
I also recommend this 14.5 minute video on how AI works. It’s really cool! https://www.youtube.com/watch?v=NxTTXuUl-Lc

Have a great weekend! See you on the other side of WWDC. As always, feel free to offer any suggestions or ask me any questions you may have.

-Michael :smile:

2 Likes

Update 06/13/2024

Wow. There’s a lot to discuss. I’ll keep it brief, however, given we all know Apple’s plans.

With Apple Intelligence (a sneaky way to prove that they have always been ahead in the AI era by stealing the acronym), there is an overwhelming number of possibilities for developers; in fact, I haven’t spent much time at all on my app, instead watching the Keynote and State of the Union videos and looking into the newly available APIs. (I also installed the Developer Beta of macOS Sequoia on a separate volume and have been playing around with it, thinking about where my app is going next). Overall, “AI for the rest of us” seems like a compelling feature to build upon, though we must acknowledge the fact that it will only be available on devices running M1 (or later), or A17 Pro.

On the note of compatibility, my plan is to build with the worldwide Apple user base in mind, meaning supporting older platforms and iPhones not compatible with AI (Apple Intelligence).

I’ll keep you updated on my progress. Next week I will be gone on a trip, so hang tight for an update two weeks from now.

-Michael :slight_smile:

@Design_Pro thanks for share your idea and your idea is amazing for grow app. I love your positive approach and customization features.

1 Like

Hello Nicomani!

Sorry for the late reply. I really appreciate your support! I’ve got some exciting news for this week’s Update…that is, as long as I can figure out how to code something I have never coded before.

Take care,
Michael :slight_smile:

Update 06/27/2024

It’s been a long couple of weeks, so I will make sure not to disappoint with this update.

  1. Feature update: After the announcement of Apple Intelligence which I briefly mentioned earlier, it seems like I will have to modify my AI plans just slightly; instead of including many of the crazy features I mentioned, I think I will leave everything to Apple Intelligence since it already has many capabilities such as Priority Notifications and Onscreen Awareness. I will adopt App Intents and Siri for sure.

  2. App role update: Now I’m thinking about keeping my app extremely simple and straightforward; especially having seen many apps such as Flow and One Thing absolutely nail the concept by being clean and having a definite purpose. Grow will help people focus on one thing at a time by blocking apps if necessary or the entire device and providing a timer to track progress. The user can create focus cards tailored to specific subjects (Math, Science, etc) or work and set customized parameters for each so that all it takes is a tap or a Siri request.

  3. Implementation approach: After hanging out in Figma for several months, I decided to go all out and create a timer for my app. My future approach is probably going to be separately building each element needed to complete the grow app (a timer, a website/device blocker, the Lotus flower, etc). Check out the video of the timer I coded! I’m on the way towards adding the Dynamic Island support and Live Activity, but having spent probably about 50 hours so far just on a timer, I’ll leave that until next week.

Thank you so much for your support, everyone! It really helps to have motivation (something my app will strive to foster) so that I can stay on track and push myself.

May you all keep up the good work as we developers try to create better apps as more tools (such as AI) are made available to us.

-Michael :smiley:

iPhoneTimer-ezgif.com-video-to-webp-converter

iPhoneTimer-ezgif.com-video-to-webp-converter-2

iPhoneTimer-ezgif.com-video-to-webp-converter-3

Update 07/04/2024

Happy Fourth of July!! I’m leaving for a trip tomorrow, so I won’t have any update next week. Today, I just want to say that I’ve spent some more time on my Timer app, integrating the Dynamic Island feature, Live Activity, notifications, and a more efficient timer system. There are still a few problems with the underlying functionality of the timer, but overall it’s almost ready for integration into my Grow app! Next, I plan to start on the Lotus flower which should be much easier; I’m guessing SwiftUI’s Paths are the best way to do this. Hopefully I can post a few screen recordings when I get back.

Have a nice day!

-Michael :slight_smile:

Update 08/15/24

Hey everyone! It’s been quite the roller coaster ride this past month! Well… I have had trouble staying motivated to complete my app, especially juggling math, a summer job, college applications coming soon, and an SAT, but hopefully everything will fall into place soon. It seems that I need motivation to complete a motivation/focus app (ironic, right?). Well, I read a great article on motivation from the Harvard Business Review; here is Apple Intelligence’s summary of the article (along with the link to it):

"Self-motivation is crucial for high-achieving professionals, but it can be challenging to maintain. To enhance motivation, individuals should design specific, intrinsic goals, find enjoyable aspects of tasks, and create effective rewards that complement organizational incentives. Gamifying tasks with uncertain rewards can also boost motivation.

Loss aversion can be used to design external motivators, such as StickK.com, which allows users to commit to a loss if they fail to achieve their goal. To sustain progress, breaking goals into smaller subgoals, changing the way progress is perceived, and focusing on completed steps can increase motivation. Social influence can also be harnessed by seeking advice from role models, talking to ambitious peers, and recognizing the support of close friends and family."
-Apple Intelligence

(Harvard Business Review: How to Keep Working When You’re Just Not Feeling It)

Anyways, that’s my two cents; hopefully I can pick up speed in September after I drop a few things.

Take care,
-Michael :wink:

P.S. Reading this article has brought to light a few things that might really work in the Grow app:

  • Gamification
  • Surprises
  • Social competition
  • Fun UI
  • Progress map (1st half)
  • Target in-sight (2nd half)
  • Subgoals
  • Failure consequences