hi,you mentioned:I checked out the free 24 page download for Hacking macOS by Paul Hudson from the Hacking With Swift site, but the in the 24 page sample it starts off having you create a macOS application and choosing "Cocoa Application", which is not an option in XCode 11.1. Is it the same and Apple just changed the name to just "App"? I'm skeptical to buy something when right off the bat there is an inconsistency with what the reader is encountering between the book and real world. Anyone have experience learning from that author?i've found Paul Hudson's materials to be top-notch, but it's unfortunate that the screen shot he has at the opening shows a slightly older version of XCode. it should of course be MacOS --> App (in XCode 11.4). you should contact Paul directly if you have concerns -- he posts his email address on the page where you found the download link.hope that helps,DMG
Post
Replies
Boosts
Views
Activity
hi,the ToDoList and the ToDoItem relationship is one-to-many, so (if you take the usual, default Class Definition codegen method), XCode will generate the ToDoList class having an property toDoItems: NSSet?.you can turn those items into an array of the right type and work with it using something like this:struct TodoItemView: View {
var todoList: TodoList
var toDoItems: [ToDoItem] = {
if let items = todoList.toDoItems as? Set {
return Array(items)
}
return [ToDoItem]()
}
var body: some View {
VStack {
Text(todoList.title!)
List {
ForEach(toDoItems, id: \.self) {todoItem in
Text(todoItem.title!)
}
}
}
}
}i think this works -- but, apologies, i haven't tested (i'm guessing a little on this, but i have used a type conversion like the one above several times [thanks, Paul Hudson @twostraws]). hope this helps,DMG
hi,i pulled the syntax i used from something else that's a little older; i believe the syntax you found is equivalent (i explicitly created the timer and added it to the run loop).i think there's also no need to explicitly invalidate() the timer as i did -- it fires only once and is immediately invalidated (whereas the situation i pulled this from involved a repeating timer).hope this helps,DMG
hi,how about setting a timer when the LabelView() appears, and then resetting presentClipboardView to false with animation? i think the following works in my test:(1) add the .onAppear modifier to LabelView() in line 39if(self.presentClipboardView){
LabelView()
.onAppear(perform: setDismissTimer)
}(2) and add this new function to ContentView:func setDismissTimer() {
let timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { timer in
withAnimation(.easeInOut(duration: 2)) {
self.presentClipboardView = false
}
timer.invalidate()
}
RunLoop.current.add(timer, forMode:RunLoop.Mode.default)
}hope this helps,DMG
hi,MARK can be used in conjunction with comment syntax to separate out portions of your code so you can navigate easily in your code using the pull-down at the top of the XCode window.for example, you might group all the life cycle code for a UIViewController this way, followed by methods for certain other tasks:// MARK: - Lifecycle
override func viewDidLoad() {
// your code
}
override func viewDidAppear(_ animated: Bool) {
// your code
}
// MARK: - Photo Handling
func takePhoto(_ sender: Any) {
// your code
}
func editPhoto() {
// your code
}in XCode 11, you'll see a horizontal line drawn across the screen to visually enforce this grouping, and you'll see the MARK phrases ("LifeCycle", "Photo Handling", and so forth) in large type in the Minimap.hope this helps,DMG
hi,i'm still working to get up to speed on SwiftUI, but i'd say that when the body of the ContentView is evaluated, that's when the random number is computed as the value for the parameter in NextView.segueing (if i'm allowed to say that in SwiftUI) to SecondView from the "without binding" link and then coming back to ContentView does not have any obvious effect on the body of ContentView, since no state variable has been changed. nothing triggers a recomputation of the random number.it's curious that the "with binding" link causes this, since showEdit has not been changed, but that's for someone else to think about. it's probably just following a rubric to be sure to reevaluate anything that depends on a @State variable, in case it's been changed underneath it (it's a struct, not a class that's an ObservableObject)nevertheless, my suggestion would be: use the .onAppear() modifier to create a new random number and setting a @State variable to its result. something like this:struct ContentView: View {
@State private var nextInteger: Int = 0
func newRandomInteger() {
nextInteger = Int.random(in: 0 ... 9)
}
var body: some View {
NavigationView {
List {
NavigationLink(destination: NextView(n: nextInteger)) {
Text("List Item")
}
.onAppear(perform: newRandomInteger)
}
}
}
}i think this works.hope this helps,DMG
hi mr.bodich,i'm not sure this is on point, but if you want all the tableViewCells in a given section to have the same background color, then just assign that background color to each cell.if say you have just two sections (0, 1), colored green and red, then use something like this:let sectionColors: [UIColor] = [.green, .red]
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "some cell identifier", for: indexPath)
// set up the cell with whatever data it needs
cell.backgroundColor = sectionColors[indexPath.section]
return cell
}if you also want to tweak the color in the section header for each section, then supply a custom header with something like this:(1) in your viewDidLoad() function, include a definition of what you use for a class for the section headers. (i usually define this using its own .xib)tableView.register(MyTableViewSectionHeaderCell.nib, forHeaderFooterViewReuseIdentifier: TableViewSectionHeaderCell.identifier)(2) and then override viewForHeaderInSection:override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
guard let cell = tableView.dequeueReusableHeaderFooterView(withIdentifier: TableViewSectionHeaderCell.identifier) as? TableViewSectionHeaderCell else {
return nil
}
// set the title and whatever else
cell.backgroundColor = sectionColors[section]
return cell
}i don't wish to be too cryptic about the last few lines above, since you'll have to set up an .xib with a UITableViewCell, so go back to take a look at this related discussion: How to design a table header in IBas for that last section's extra space at the bottom, just set tableView.sectionFooterHeight = 0.0 in viewDidLoad.hope that helps,DMG
hi,if all you want is a "did it succeed" indication, you could use something like this, returning a boolean that indicates whether everything worked:func updateData() -> Bool {
var success = false
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest = NSFetchRequest.init(entityName: "User")
fetchRequest.predicate = NSPredicate(format: "username = %@", "John")
do {
let test = try managedContext.fetch(fetchRequest)
if test.count == 1 {
let objectUpdate = test[0] as! NSManagedObject
objectUpdate.setValue("John2", forKey: "username")
objectUpdate.setValue("John2@example.com", forKey: "email")
objectUpdate.setValue("newPassword", forKey: "password")
appDelegate.saveContext() // look in AppDelegate.swift for this function
success = true
}
} catch {
print(error)
}
return success
}check the return value of updateData() to issue your success message:if updateData() {
print ("I have found the name and I have changed")
} else {
print ("I could not find the name you are looking for")
}there are many variations on how to use what's above -- it's not the best code, it may not generalize well to whatever you specifically are doing, and the appDelegate may throw/catch an error in saveContext() that would not be recognized in this code.but then, consider: your code would be cleaner and less prone to error if you used what XCode generates for you for whatever class of managed object you defined in the .xcdatamodeld file. if, for example, the NSManagedObject you're working with was defined as an entity named User -- with attributes named username, email, and password (all String types) -- this code should work (i think -- i did not test, but it mirrors code i have in a current project)func updateData() -> Bool {
var success = false
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return success }
let managedContext = appDelegate.persistentContainer.viewContext
let fetchRequest: NSFetchRequest<User> = User.fetchRequest() // this provided by XCode code generation
fetchRequest.predicate = NSPredicate(format: "username = %@", "John")
do {
let test = try managedContext.fetch(fetchRequest)
if test.count == 1 {
let userToUpdate = test[0]
userToUpdate.username = "John2"
userToUpdate.email = "John2@example.com"
userToUpdate.password = "newPassword"
appDelegate.saveContext() // look in AppDelegate.swift for this function
success = true
}
} catch {
print(error)
}
return success
}hope that helps,DMG** EDIT A DAY LATER: line 5 above was updated to provide specificity for the type of fetchRequest as NSFetchRequest<User>.
hi,i think the code you wrote looks workable -- although you did not mention any error or say exactly what it is you want to do that the current code doesn't appear do for you. unless, of course, you're simply asking what is the value of objectUpdate in line 9 and you want to check that new values have been assigned to its attributes -- and you might just print(objectUpdate.username) before line 10 and again after line 10.nevertheless, i'd suggest you might want to go over to Youtube.com and search for "Stanford CS193P 2017" watch the episodes Lecture 10 (Core Data) and Lecture 11 (Core Data Demo). i think you'll find those very helpful at this stage (even though this was in 2017, everything you'll see pretty much works as is today).hope that helps,DMG
hi,i would suggest two things:(1) use the default setting of Codgen to Class Definition in the xcdatamodeld file (set for each entity, look in the right-most tab of the Inspector pane). this generates the (empty) Class definition and the extensions that define the attributes as class variables.-- if you need to attach your own functions or computed properties to a Core Data class, just add a new file to extend the class.(2) in XCode, use the Product menu, with the menu on-screen, hold down the Option key, and select Clean Build folder. (i think this is Option-Shift-Command K.) it would not hurt at this point to quit and then restart XCode.it takes a little bit to get everything in-sync with the Core Data model during compiles -- for example, the Core Data class definitions have to be generated as Swift files before anything that uses them gets compiled. and i've had occasional troubles with XCode (current version 11.3.1) getting it to take changes in the model right away and "forget" other attributes or entities that have changed.hope that helps,DMG
hi,FWIW: i'm convinced that the problem you're having is with your Core Data definitions and not with any code that i've suggested.to push this forward, i created a little project that adds entities of one type to Core data having a date attribute. it can then can show you what's been added to Core Data (so you can see how to retrieve data).you can find it at: bitbucket.org/delawaremathguy/narcisfromgironahope that helps,DMG
hi,you're having trouble with my suggestion of:contingut.setValue(Date(), forKey: "dateCreated")i'm puzzled as to why this has a problem -- but you're not showing the error message that's generated. that might help the diagnoosis.on the other hand, i have a working app where an entity named Session has a attribute named date that's defined in the object model as a Date, which comes across in code generation for the class Session as:@NSManaged public var date: Date?i don't usually move values with setValue(forKey:), but instead i prefer to assign a date value to a variable "session" of type Session with (something like) thislet date = Date()
session.date = dateperhaps this alternative syntax will work, or at least provide a better error message for you.hope that helps,DMG
hi,thanks for updating your code ... and so i'd offer these suggestions:(1) your today1 function probably should not print the date (unless you'd like to rename it as "printTheDateRightNow()" or something), but return the date as a String. like this:func today1() -> String {
let date = Date()
let calendar = Calendar.current
let day = calendar.component(.day, from: date)
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date)
return "\(year)-\(month)-\(day)"
}note that this function is merely one of convenience on your part: it's not necessary to place the current date into Core Data.(2) replace line 19 to simply put the current Date into Core Data ascontingut.setValue(Date(), forKey: "dateCreated")(3) replace line 24 asprint (today1())again, this has no effect on what was moved into Core Data, but it does confirm for you that the save was successful.i think that should do it, although please understand that you are storing a "point in time" in Core Data, not simply a month-day-year combination.hope that helps,DMG
hi,i think someone in the forum can help with this, but we will need more information before going forward. and we'll need more code.some things to consider and/or answer:(1) there is no call to (execute) the function today1 anywhere in your code. did you ever call it, and was the current date ever printed? Note: line 23 does not call the today1 function -- it asks to print the function, not to invoke the function. for example, consider the following playground code:import Foundation
func today1() {
let date = Date()
let calendar = Calendar.current
let day = calendar.component(.day, from: date)
let month = calendar.component(.month, from: date)
let year = calendar.component(.year, from: date)
let avui = "\(year)-\(month)-\(day)"
print (avui)
}
print(today1)
today1()the output of line 11 is (Function), while the output of line 12 is 2020-3-4.(2) what is "today2" in line 19 of your code? is there another function somewhere in your code that you have not shown? if so, what is it, and why would you try to save a string whose content is a function (?) as an NSDate/Date?(3) do you have a class/type definition for "contingut" in line 18? is this a generic NSManagedObject, or is it a variable of class "Contingut" that's defined in a Core Data Data Model?hope to hear more on this and that we can help,DMG
hi,i'm somewhat playing with this right now in a current project, where i have an NSOrderedSet supporting a one-to-many relationship. in my case, it's a high-res image, then one or more thumbnail images for various sizes. so the data for a "Player," say, is defined in the Core Data model, with an relationship "images," a one-to-many with a type "ImageData," where ImageData is really just the data for one image.Player has this generated variablevar images: NSOrderedSet?i can add a new Player with this type of code:let player = Player(context: managedObjectContext)
// fill in some fields
let imageData = ImageData(context: managedObjectContext)
// get some data for the hi-res image
imageData.data = data
player.addToImages(imageData)
// repeat for other images such as thumbnails
let imageData1 = ImageData(context: managedObjectContext)
imageData1.data = data
player.addToImages(imageData1)
// keep on goingi can later retrieve the data using computed properties on Player such as var photo: UIImage? {
get {
if images!.count == 0 {
return nil
}
let imageData = images![0] as! ImageData // edited after the fact
return UIImage(data: imageData.data!)
}
}or var thumbnail: UIImage? {
NSLog("Image count is \(images!.count)")
if images!.count >= 2,
let imageData = images![1] as? ImageData { // edited after the fact
return UIImage(data: imageData.data!)
}
return nil
}this seems to be working for me (although i too struggled a little with this early on in the process).when you swap two positions in your CollectionView, you'd have to use other accessors to reorder the NSOrderedSet (don't do it by hand, use the accessors); but, full disclosure, i haven't done this since my data has fixed positions in the NSOrderedSet that are set upon creationhope that helps,DMG