I see examples of variable initialisation code that can contain any number of statements, for example:
var _globalCounter = 0
let instanceCount: Int = { _globalCounter++ }()
print(instanceCount.dynamicType) // Int
and a full code sample:
struct User {
let name: String
let company: String
let login: String
let password: String
static let database: Dictionary<String, User> = {
var theDatabase = Dictionary<String, User>()
for user in [
User(name: "John Appleseed", company: "Apple", login: "japple", password: "foo"),
User(name: "John Hennessy", company: "Stanford", login: "hennessy", password: "foo"),
User(name: "Bad Guy", company: "Criminals, Inc.", login: "baddie", password: "foo")
] {
theDatabase[user.login] = user
}
return theDatabase
}()
}
What's going on? Is it assignment from a closure like assigning a value from a function call?
These samples are copied form cs 193p fall 2014 class, are these considered to be good practice in terms of complex variable initialisation compared to putting stuff in init?
Please note that here in the second example the variable is static, so effectively this is a thread safe initialisation code?
I'm #learning swift and #confused.
Yes, a closure is just a function. Like any other function, you call it by passing in the arguments inside parentheses. If it is a function that takes no arguments, then you call it with empty parentheses.
let one: Int = { return 1 }()
It's actually as simple as that :)
Related
Say I have a User class with three properties: name, email, and fruits. What's the most efficient way to determine the differences, and print them out in a dictionary alongside the property name?:
struct User: Equatable {
let name: String
let email: String
let fruits: [Fruit]
}
// Old user
let user = User(name: "Jane", email:"ja#ne.com", fruits: [.banana, .apple])
// Updated user
let updatedUser = User(name: user.name, email: user.email, fruits: [.apple, .peach])
// Looking for help writing a function that can efficiently find the changes + format them into a dictionary for Firebase etc:
let updatedProperties = updatesBetween(old: user, new: updatedUser)
// Output:
//["fruits": ["apple", "peach"]]
Take advantage of the fact that these objects are Equatable and check for their equality right off the bat before proceeding, and then go property by property and look for differences. Keep in mind that arrays care about order so [.apple, .banana] will be different from [.banana, .apple]. If you don't care about order then just sort them both before comparing (this may require additional steps depending on the contents of the array). You can also consider translating the arrays into sets before comparing (if the arrays don't contain duplicates).
func getDifferencesBetween(old: User, new: User) -> [String: Any] {
guard old != new else {
return [:]
}
var differences: [String: Any] = [:]
if old.name != new.name {
differences["name"] = new.name
}
if old.email != new.email {
differences["email"] = new.email
}
if old.fruits != new.fruits {
differences["fruits"] = new.fruits
}
return differences
}
i'm new to coding in Swift, I have been looking around for some time for a solution to this issue and as of yet unable to find anything that solves my problem.
I am using a function to return some data from Firebase. After many errors and much research, I have managed to get the function 'working' without throwing errors, but the data that is returned is blank. Essentially I am trying to return multiple values and hold them in an array when I call the function.
The data is returning fine from Firebase, when I print out the variable just after setting it, it will print out correctly but when I do the same just before returning the function, it returns blank. If I try to return the function just after setting the data, I get the error "Unexpected non-void return value in void function".
Here is the full code:
func loadClientData(client_id: String) -> (address_1: String, address_2: String, city: String, company_name: String, contact_name: String, county: String, email: String, phone: String, postcode: String, country: String) {
let db = Firestore.firestore()
let userID : String = (Auth.auth().currentUser!.uid)
print("\(userID)/clients/existing/\(client_id)")
let docRef = db.collection("\(userID)/clients/existing/").document(client_id)
var address_1 = ""
var address_2 = ""
var city = ""
var company_name = ""
var contact_name = ""
var county = ""
var email = ""
var phone = ""
var postcode = ""
var country = ""
docRef.getDocument { (document, error) in
if let document = document, document.exists {
let data = document.data()
address_1 = data?["address_1"] as? String ?? ""
address_2 = data?["address_2"] as? String ?? ""
city = data?["city"] as? String ?? ""
company_name = data?["company_name"] as? String ?? ""
contact_name = data?["contact_name"] as? String ?? ""
county = data?["county"] as? String ?? ""
email = data?["email"] as? String ?? ""
phone = data?["phone"] as? String ?? ""
postcode = data?["postcode"] as? String ?? ""
country = data?["country"] as? String ?? ""
print("Company name is \(company_name)") // <---- THIS prints out the company name
} else {
print("client does not exist")
return
}
}
print("Company name is \(company_name)") // <---- THIS prints the company name as blank
return (address_1: address_1, address_2: address_2, city: city, company_name: company_name, contact_name: contact_name, county: county, email: email, phone: phone, postcode: postcode, country: country)
}
This is being called like so:
let companyInfo = loadClientData(client_id: self.items[indexPath.item].company)
print(companyInfo)
And prints out the following:
(address_1: "", address_2: "", city: "", company_name: "",
contact_name: "", county: "", email: "", phone: "", postcode: "",
country: "")
Thanks in advance for your input.
There are many questions on this site that are fundamentally the same as yours, and you should take a look at them. To avoid redundancy, I'll explain in simple terms, but I urge you to read up on similar questions.
In a nutshell, what's happening is that loadClientData fetches data asynchronously, meaning that it happens in the background while other stuff happens in the foreground. The foreground stuff includes your code that calls loadClientData and prints it out. The foreground stuff doesn't wait for the background stuff to finish.
You're getting nothing because the background stuff isn't finished processing by the time you print(companyInfo).
Real world example:
Your mom asks you to go buy a lemon for a dish she is cooking for dinner.
She starts cooking, but she has no lemon!
Why? Because you haven't yet returned from the supermarket, and your mom didn't wait.
If you want to make the foreground stuff wait, you have a lot of options, which you can consider by looking at other similar questions.
You need to refactor your function to not return anything, and take a completion handler. The completion handler is a block of code that the caller passes to your function. Your function will call the comletion handler once the results are available. This is a common coding pattern in Swift, and your firebase getDocument() call is written to use a completion handler.
Search on Swift completion handler design pattern to learn more.
**Note:
Using a completion handler is only one way to do this. You could also use the delegate design pattern, but completion handers are a more modern way of handling async events, and the way I would suggest handling your problem.
1.what I code
class hamburger {
var tomato: String
var patty: String
var bread: String
var number: Int
init(_ tomato: String, _ patty: String, _ bread: String, _ number: Int) {
self.tomato = tomato
self.patty = patty
self.bread = bread
self.number = number
}
init() {
self.tomato = "tomato"
self.patty = "patty"
self.bread = "bread"
self.number = 10
}
}
let sandwich = hamburger("texas" , "iii" , "iii" , 10)
print(hamburger.tomato)
2.error message
Playground execution failed:
error: dotinstall.playground:342:7: error: instance member 'tomato'
cannot be used on type 'hamburger'
print(hamburger.tomato)
^~~~~~~~~ ~~~~~~
3.The sample I followed
enter code here// Class
class User {
let name: String // property
var score: Int
init(name: String, score: Int) {
init(_ name: String, _ score: Int) {
self.name = name
self.score = score
}
init() {
self.name = "bob"
self.score = 30
}
}
//let tom = User(name: "tom", score: 23)
let tom = User("tom", 23)
print(tom.name)
print(tom.score)
let bob = User()
print(bob.name)
print(bob.score)
I have coded like 1 following 3, but I got a error message like 2.
what I did to solve
・anyway follow this sample to be like same
・studied a basic of class syntax, initializer, instance on website
・was looking for a mistypes
・I checked the order of property
I don't why it is not worked even if I just follow the sample code.
please give me tips on the solution.
thanks
You're making a mistake regarding object oriented programming. With hamburger.tomato you try to access the property tomato of the Class Hamburger, not the Object, which is sandwich here. So the solution would be:
print(sandwich.tomato)
In the future, what you might want to do is take a look at styling your code better. Classes(Hamburger) are written starting with an uppercased letter, while the objects, or instances(sandwich), of the Classes are written starting with a lowercased letter.
I'm having trouble coding an apparently simple task. I want to add new client profile data to a client profile dictionary (clientDatabase) but keep getting errors - can't seem to append - error: value of type '(String: clientProfile)' has no member 'append' (see error at bottom of code)
Any insights you can provide are greatly appreciated.
Thanks,
Bill
//: Playground - noun: a place where people can play
import UIKit
import Foundation
/*
code copied from B-C Dev Database - structs but simplified with fewer variables
goal is to getappend new client to work.
*/
/*
Globals: these go in main.swift file
*/
struct clientProfile {
var firstName: String = ""
var lastName: String = ""
var group: Int = 0
}
//var clientDatabase : NSMutableDictionary! = [String:clientProfile]()
var clientDatabase:[String:clientProfile]
/* sample data template: phone is key, sub array is;
(firstName: "", lastName: "",pilatesGroup: )
*/
clientDatabase = [
"1234567": clientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": clientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": clientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": clientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
clientDatabase.count
// so far so good
/*
add new client
declare local variables in scene swift files where used
*/
var firstName: String = ""
var phone:String = ""
var newPhone: String = ""
var newFirstName: String = ""
var newLastName: String = ""
var newGroup: Int = 0
// define struct using these input variables for values but with same keys as (global) clientDatabase
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
// input values from scene - UITextFields
newPhone = "4567890"
newFirstName = "Super"
newLastName = "Dave"
newGroup = 14
// test that all values are where they should be
clientDatabase
clientDatabase.count
newClientDatabase = [newPhone:newClientProfile()]
newClientDatabase.count
// ok so far
//the following line returns an error
clientDatabase.append(newClientDatabase)
// can't seem to append - error value of type '(String: clientProfile)' has no member 'append'
Two things. First of all clientDatabase is a dictionary which doesn't have append, instead you'll have to iterate through the other dictionary and insert its elements into clientDatabase.
The other issue is that clientDatabase and newClientDatabase aren't the same type. The first one is [String : clientProfile] and the second is [String : newClientProfile]. You'll have to convert the values from one type to the other to combine the dictionaries.
Looking deeper into the code there some misunderstandings about the language. For example:
struct newClientProfile {
var firstName: String = newFirstName
var lastName: String = newLastName
var group: Int = newGroup
}
// put newClientProfile into newClientDictionary
var newClientDatabase:Dictionary = [String:newClientProfile]()
You're creating a struct just for the purpose of containing a single set of values when you already have clientProfile. Instead you could do:
var newClientProfile = clientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
This will create a variable which is an instance of clientProfile and stores the information you want. However, you have the other variables defined as empty values.
Here's a cleaned up version of your code, take a look at it and let me know if you have any questions.
struct ClientProfile { // Convention is to use initial caps for enum, struct, class
let firstName: String
let lastName: String
let group: Int
}
var clientDatabase = [
"1234567": ClientProfile(firstName: "Sally", lastName: "Sillious", group: 3),
"2345678": ClientProfile(firstName: "Sue", lastName: "Parker",group: 8),
"3456789": ClientProfile(firstName: "Bob", lastName: "Parker", group: 2),
"5678901": ClientProfile(firstName: "Jim", lastName: "Beam", group: 12)
]
// input values from scene - UITextFields
let newPhone = "4567890"
let newFirstName = "Super"
let newLastName = "Dave"
let newGroup = 14
// define struct using these input variables for values but with same keys as (global) clientDatabase
let newClientProfile = ClientProfile(firstName: newFirstName, lastName: newLastName, group: newGroup)
let newClientDatabase = [newPhone:newClientProfile]
for (phone,client) in newClientDatabase {
clientDatabase[phone] = client
}
Sorry about the last thread where I posted this question by accident! Here it is. I received a response from someone (Their response is at bottom of page) and unfortunately it did not work. Thanks in advance!
Create a variable named result and assign it the tuple returned from function greeting. (Note: pass the string "Tom" to the greeting function.)
func greeting() -> (language: String, greeting: String, name: String) {
let language = "English"
let greeting = "Hello"
let name = "Tom"
return (language, greeting, name)
}
var result = greeting()
Error = Your function needs to return a tuple with elements named 'greeting' and 'language'.
Now this solution below tells me that my variable has the wrong value in it, but I can't figure out how to pass it "Tom" to the greeting function because I keep getting an error.
func greeting() -> (language: String, greeting: String) {
let language = "English"
let greeting = "Hello"
return (language, greeting)
}
var result = greeting("Tom")
Response I got in the last thread is below. I still get the error message saying that I need to assign the return value of greeting to result.
func greeting(name:String) -> (language: String, greeting: String) {
let language = "English"
let greeting = "Hello " + name + "!"
return (language, greeting)
}
var result = greeting("Tom").greeting
The task is
Create a variable named result and assign it the tuple returned from function greeting.
What you do in the last block is
var result = greeting("Tom").greeting
what I would expect you to do is
var result = greeting("Tom")