I was build an IOS application, and somehow I have to do an aggregation in swift class, but every time I want to get the data, it always return an error. it seems like I the data always return a nil result.
I want to push the DoctorList member object (schedule) in the view controller class
I have create the object, and I also called the init() function. but, however, when I push the (or call) the init function for the DoctorList class and pass the array of ScheduleList, the content of the schedule member in doctorlist class will always be empty.
so when I try to get the schedule, it will return a nil result.
can every one tell me what I did wrong, because I already change the code but it still give a nil result.
I have two class like this
class DoctorList: NSObject {
var DoctorID: String?
var DoctorName: String?
var SpecialtyID: String?
var SpecialtyName: String?
var ImageURL: String?
var Schedule: [ScheduleList]?
init(_ DoctorID:String, _ DoctorName:String, _ SpecialtyID:String, _ SpecialtyName:String, _ ImageUrl:String, _ Schedule:[ScheduleList] ){
self.DoctorID = DoctorID
self.DoctorName = DoctorName
self.SpecialtyID = SpecialtyID
self.SpecialtyName = SpecialtyName
self.ImageURL = ImageUrl
for sc in Schedule {
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
}
var getSchedule: [ScheduleList] {
get {
return self.Schedule!
}
}
and this one
class ScheduleList: NSObject {
var DoctorID: String?
var DayName: String?
var FirstHour: String?
var LastHour: String?
init(_ DoctorID:String, _ DayName:String, _ FirstHour:String, _ LastHour:String ){
self.DoctorID = DoctorID
self.DayName = DayName
self.FirstHour = FirstHour
self.LastHour = LastHour
}
the return value for the schedule was always empty
I'm sorry, could anyone give a suggestion how to make a global variable in swift?
You haven't initialized the Schedule array.
The append statement in the loop just never execute:
for sc in Schedule {
// self.Schedule is nil so anything come after the question mark does not run
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
To fix it initialize your array before use:
self.Schedule = [ScheduleList]()
for sc in Schedule {
self.Schedule?.append(ScheduleList(sc.DoctorID!, sc.DayName!, sc.FirstHour!, sc.LastHour!))
}
Also, your code is a pain to read:
Optionals everywhere! You should decide what properties can and cannot be nil and get rid of the unnecessary ? and !
The convention in Swift is lowerCamelCase for variable names, and CamelCase for class names
No need to inherit from NSObject unless you want something from the ObjC world, whether working with ObjC or use KVO
Just noticed, recommendations from #CodeDifferent should be appropriate and helpful.
It is because you haven't initialised the Schedule in DoctorList
init(_ DoctorID:String, _ DoctorName:String, _ SpecialtyID:String, _ SpecialtyName:String, _ ImageUrl:String, _ Schedule:[ScheduleList] ){
self.DoctorID = DoctorID
self.DoctorName = DoctorName
self.SpecialtyID = SpecialtyID
self.SpecialtyName = SpecialtyName
self.ImageURL = ImageUrl
// Use this for init an empty array and append the content
self.Schedule = [ScheduleList]()
self.Schedule?.append(contentsOf: Schedule)
}
An example of the result:
let schedule = ScheduleList("scheduleId", "name", "1st", "last")
let doctorList = DoctorList("docId", "docName", "specId", "scpecName", "imgURL", [schedule])
if let list = doctorList.Schedule {
print("\(list[0].DoctorID)")
}
Related
I have the following 2 class / structs:
class ConversationDetails {
var messages: [ChatMessage]?
var participants: [User]?
}
class User: Codable {
init (email: String) {
self.email = email
}
// system
var id: String?
var verifiedaccount: Int?
var rejected: Int?
...
}
I've further got the var conversationDetails = ConversationDetails () and I'm populating it with an API call. That all works fine.
I'd like to map the participants array inconversationDetailsand access the id property of each participant like so:
let recipient_ids = self.conversationDetails.participants.map( { (participant) -> String in
return participant.id
})
In my understanding, map iterates over the entire participants array, which is an array of User objects and I can access each item via participant.
However, I get Value of type '[User]' has no member 'id' for return participant.id.
Where is my misunderstanding?
Your participants var is optional, so you need to add question mark to access array. Without it, you try to call map on optional.
This is working code:
let recipientIds = conversationDetails.participants?.map( { (participant) -> String in
return participant.id
})
or shorter:
let recipientIds = conversationDetails.participants?.map { $0.id }
Also, you can use compactMap to remove nils from recipientIds array and have [String] array instead of [String?]:
let recipientIds = conversationDetails.participants?.compactMap { $0.id }
The first problem is that participants is optional so you need to add a ? when accessing it
conversationDetails.participants?
then when mapping you should use compactMap since id is also an optional property
let recipient_ids = conversationDetails.participants?.compactMap { $0.id }
Another variant is to not have an optional array but instead initialize it to an empty array. This is actually a much better way to handle collection properties because you can have a cleaner code by initializing them to an empty collection
var participants = [User]()
and then do
let recipient_ids = conversationDetails.participants.compactMap { $0.id }
class Employee{
var id:Int
var name:String
var salary:Int
init(){
self.id=0
self.name=""
self.salary=0
}
func getInfo(){
self.name=readLine()!
self.id=Int(readLine()!)!
self.salary=Int(readLine()!)!
}
}
var count=0
var flag="y"
var empData:[Employee]=[]
repeat{
count+=1
empData[count]=Employee()
empData[count].getInfo()
flag=readLine()!
}while(flag=="y") `
I have a class Employee with properties id , nam and salary. The function getInfo() is used to get information from user. I want to read data until the flag!="y" . I am getting index out of range error.
What is the right way of inputting data? Can we index the objects ?
You need to append to your array to make it increase in size. Replace
empData[count]=Employee()
with
empData.append(Employee())
to avoid index out of range error
Update
To make your code a little less horrible I would do
repeat {
var employee = Employee()
employee.getInfo()
empData.append(employee)
flag=readLine()!
}while( flag == "y" )
The subscript operator cannot be used to add elements to an array index which doesn't exist yet. You either need to initialize the array with an element count if you know at the time of initialization how many elements your array will have or use the append operator to add new elements to the array after the last index.
You don't even need the count variable, as you can simply access empData.last safely after calling append and adding a new Employee to the Array.
var flag="y"
var empData:[Employee]=[]
repeat {
empData.append(Employee())
empData.last!.getInfo()
flag=readLine()!
} while(flag=="y")
I would advise you to seriously reconsider your implementation as it is really unsafe at the moment. You are not validating user input in any way, hence your getInfo function can easily cause runtime errors if the user input is not in the expected form. Moreover, creating an empty initializer for Employee doesn't make sense, you could simply create a failable initializer, where you read the input and if the input is not of the correct form, make the initializer return nil.
class Employee{
let id:Int
let name:String
let salary:Int
init?(){
guard let name = readLine() else { return nil }
self.name = name
guard let idString = readLine(), let id = Int(idString) else { return nil }
self.id = id
guard let salaryString = readLine(), let salary = Int(salaryString) else { return nil}
self.salary = salary
}
}
var flag="y"
var empData:[Employee]=[]
repeat {
if let employee = Employee() {
empData.append(employee)
} else {
// Display error message to the user
}
flag=readLine() ?? ""
} while(flag=="y")
According to Swift documentation:
Optional binding can be used with if and while statements to check for a value inside an optional, and to extract that value into a constant or variable, as part of a single action.
The documentation only shows an example of optional-binding using if statement like:
if let constantName = someOptional {
statements
}
I'm looking for an example of optional-binding using while loop?
It's the same
while let someValue = someOptional
{
doSomethingThatmightAffectSomeOptional(with: someValue)
}
Here is a concrete example of iterating a linked list.
class ListNode
{
var value: String
var next: ListNode?
init(_ value: String, _ tail: ListNode?)
{
self.value = value
self.next = tail
}
}
let list = ListNode("foo", ListNode("bar", nil))
var currentNode: ListNode? = list
while let thisNode = currentNode
{
print(thisNode.value)
currentNode = thisNode.next
}
// prints foo and then bar and then stops
I think the advantage of using while let ... is infinite loop that check some variable for any changes. But it's weird. For this kind of work you should use didSet. Or other good example is List data structure. Anyway, here is the example:
var value: Int? = 2
while let value = value {
print(value) // 2
break
}
So I am trying to get the Actual Variable Name as String in Swift, but have not found a way to do so... or maybe I am looking at this problem and solution in a bad angle.
So this is basically what I want to do:
var appId: String? = nil
//This is true, since appId is actually the name of the var appId
if( appId.getVarName = "appId"){
appId = "CommandoFurball"
}
Unfortunately I have not been able to find in apple docs anything that is close to this but this:
varobj.self or reflect(var).summary
however, this gives information of what is inside the variable itself or the type of the variable in this case being String and I want the Actual name of the Variable.
This is officially supported in Swift 3 using #keyPath()
https://github.com/apple/swift-evolution/blob/master/proposals/0062-objc-keypaths.md
Example usage would look like:
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Wendy")
In Swift 4 we have something even better: \KeyPath notation
https://github.com/apple/swift-evolution/blob/master/proposals/0161-key-paths.md
NSPredicate(format: "%K == %#", \Person.mother.firstName, "Wendy")
// or
let keyPath = \Person.mother.firstName
NSPredicate(format: "%K == %#", keyPath, "Andrew")
The shorthand is a welcome addition, and being able to reference keypaths from a variable is extremely powerful
As per the updated from this answer, it is supported in Swift 3 via #keyPath
NSPredicate(format: "%K == %#", #keyPath(Person.firstName), "Andrew")
This is my solution
class Test {
var name: String = "Ido"
var lastName: String = "Cohen"
}
let t = Test()
let mirror = Mirror(reflecting: t)
for child in mirror.children {
print(child.label ?? "")
}
print will be
name
lastName
This works:
struct s {
var x:Int = 1
var y:Int = 2
var z:Int = 3
}
var xyz = s()
let m = Mirror(reflecting: xyz)
print(m.description)
print(m.children.count)
for p in m.children {
print(p.label as Any)
}
I've come up with a swift solution, however unfortunately it doesn't work with Ints, Floats, and Doubles I believe.
func propertyNameFor(inout item : AnyObject) -> String{
let listMemAdd = unsafeAddressOf(item)
let propertyName = Mirror(reflecting: self).children.filter { (child: (label: String?, value: Any)) -> Bool in
if let value = child.value as? AnyObject {
return listMemAdd == unsafeAddressOf(value)
}
return false
}.flatMap {
return $0.label!
}.first ?? ""
return propertyName
}
var mutableObject : AnyObject = object
let propertyName = MyClass().propertyNameFor(&mutableObject)
It compares memory addresses for an object's properties and sees if any match.
The reason it doesn't work for Ints, Floats, and Doubles because they're not of type anyobject, although you can pass them as anyobject, when you do so they get converted to NSNumbers. therefore the memory address changes. they talk about it here.
For my app, it didn't hinder me at all because I only needed it for custom classes. So maybe someone will find this useful. If anyone can make this work with the other datatypes then that would be pretty cool.
Completing the accepted answer for extensions:
The property needs to be #objc.
var appId: String? {
....
}
You need to use #keyPath syntax, \ notation is not supported yet for extensions.
#keyPath(YourClass.appId)
The best solution is Here
From given link
import Foundation
extension NSObject {
//
// Retrieves an array of property names found on the current object
// using Objective-C runtime functions for introspection:
// https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtPropertyIntrospection.html
//
func propertyNames() -> Array<String> {
var results: Array<String> = [];
// retrieve the properties via the class_copyPropertyList function
var count: UInt32 = 0;
var myClass: AnyClass = self.classForCoder;
var properties = class_copyPropertyList(myClass, &count);
// iterate each objc_property_t struct
for var i: UInt32 = 0; i < count; i++ {
var property = properties[Int(i)];
// retrieve the property name by calling property_getName function
var cname = property_getName(property);
// covert the c string into a Swift string
var name = String.fromCString(cname);
results.append(name!);
}
// release objc_property_t structs
free(properties);
return results;
}
}
Inside the optional binding when I assign the variable ammo (and ammo2) I am pretty sure that I should be using ! to unbox the optional, but on my first attempt I put ? by mistake and was a little confused why it still worked, can anyone cast some light onto whats going on there?
let soldierA = Soldier(name: "Brian")
soldierA.weapon = Weapon()
soldierA.weapon!.grenadeLauncher = GrenadeLauncher()
let soldierB = Soldier(name: "Gavin")
soldierB.weapon = Weapon()
let soldierC = Soldier(name: "Berty")
soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234
let missionTeam = [soldierA, soldierB, soldierC]
for eachSoldier in missionTeam {
if let launcherAvailable = eachSoldier.weapon?.grenadeLauncher? {
var ammo = eachSoldier.weapon!.grenadeLauncher!.ammo // PRETTY SURE THIS IS RIGHT
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo // SHOULD THIS WORK, IT DOES?
println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo)")
} else {
println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
}
}
.
// CLASSES
class Soldier {
var name: String
var weapon: Weapon?
init(name: String) {
self.name = name
}
}
class Weapon {
var ammo = 500
var grenadeLauncher: GrenadeLauncher?
}
class GrenadeLauncher {
var ammo = 20
}
EDIT
Thank you, I was getting confused about how this works, but I now see what is happening. Here is the modified eachSoldier section again, using optional binding with optional chaining...
for eachSoldier in missionTeam {
if let weapon = eachSoldier.weapon? {
if let launcher = eachSoldier.weapon?.grenadeLauncher? {
println("SOLDIER: \(eachSoldier.name) Weapon has launcher with \(launcher.ammo) ammo")
} else {
println("SOLDIER: \(eachSoldier.name) Weapon does not have launcher ")
}
} else {
println("SOLDIER: \(eachSoldier.name) does not have weapon ")
}
}
soldierC.weapon = Weapon()
soldierC.weapon!.grenadeLauncher = GrenadeLauncher()
soldierC.weapon!.grenadeLauncher!.ammo = 234
it is correct in the current pattern.
var ammo = eachSoldier.weapon!.grenadeLauncher!.ammo
implicitly unwraps the weapon and its grenadeLauncher; it does not care of whether or not they have been inited before, therefore it could lead a direct crash if your code tries to unwrap when any of them is still a nil value.
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo
tries to access the weapon and its grenadeLauncher; if the object does not exist, they will be left alone, therefore nothing happens but the ammo2 will be nil only, and application can proceed.
therefore your flow could be similar to that:
for eachSoldier in missionTeam {
var ammo2 = eachSoldier.weapon?.grenadeLauncher?.ammo
if ammo2 != nil {
println("SOLDIER: \(eachSoldier.name), Weapon has launcher AMMO: \(ammo2)")
} else {
println("SOLDIER: \(eachSoldier.name), Weapon does not have launcher ")
}
}
In addition to what #holex has stated, I would like to say that your case called Optional Chaining, in which if you use ? instead of ! on an optional variable (or constant), that means you are checking if the variable (or the constant) is not nil. In other words, it has a value.
The lovely thing about optional chaining is that you can apply it to many levels.
For example:
Let's say you have these two classes:
class Student{
var subjects: [Subject]?
}
class Subject{
var name: String?
}
and you created a variable:
var william = Student()
At any time, you can print the name of the first subject as this:
print(william.subjects?[0].name)
Notice that the result of that print statement is nil, while if you unwrapped it like this:
print(william.subjects![0].name)
You would get a run time error