UPGRADE YOUR SKILLS: Learn advanced Swift and SwiftUI on Hacking with Swift+! >>

Notification in an app

Forums > Swift

I developing an app that will track the number of times a hymn is sung. The app will also give the user the ability to select hymns and then email a list of hymns that will be sung at the next church meeting.

Question #1: When the user updates/deletes/adds a record in the database, how do you notifiy the user that a change has occured.

Question #2: I also want to have a reminder notification to the user to create the list of hymns to email. I want this notification to be one that would notify the user like 3 days before the sacrament meeting.

Thanks Mark

3      

I am using SwiftUI, so Im not sure if that matters or not. Just wanted to be clear.

3      

hi Mark,

i went back and reviewed our previous conversation, and have thought more about the overall structure of your app. instead of addressing just the loading/setting up of Core Data issue, i think i now better get what you're trying to do. i think this is:

1) the actual Hymn content (consisting of an id, songName, and songNum, and maybe more detail such as lyrics, even a URL to the music score) is fixed in a json file in the app bundle and will not be edited by your app.

2) for each Hymn, you maintain a persistent representation (not a complete copy) of it in Core Data to track it by its id, hymnName, hymnNum, and number of times played. we got that structure initialized in the previous thread.

this can work, but you'll need to next describe in more detail how the app will function and what you will see on screen.

it sounds as if you want to build "Playlists," where each is an ordered list of the Hymns to be played on such-and-such a date, with a name or occasion such as "Easter, 2020."

perhaps the following could be an app design.

the Main view (default ContentView) of the app is a TabView, with tabs for Playlists and Hymns. each shows a list: perhaps for each Playlist, that's a date and a name; for a Hymn, it might be a name and the number of times it has been used in a Playlist.

tapping on any list item shows a detail screen (for a PlayList, it allows you to edit the Playlist, set a reminder notification and email it, while for a Hymn, it shows you non-editable detail such as lyrics). the Playlist screen also lets you add a new Playlist.

would something like this work for you?

if so, i'd suggest using two Core Data entities and a relationship:

  • one, called HymnsPlayed from the earlier thread. but i would pick a better name such as HymnReference to make clear that it is information about only one Hymn and merely a reference to complete information about the Hymn that is stored elsewhere in your app of type [HymnData].
  • the other might be a PlayList.
  • there would be a many-to-many relationship between these two entities (e.g., hymns for a Playlist, and playlists for HymnReference). you want many-to-many: each Playlist will have multiple hymns, and each Hymn/HymnReference will appear in many Playlists.

of note: a HymnReference need not worry about the order of its playlists, but a Playlist probably does want Core Data to keep its hymns ordered. and importantly, a HymnReference need not keep a playCount attribute, since it will be the same as playlists!.count

the Playlists tab view now pulls in all the Playlist objects to form a list display using @FetchRequest; the Hymns tab view now pulls in all the HymnReference objects to form a list display using @FetchRequest.

getting to detail views from either of these lists is easy via a NavigationLink, although to show full detail on a Hymn might require looking up extended data in the [HymnData] array, which is easy because every Hymn has a unique id.

if all of this works, then we can talk more about scheduling future notifications (but Paul Hudson has already done this) and then work through what you mean by your Question #1.

sorry for the length, but hope that helps,

DMG

3      

Hi, DMG

Basically my app will have two main views.

First view will show all the hymns from the JSON file [HymnData] structure. This view will have 3 picker options (123, ABC, Topics) as a segmented control. You will also have the option to search by name or topic. This view will show a play icon if the hymn has a URL to the MP3 in the [HymnData] structure. This view is primarily used to search through the list of hymns that are available and if you need to play a MP3 if you don't remember what the hymn sounded like.

123 Tab shows a list of hymns by category. The section header has the name of the category. ABC Tab shows a list of hymns sorted alphabetically Topics Tab shows list of topics and if you tap on the topic it will show detail screen with hymns that belong to that topic.

This whole view is controlled by the JSON file.

Second View is my playlist view, if you want to call it that. I am not really creating playlists that I will show to the user. Instead this view has a segemented control with the following segments: (Played, Not Played).
The Played view will show all the hymns through an @FetchRequest that have playCount greater than 0. The Not Played will show all the hymns through an @FetchRequest with a playCount equal to 0.

I only want one CoreData entity called HymnsPlayed that just trackes the date the hymn was played and the number of times it was played.

I plan to have a contextual menu on this view that will give the option to reset the playCount of a hymn. This is done just in case you want to reset a single hymn or do a year end reset of all the hymns. This view will also have a navigation bar item that will let you show a sheet view where you can choose the hymns for the upcoming Sundays service. Those chosen hymns will just be in some sort of array/structure and once the hymns are chosen I want the user to be able to email the list to someone who would be in charge of creating the pamphlet for a Sunday service.

So for question #1 - if a user is in this view and chooses the hymns he/she wants how do you notify the user that the entity for the hymns have been updated. Like the datePlayed attributed could get updated to the new date if the song is chosen and the playCount attribute would get increased. Lets say the user decides to delete a song and what that means to me is resetting its playCount back to 0. How can I handle notifications to the user that those actions have completed.

So for question #2 - I want to have simple notification to the use to remind them its time to create and email this sundays hymns.

Hopes this makes it more clear what Im trying to do.

Its a pretty simple app I think. I'm just new to iOS development so I'm trying to figure out all the moving parts.

Look forward to any suggestions or help you could provide.

Thanks, Mark

3      

(this is the shortest TL;DR; version i could come up with!)

hi Mark,

i don't think you're too far from getting this done. i suggested a Playlist entity so you can keep a historical record of what had previously been selected for services, and you could not only track the number of times a hymn had been played, but where and when, by date or service. but if that's not of interest, we'll pass on it.

so, some suggestions.

1) the first two views could work as you describe, although they are somewhat necessitated by having two different types of data for hymns (in the first) and then their references that count (in the second).

you might consider not creating just hymn references in Core Data, but instead copying complete HymnData into Core Data (along with the extra attribute for number of times played). you immediately gain the benefits of Core Data's scalability and memory management, and you'll not find yourself continually matching up different types of objects (e.g., given a hymn reference from Core Data, find the Hymn in an array for more data).

your code would be more uniform since all data comes from Core Data and its all the same type. if you do that, then these two views might change into a single view with different sorting or display choices determined by a segmented control.

2) your app somehow keeps track of a "current hymn list" of hymns selected for the next service. you can easily add to it while running the app by having a contextmenu item attached to each hymn that does "add" to place any hymn you see in the first view onto the list. it could also do "remove" for a hymn that is on the list.

3) at some point you will pull up a sheet that shows the "current hymn list" and make further modifications before sending it out. perhaps you can rearrange the order of selected Hymns (set editMode to .active and use an .onMove() modifier) or remove items (use the .onDelete() modifier).

somewhere on that sheet, there's a button that says "send the email" and perhaps another button that says "clear the list."

that should do it, except for a just few details.

PERSISTENCE: if you are building a "current hymn list," you probably want to persist your choices. you'd not want your program to crash or your app to be shutdown by the system while it's in the background and lose that list before you're finished with it.

this suggests adding a boolean field for the Core Data record that indicates whether a hymn is selected or not for the current list. adding a hymn to the list simply sets that boolean and saves the current managed object context.

any list display of hymns can also easily indicate whether the hymn is already chosen; and whenever the sheet is brought up, it just fetches all the Core Data records that are selected. but note that you'll have to put the managedObjectContext into the environment of the sheet when you open it up; sheets don't automatically inherit environment of the parent.

EMAIL: this was easy in UIKit-based apps by directly using MFMailComposeViewController, or by wrapping the mail message inside a UIActivityViewController. to do this from SwiftUI requires a little research, and i'd recommend that you start searching around on Github. i don't have any code to share on this, although most solutions out there will probably wrap the MFMailComposeViewController in a UIViewRepresentable.

when the email is successfully sent, that's when you update the number of times played for the hymn on the list.

REMINDER NOTIFICATIONS: that's not anything i really know much about. i used Paul Hudson's code one time when learning about it, but have not done this myself.

UPDATE NOTIFICATIONS. although i use the NotificationCenter in one of my projects to communicate internally among disparate view models, i don't see a need for that here in your project. (perhaps that's the source of "how will the user be notified" question #1?)

all Core Data objects are ObservableObjects and any View driven by a @FetchRequest will (or at least should) pick up any change you make to the number of times played attribute or the selected attribute. those appear to be the only items that you edit. only if the changes are not reflected would you want to consider using some method other than reliance of @FetchRequest.

not sure what else can be said until we see real code (e.g., if you have something on Github); nevertheless, hope that helps,

DMG

3      

Hi DMG,

Yes, the two views were necessitated by the two different data types for hymns. So I agree I will just have one entity for the HymnData, that way I will eliminate that problem with what I termed a Playlist view. Then I will create a true playlist view that shows playlists by name. Thanks for pointing that out to me.

I like the idea of having a playlist entity, the problem I have with the playlist option is that I can don't know how to get that to work. How does or would HymnData relate to a Playlist entity. I also don't understand the inverse relationship stuff. How would I go about doing that?

If you could explain that with some examples showing how to set the many-to-many relationship between the HymnData and Playlist entities that would help alot. Also when adding a playlist how do you add hymns to it.

Thanks, Mark

3      

hi Mark,

so, you'll do a Playlist entity ... i think that's a good choice. so there's a now Core Data entity that describes a Playlist. it contains basic info such as a date of a service, a list of hymns used (and in what order), and whatever else you'd like to manage.

How does or would HymnData relate to a Playlist entity. I also don't understand the inverse relationship stuff. How would I go about doing that? If you could explain that with some examples showing how to set the many-to-many relationship between the HymnData and Playlist entities

basically, each Hymn (note: your name for the Core Data version must be different from HymnData, so i will call it simply Hymn) will have an attribute named playlists to hold references to the playlists in which it is used; and each Playlist will have an attribute named hymns for the hymns used in the Playlist.

you set these up in the data model editor:

  • add a relationship to Hymn named playlists, set its Destination to PlayList, and set its Type to "To Many"
  • add a relationship to Playlist named hymns, set its destination to Hymn, set its Type "To Many," set its Inverse to playlists, and check the box next to Arrangement to "Ordered"

Also when adding a playlist how do you add hymns to it

there are two questions here:

  • how to link a hymn and a playlist in code, and
  • how the user of the app indicates within a View that a given hymn should be added to some playlist

the second of these is up to you, since it's your UI. so i will try to answer only the first.

given a Playlist object p and a Hymn object h, you link them together with either of the following statements

p.addToHymns(h) // addToHymns is a function provided by XCode
// or
h.addToPlaylists(p) // addToPlaylists is a function provided by XCode

the reason i say either above is that if you do one, Core Data will automatically do the other for you exactly because of the inverse relationship. there's no need to do both (although it's not an error to do both, since Core Data -- i think -- will not duplicate these relationships).

and then, how do you read the playlists and hymns attributes? unfortunately, because of Core Data's Objective-C heritage, XCode generates these definitions for the types:

// for Hymn
@NSManaged public var playlists: NSSet?
// for Playlist
@NSManaged public var hymns: NSOrderedSet?

it's typical to add extensions such as these to the Hymn and Playlist classes to avoid constantly having to switch back and forth between Objective-C types and Swift types:

extension Hymn {
  var thePlaylists: Set<Playlist> {
    if let playlists = playlists as? Set<Playlist> { // this will not fail
      return playlists
    }
    return []
  }
}

extension Playlist {
  var theHymns: [Hymn] {
    if let result = hymns?.array as? [Hymn] { // this will not fail
      return result
    }
    return []
  }
}

so, for example, in a (detail) View associated with a Playlist that is var playlist: Playlist, you would display all the associated Hymns with

List {
  ForEach(playlist.theHymns) { hymn in
    // display its name, etc.
  }
}

hope that helps,

DMG

3      

Hi DMG,

Thanks for the reply, I will let ya know when i get close to getting something to look at. i wouls appreciate you opinion.

Thanks Mark

3      

Hacking with Swift is sponsored by Essential Developer

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until April 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.