|
Greetings, I already have the following: an Entity that have these attributes and a form that feeds information to that Entity. Beside the IP, that is computed based on the host name, everything is free text as a Textfield. That works well, and when added looks like that. What I would like to do is change the port number to a Picker and that Picker would let the user chose from a list of ports, that the user would have created beforehand. I want this list of ports to be saved in the cloud as well, like the current Entity I have. The list of port should be unique, as in the port number is only available once in the list (no duplicates), while the hostname can have multiple instances, each using a different port (I might work later on allowing to select multiple port at once for the same hostname, but not right now). What would be the best practice to do this? Do i need another Entity? Do I need to update the current Entity while creating a new one? If i have 2 Entities, do they need any kind of relationship? Than you :)! |
|
This is a great question! But please note, this is not a SwiftUI question. This is a data modeling question. @twostraws and HackingWithSwift.com excel at teaching Swift concepts, iOS programming, interspersed with fantastic interface design. However, data modeling is a skill taught in separate courses. It’s an important skill for developers to have, so it’s great to see you dig in. You’re asking the right questions. First: Step away from the keyboard, and grab some paper and pencils. Stop thinking about HOW you display the data on the iPhone. Instead, think of the data you want to collect and store. Focus on the data. EntityPlease don’t name anything in your model Entity. This is like naming your dog, Dog. Or naming every plate on your menu “Pasta”. Give it a descriptive name! In your app, the labels say Host. This seems like a nice descriptive name. Design your ModelHost ModelYou have already defined this model. It will hold name, IP, port, and group(?). Port ModelNext design the Port model. What data do you want to collect about a port? Obviously, you want to know its number. Is this always in integer? What are the rules about this number? Must be greater than zero, but less than 25,000 ? What does port 443 represent? Maybe you want a description of this data too? Port 443 is typically used for secure HTTPS data, while Port 25 is associated with SMTP email protocol. Focus on the data you want to collect. RelationshipsIf you draw a line between your Host entity and your Port entity, you’re establishing a relationship. But you need to decide what the rules are. Can a Host have more than one port? Can the host have two ports with the same port number? Can a port, say 443, be connected to the host named google.com AND to the host named hackingWithSwift.com ? In this case, Port number is NOT unique in the Port model. The combination of Port number and IP address is unique. In data modeling, this points out you may need a third entity table named PortTypes. PortTypesPortTypes might be a table you maintain that contains all possible port IDs and their descriptions. This could be the list your user updates and modifies. Then your app uses the values in the data table as a pick list when creating a new Host object. When your user picks the SMTP Port 25 and associates it with the google.com Host object, your model will create a new Port object where the object’s host is google.com, and the port is 25. Your rules will stipulate that for each Host, your user can only select a port once from the list. This is the fun of data modeling. Hope this helped. |
|
Hi @Obelix, Thank you very much for the detailed answer :-)
They did it in The Walking Dead :D 'group' is an optional attribute, in case users would want to group them, by environment like Prod, PreProd, etc... or by type, like Search Engines, Social, Coding, etc... I was already thinking of having a name for the port, so it's more user-friendly and that is why I was thinking at least a second Entity would be needed, so it can store more than 1 attribute. And ports range from 0 to 65535, with 0 being reserved to TCP, which is what I'm using here.
Yes
No
Yes
That is correct!
This is probably what I need, but also where you lost me :) Are you saying this 3rd table would have a relationship with the Port Entity, for port creation and a relationship with Host Entity, for creating the Picker for the user to select from? And no relationship whatsoever between Host and Port Entities? If that is the case, what kind of attributes would be linked together? I'm not really sure I get how they are actually linked once the relationship is created in XCode. I get the idea, but putting it in practice is more challenging for sure :) Thank you |
|
Data ModelThis is just a starting point. This may not be the best way to model your solution. To start, notice the only data you want to directly collect about a Host is its name and ipAddress. However, each host may have zero or many related Ports. For example a host may have both HTTPS, and SMTP ports. In my models I like to use the term "myPorts". This tells me that ONE host object contains a SET that contains that object's Ports. (The object can say these are "my ports".) But you don't want to store those inside the Host object. Instead, with Core Data, you create a relationship to the Port object as a one to many relationship. You can set up rules in your code telling Core Data that each Host can only have ONE SMTP port. That is, you cannot store more than one Port object with the same port number to any Host. This prevents you from having 2 or more 443 ports, for example. Also notice, it's not a good practice to store the Port's definition (or purpose) in the Port object. This would be redundant. If you had one thousand Hosts, and each Host had one port 443, you'd carry 1,000 strings holding the text "Secure HTTP". This is redundant, and could also cause problems. For example, your data reports could get out of whack if someone accidentally updates 600 of those descriptions with "Chat Protocol". Instead, you have a third relationship, PortDescription. This holds ONE record for each port number along with its description. You can set up constraints on this third entity so that you can never duplicate a port number. You can't have one record with port 443 and call it "Secure HTTP" and another record with number 443 and call it "Chat Protocol". This model allows you to have as many Hosts as you want, each host having as many Ports as necessary. If you have a port number and want to know its description, you reach out to the related PortDescription table for it. MaintenanceThis model probably points out that you need a maintenance screen to add, update, and delete Port Descriptions. You can prepopulate your application with the most common ports and their descriptions. However, you may want to add others in the future and allow your users to add them. This is a separate screen from updating your hosts and their ports. Probably this belongs in a maintenance or preference section in your application. (Think gear icon.) Cascading DeletesNow what!? Port Pick ListThis model also allows you to easily select ALL the PortDescriptions in your core data store, transfer them to an internal array structure, then use this array as a pick list when adding Port records to a Host object. If you user wants to add ports to a selected Host, it could easily filter the array of ALL port descriptions and REMOVE the ones already assigned to the Host. This would help prevent your users from accidentally picking a port twice. This is YOUR application, however! You must determine the business rules, figure out the relationships, and build the interfaces to help your users be most productive and to meet their business requirements. HomeworkReport back! We want to know your designs and business rules! Phew! Class dismissed! |
|
A lot of what you say makes sense and aligns with what I have in mind, for instance being able to rename a port description and prevent a host to get added twice with the same port. Not that it would break anything, but when you look for results later on, having duplicates just does not look good. Also i already know that i will not allow a port to be deleted if it's in use by a host :) Before implementing it in my application, I will practice and experiment with a blank app just to get a better grip on the relationships and building the interface around it. It will probably be a lot of trial and errors, better not do that right in my app :) If I get it right from your Dat Model above, I should start with that as Relationships: Host -> Port named myPorts and having Inverse defined and this one has a Type One-To-Many PortDescription -> Port named my Ports as well (but different from the previous one) and having reverse defined and this one has a Type One-To-Many Port -> Host named myHost and reverse defined and being Type One-To-One Port -> PortDescription named myDescription and reverse defined and being Type One-To-One Would that be a correct understanding? One more question: I've see a lot of video explaining Relationships and in most cases, even here on Hakcing with Swift, the Codegen is set to Manual/None instead of the default Class Definition. I'm not sure I really get the difference here and if I could just use the default or if I need to go the Manual way, if you have something simple example/explanation? Thank you |
|
So I've made some progress, not sure if in the right direction or not, we will see :) I've set up the 3 Entities like you suggested and their relationships too, which now looks like that https://ibb.co/7zHYSfx I've set up the basic code and it works until I tried to add an entry within the relationship and then I get this error:
On that line https://ibb.co/zQ7kpmG Here is the full code:
And the page that makes the call to that function:
I do not know what I do wrong here :-( Thank you :-) PS: Can't seem to be able to post images like you do. Either it's a link to the image or it does show like a broken image like in this post (right-click and open image does work though). |
|
@Nigel to the rescue. Please try searching for the answer in the forums, before giving up! |
|
That is the only post on the topic I could find, but it's valid only for dropbox. There is no ?dl=1 at the end of my image url, since it's coming from imgbb. Will ask in a separate topic to not pollute this one. Still stuck with my issue with the code though, if you see something obvious I'm doing wrong. |
|
I have tried multiple things tonight to no avail. The only progress I've made is actually understanding that my actual error is this one:
It's most probably because of all the errors about that "Failed to find a unique match for an NSEntityDescription to a managed object subclass CoreData: error: +[HostEntity entity] Failed to find a unique match for an NSEntityDescription to a managed object subclass (lldb)" but I have no clue why this happens as I do not believe I'm having multiple classes with the same name and from what I gathered, I'm only accessing it once in the init of the class. I'll probably restart from scratch and see where this goes I guess. |
|
I've created a brand new application, and really did the minimum on it, as in: Creating the Entities Setting their relationships Updating the View to display the information from 1 Entity only Updated the Save function to account for the Entity name and add an entry in the second Entity Still no luck and same error message:
Nothing about the duplicate Entities this time, just this error. And all I do is that line (since it's a static test to add, I just pass a plain Integer, nothing fancy):
Why is this line of code throwing that error? Am I doing it wrong? Thank you |
|
Still don't know what I'm doing wrong with using the syntax (and kinda feel like I'm talking alone here), but made some progress using a different syntax. Now it looks like that:
That works well to add everything, but the issue I have now is that it's all decorrelated, as in when I add a host, it adds an entry of myHost in HostToPortEntity and if I do the same with a new port, it adds an entry of myDescription in HostToPortEntity, but then I fail to be able, on one page, to display all the information at once, since adding a new port and adding a new host creates 2 different entries in the Entity. I'll keep looking... |
|
I think I got something that works. It's not pretty in the code, but it's functional for now. Turns out, I do not need a Relationship at all, nor 3 Entitites, but just 2 of them. One for the Host information, and one for the Service information (port number and port name). As for the Picker, I display it like that:
hostServices is a reference to the Service Entity, which contains the port names and port numbers. hostPortValue is just a variable that will store the value. From there I will save the port number in the Host Entity. And on the edit form, I use similar approach:
And if the port has changed, again, will save that new information in the Host Entity. Finally, if someone were to change the port number of the service, I will update the information in both the Service Entity and the Host Entity:
I can now work on tidying up the code now :-) |
SAVE 50% To celebrate WWDC23, all our books and bundles are half price, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.
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.
Link copied to your pasteboard.