After covering Reflection Prompts, I decided to talk about State of Mind suggestion for journaling.
Understanding JournalingSuggestion.StateOfMind
Apple introduced the JournalingSuggestion.StateOfMind
structure in iOS 18.0 (currently in beta). It captures a person's emotional state in a journaling app.
Here is what the structure looks like:
/// A suggestion that describes a state of mind reflection in the Health app.
@available(iOS 18.0, *)
public struct StateOfMind : JournalingSuggestionAsset { }
This structure comes with some useful properties:
darkBackground
: An optionalGradient
for dark mode icons.lightBackground
: An optionalGradient
for light mode icons.icon
: A URL to an image that represents the mood.state
: The actual state of mind, stored as anHKStateOfMind
object.
I have already covered HKStateOfMind
in detail here:
The system gives your app an instance of this structure when someone picks a state of mind suggestion in the JournalingSuggestionsPicker
.
Implementing State of Mind Suggestions
Let's look at how you can use this in your app:
import JournalingSuggestions
import SwiftUI
import HealthKit
@available(iOS 18.0, *)
struct StateOfMindView: View {
@State private var stateOfMind: JournalingSuggestion.StateOfMind?
@State private var suggestionTitle: String?
var body: some View {
NavigationStack {
VStack {
if let stateOfMind = stateOfMind {
StateOfMindDisplayView(stateOfMind: stateOfMind)
}
JournalingSuggestionsPicker {
Text("How are you feeling?")
} onCompletion: { suggestion in
suggestionTitle = suggestion.title
Task {
stateOfMind = await suggestion.content(forType: JournalingSuggestion.StateOfMind.self).first
}
}
.buttonStyle(.borderedProminent)
}
.navigationTitle(suggestionTitle ?? "")
}
}
}
This view includes a JournalingSuggestionsPicker
to get new state of mind suggestions. When a user picks a suggestion, we update the stateOfMind
property.
An interesting thing to note is how the system handles data from third-party apps. If a third-party app logs state of mind data, it shows up in the title. For example, you might see "Arising - Momentary Emotion" for my app called Arising that logged the mood data. This helps users understand where their mood data is coming from.
Next, let's create a view to display the state of mind related information:
import HealthKit
extension HKStateOfMind.ValenceClassification: @retroactive CustomStringConvertible {
public var description: String {
switch self {
case .veryUnpleasant: "Very Unpleasant"
case .unpleasant: "Unpleasant"
case .slightlyUnpleasant: "Slightly Unpleasant"
case .neutral: "Neutral"
case .slightlyPleasant: "Slightly Pleasant"
case .pleasant: "Pleasant"
case .veryPleasant: "Very Pleasant"
@unknown default: "Neautral"
}
}
}
@available(iOS 18.0, *)
struct StateOfMindDisplayView: View {
let stateOfMind: JournalingSuggestion.StateOfMind
var body: some View {
VStack {
if let iconURL = stateOfMind.icon {
AsyncImage(url: iconURL) { image in
image.resizable()
.scaledToFit()
} placeholder: {
ProgressView()
}
}
Text(stateOfMind.state.valenceClassification.description)
.font(.title)
.bold()
.multilineTextAlignment(.center)
.padding()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(backgroundGradient)
}
private var backgroundGradient: some View {
LinearGradient(
gradient: stateOfMind.lightBackground ?? Gradient(colors: [.white]),
startPoint: .topLeading,
endPoint: .bottomTrailing
)
}
}
I added custom string representation for each mood state. This view shows the mood icon and uses the custom description for the state of mind.
When you tap "How are you feeling?", the system shows a picker interface. After selecting a mood, the onCompletion
closure runs with a JournalingSuggestion
object. This object has the state of mind information.
Here is what a raw JournalingSuggestion
object looks like:
JournalingSuggestions.JournalingSuggestion(items: [JournalingSuggestions.JournalingSuggestion.ItemContent(id: FE9C3C74-1AEA-4003-9BFD-5F0C58DC36C2, representations: [JournalingSuggestions.JournalingSuggestion.StateOfMind, SwiftUI.Image, UIImage], content: JournalingSuggestions.InternalAssetContent(providers: [JournalingSuggestions.InternalAssetContent.AssetProvider(type: JournalingSuggestions.JournalingSuggestion.StateOfMind, loader: (Function)), JournalingSuggestions.InternalAssetContent.AssetProvider(type: SwiftUI.Image, loader: (Function)), JournalingSuggestions.InternalAssetContent.AssetProvider(type: UIImage, loader: (Function))]))], title: "Arising — Momentary Emotion", date: Optional(2024-08-16 06:42:23 +0000 to 2024-08-16 06:42:23 +0000), suggestionIdentifier: AB5F68CD-D892-4873-9E24-606B3C887518, suggestionHashValue: 12941230080)
Moving Forward
It is a bit annoying that the Health app notes are constrained when you log a new entry, but makes sense that it is not a journaling app. I guess that is where this journaling suggestions fills the gap.
Remember, this feature is still in beta for iOS 18.0. Keep an eye on Apple's documentation for any changes before the official release in September.
Happy coding and journaling!