I have a problem with getting all dictionary keys.
When I call .keys, I get error as follows.
And I don't know how to solve it.
Thanks.
error:
Value of optional type 'LazyMapCollection<Dictionary<String, ChatroomMember>, String>?' not unwrapped; did you mean to use '!' or '?'?
class Chatroom:Model {
var id:String = ""
var members:[String:ChatroomMember] = [String:ChatroomMember]()
}
class ChatroomMember:Model {
var id:String = ""
}
class ViewController: UIViewController {
weak var chatroom:Chatroom?
override func viewDidLoad() {
var memberUsers:[String] = [String](chatroom?.members.keys) // get Error
}
}
Simply unwrap your chatroom:
var memberUsers:[String] = [String](chatroom!.members.keys)
Or (if chatroom has a chance to remain uninitialized) use optional if let unwrap:
var memberUsers:[String]
if let chatroom = chatroom {
memberUsers = Array(chatroom.members.keys)
} else {
memberUsers = []
}
Or if your entire app depends on presence of chatroom, guard it:
guard let chatroom = self.chatroom else {
// chatroom is not here!
return
}
var memberUsers = Array(chatroom.members.keys)
P.S. You can rely on Swift type inference and use Array(..) instead of [String](..) to cast lazy keys collection to normal array.
Related
I have a data model which I made for API returns, it is something like this:
struct VehicleData: Codable {
let _embedded: Embedded
}
struct Embedded: Codable {
let userVehicles: [UserVehicles]
}
struct UserVehicles: Codable {
let id: String
let images: [String]
let userId: String
let vehicle: Vehicle
let originalPrice: OriginalPrice
let hasBasicInsurance: Bool
}
I have used callback function to pass it to my ViewController, now I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance. basically, vehicleList?._embedded.userVehicles[i] = true
this is my function code to use the vehicle data in ViewController:
var vehicleManager = VehicleManager()
var vehicleList: VehicleData?
var i: Int = 0
#IBOutlet weak var tableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
vehicleManager.retrieveUserVehicle()
vehicleManager.onDataUpdate = { [weak self] (data: VehicleData) in
self?.useData(data: data)
}
tableView.dataSource = self
tableView.delegate = self
tableView.tableFooterView = UIView() //remove empty tableView cells
tableView.register(UINib(nibName: Constants.vehicleListCellNibName, bundle: nil), forCellReuseIdentifier: Constants.vehicleListToBeInsuredIdentifier)
}
func useData(data: VehicleData) {
vehicleList = data
// code below has issues....
for i in [vehicleList?._embedded.userVehicles] {
if let vechile = vehicleList?._embedded.userVehicles[i].hasBasicInsurance {
if vehicle == true {
i = i + 1
print(">>number of of insured vehidle: \(i)")
} else {
print(">>>number of of insured vehidle: \(i)")
}
}
}
}
Do you know how to fix it?
You need to supply a default value for optional as a good practise instead of force unwrap
for i in vehicleList?._embedded.userVehicles ?? [] { }
It's not clear from your code, but it looks like vehicleList is optional. It probably should not be (see Leo Dabus's comments). It is rare that it makes sense to have an optional array. That suggests there's some difference between an empty array and a missing array. If there is, then that's fine, but in most cases you should just use a non-optional array and make it empty.
Whether you fix that or not, the solution to this particular problem is to just use a non-optional value, and you have one: data. So change the loop to:
for i in data._embedded.userVehicles { ... }
From your updated question, you note "I want to get check in the useVehiclers list, how many vehicles hasBasicInsurance." It seems you want to put that value in i. If so, that would be:
func useData(data: VehicleData) {
vehicleList = data
i = data._embedded.userVehicles
.filter(\.hasBasicInsurance)
.count
}
You can also use for_each loop for this, for eg like this:
vehicleList?._embedded.userVehicles.for_each(x in /*Do your thing here*/)
I'm learning Swift from a Udemy tutorial that shows how to make a chat app using a Firebase database. For my own learning and as a quick reference guide, I typed the code in a single .swift file to get a quick overview of the entire app and practice debugging. However, I have one more compiler error saying that the constant 'messagesDB' doesn't have a member 'setValue'. I'm assuming that messagesDB being an instance of class 'Database' would have access to the setValue() instance method. What do you think I'm missing in order to silence this error? Does it have something to do with the way the functions are declared?
Both class Auth and Database are arbitrary classes to mimic Firebase, so that the rest of the code could be displayed without a bunch of errors, thus giving me a single file to see how things work.
class Auth {
var currentUser: String = ""
func auth() -> Self { return self }
}
class Database {
func setValue() -> Self { return self }
func database() -> Self { return self }
func reference() -> Self { return self }
func child(_ someString: String) -> String {
print(someString)
}
}
class ChatViewController: UIViewController {
#IBOutlet var messageTextfield: UITextField!
#IBOutlet var sendButton: UIButton!
#IBAction func sendPressed(_ sender: AnyObject) {
messageTextfield.endEditing(true)
messageTextfield.isEnabled = false
sendButton.isEnabled = false
let messagesDB = Database().database().reference().child("Messages")
let messageDictionary = ["Sender": Auth().auth().currentUser, "MessageBody": messageTextfield.text!] as [String : Any]
messagesDB.setValue(messageDictionary) { //ERROR: Value of type 'String' has no member 'setValue'
(error, reference) in
if error != nil {
print(error!)
} else {
print("Message saved successfully!")
self.messageTextfield.isEnabled = true
self.sendButton.isEnabled = true
self.messageTextfield.text = ""
}
}
}
}
It's easy, your messageDB constant it's being set equals to the function "child" that returns a string, that's why it's telling you that "string" doesn't has no member setValue, so you should just set your messageDB to Database().database().reference()
the child method returns a String and not a Database instance, hence the error message.
create the database instance first and then use it where needed:
let messagesDB = Database()
.
.
messagesDB.setValue(...
I am making a rhythm app, but I can't seem to randomize the circles. Here is my code:
var alternator = 0
var fallTimer:NSTimer?
var flag:Bool = true
let circleIndexes = (0..<5).map { return NSNumber(integer: $0) }
let randomIndexes = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(circleIndexes) as! [Int]
func fallCircleWrapper() {
if (flag == true) {
self.alternator += 1
} else {
self.alternator -= 1
}
if (self.alternator == 0) {
flag = true
} else if (self.alternator == 5) {
flag = false
}
self.hitAreaArray[randomIndexes[self.alternator]].emitNote(self.texture!)
}
The problem is with this line:
let randomIndexes = GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(circleIndexes) as! [Int]
which gives the error "Instance member 'circleIndexes' cannot be used on type 'GameScene'". How should I go about fixing this?
Consider this example that reproduces your error on a simpler scale:
func agePlusOne(_ num: Int) -> Int { return num + 1 }
class Cat {
var age = 5
var addedAge = agePlusOne(age) // ERROR!
}
You're trying to use a property /before/ it has been initialized... that is, before init() has finished, thus giving you a self to work with.
A way around this is to use lazy properties... lazy properties are initialized only when first called by an object.. that is, they are properly initialized only after there is a self available.
private(set) lazy var randomIndices: [Int] = {
GKRandomSource.sharedRandom().arrayByShufflingObjectsInArray(self.circleIndexes) as! [Int]
}()
computed and static properties are also lazy by default, without needing to use the keyword. Unfortunately, you cannot use lazy let, so the private(set) was added to give you more immutability, but not total.
PS, this is Swift3 so you may need to convert it to Swift2.
A Realm object:
class Dog: Object {
dynamic var name = ""
dynamic var age = 0
}
Calling results:
let results = realm.objects(Dog)
Or doing it this way:
let type = Dog.self
let results = realm.objects(type)
I want to be able to do it by passing into a method like this:
class SomeClass {
func callRealm(type: AnyObject) {
let results = realm.objects(type)
}
}
let someClass = SomeClass()
let type = Dog.self
someClass.callRealm(type)
How would I do this? I've had no luck with generics, although I think this might be the way to go.
Your function callRealm should take input as AnyClass instead of AnyObject.
func callRealm(type: AnyClass) {
let results = realm.objects(type)
}
I am trying to change my UIWebView Url depending on the variables I get from my Parse.com database.
First, I split the three worded String into three parts then I place the parts into the url.
However, I am getting an error! It is very strange:
Here is the code incase you are unable to see it:
import UIKit
import Parse
import ParseUI
class myBookingsItemTableViewController: UITableViewController {
var object: PFObject!
#IBOutlet weak var typeOfBookingLabel: UILabel!
#IBOutlet weak var typeOfBookingQRCode: UIWebView!
var ticketId = String()
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
if (self.object != nil) {
self.typeOfBookingLabel?.text = self.object["booking"] as? String
var ticketID = self.object["ticketId"] as? String
self.ticketId = ticketID!
var ticketIdArr = split(ticketId) {$0 == " "}
var first: String = ticketIdArr[0]
var second: String? = ticketIdArr.count > 1 ? ticketIdArr[1] : nil
var third: String? = ticketIdArr.count > 2 ? ticketIdArr[2] : nil
let url = NSURL (string: "http://chart.apis.google.com/chart?chl=\(first)+\(second)+\(third)&chs=200x200&cht=qr&chld=H%7C0")
let requestObj = NSURLRequest(URL: url!)
typeOfBookingQRCode.loadRequest(requestObj)
} else {
self.object = PFObject(className: "Bookings")
}
}
}
You have to make sure that your first, second and third do not contain any whitespaces - otherwise you will not be able to create a URL from it - it will return nil and your unwrapping fails.
You can do that using
first = first.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
Additionally I would recommend against using nil in your situation but rather either exclude the second and third argument if they would be nil or replace the nil with an actual value - that way you can change their type to String and do not have to worry about the optionals any more.
The following code snippet escapes all three values and always generates an URL:
func urlEncode(par:String!) -> String! {
return par.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet())!
}
var first: String = urlEncode(ticketIdArr[0])
var second: String = ticketIdArr.count > 1 ? urlEncode(ticketIdArr[1]) : "nil"
var third: String = ticketIdArr.count > 2 ? urlEncode(ticketIdArr[2]) : "nil"