How to use Swift struct implicit initializer? - swift

Here is the code I have:
struct Algorithm {
let category: String = ""
let title: String = ""
let fileName: String = ""
}
let a = Algorithm(
The autocomplete shows the only valid initializer:
But I was expecting to use the implicit initializer like
Algorithm(category: "", title: "", fileName: "")
This gives the error:
Argument passed to call that takes no arguments
There are even screenshots on another issue that shows this call being successfully used.
What am I doing wrong?

The problem is the let. If you declare your properties with var, you'll get the memberwise initializer:
struct Algorithm {
var category: String = ""
var title: String = ""
var fileName: String = ""
}
let alg = Algorithm(category: "", title: "", fileName: "")
But since you supplied default values for all the properties, you don't get the memberwise initializer for let properties. The rule is that you get the implicit memberwise initializer for stored properties without a default value and for var properties (provided you have no explicit declared initializer).

You provided the default values for the properties that's why compiler won't add the default initialiser. When you remove the default values you will get what you are expecting:
struct Algorithm {
let category: String
let title: String
let fileName: String
}

use
struct Algorithm {
var category: String = ""
var title: String = ""
var fileName: String = ""
}
and autocomplete will show you both possibilities

Related

How do I unwrap an optional within a struct and use an if statement to print the optional?

I am getting an error message for my use of nil, and I am confused on how to properly unwrap the optional while placing it in an if statement to print if it is non-nil.
The following is my code:
struct DatingProfile{
var Name: String
var Age: Int
var City: String
var FavoriteMovie: String?
var Hobbies: String?
}
let myDatingProfile = DatingProfile(
Name: "William",
Age: 30,
City: "North Hollywood",
FavoriteMovie: nil,
Hobbies: "Basketball"
)
if let myFavoriteMovie = FavoriteMovie = nil {
print("\(Name) does not have a favorite movie")
} else {
print(myDatingProfile.FavoriteMovie)
}
}
The following is an error log:
exit status 1
main.swift:12:26: error: use of unresolved identifier 'FavoriteMovie'
if let myFavoriteMovie = FavoriteMovie = nil{
^~~~~~~~~~~~~
main.swift:13:12: error: use of unresolved identifier 'Name'
print("\(Name) does not have a favorite movie")
^~~~
main.swift:17:11: warning: expression implicitly coerced from 'String?' to 'Any'
print(myDatingProfile.FavoriteMovie)
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.swift:17:27: note: provide a default value to avoid this warning
print(myDatingProfile.FavoriteMovie)
~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
?? <#default value#>
main.swift:17:27: note: force-unwrap the value to avoid this warning
print(myDatingProfile.FavoriteMovie)
~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
!
main.swift:17:27: note: explicitly cast to 'Any' with 'as Any' to silence this warning
print(myDatingProfile.FavoriteMovie)
~~~~~~~~~~~~~~~~^~~~~~~~~~~~~
as Any
main.swift:20:1: error: extraneous '}' at top level
}
^
You need to access properties of a struct via the instance of the struct. You can only use the property names directly inside instance methods of that struct.
if let myFavoriteMovie = myDatingProfile.FavoriteMovie {
print(myFavoriteMovie)
} else {
print("\(myDatingProfile.Name) does not have a favorite movie")
}
You should also be conforming to the Swift naming convention, which is lowerCamelCase for variable names and properties (city, name, favoriteMovie, etc.).
Try the following, and please use camelCase for swift properties.
Also, you should really consider reading The Swift Programming Language. Start from here.
struct DatingProfile {
var name: String
var age: Int
var city: String
var favoriteMovie: String?
var hobbies: String?
}
let myDatingProfile = DatingProfile(
name: "William",
age: 30,
city: "North Hollywood",
favoriteMovie: nil,
hobbies: "Basketball"
)
// Use if-let syntax
if let myFavoriteMovie = myDatingProfile.favoriteMovie {
print(myFavoriteMovie)
} else {
print("\(myDatingProfile.name) does not have a favorite movie")
}
// Or use guard-let syntax
guard let myFavoriteMovie = myDatingProfile.favoriteMovie else {
print("\(myDatingProfile.name) does not have a favorite movie")
return
}
print(myFavoriteMovie)

Decoding numerical snake_case keys with JSONDecoder

I have the following JSON object which is to be converted to an object using JSONDecoder:
{
"first_key": 3,
"image_1000x1000": "location"
}
This maps to the following Swift model:
struct Response: Decodable {
var firstKey: Int
var image1000x1000: String
}
By using JSONDecoder with the .convertFromSnakeCase option, the snake_case keys inside the JSON are transformed to camelCase by using the algorithm defined in the documentation:
This strategy follows these steps to convert JSON keys to camel-case:
Capitalize each word that follows an underscore.
Remove all underscores that aren't at the very start or end of the string.
Combine the words into a single string.
Therefore, in this case:
first_key becomes firstKey (as expected)
image_1000x1000 should become image1000x1000
However when attempting to decode this response, a keyNotFound error for the image1000x1000 key is thrown (see this live example):
let json = "{\"first_key\": 3, \"image_1000x1000\": \"location\"}".data(using: .utf8)!
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let response = try decoder.decode(Response.self, from: json)
print(response)
} catch let e {
print(e)
}
What is incorrect about my camel case conversion of image_1000x1000, and why can’t JSONDecoder find the corresponding key?
You can see what the algorithm is expecting by running the process in reverse; use a JSONEncoder to encoder your data and inspect the output:
struct Response: Codable {
var firstKey: Int
var image1000x1000: String
}
let test = Response(firstKey: 10, image1000x1000: "secondKey" )
let encoder = JSONEncoder()
encoder.keyEncodingStrategy = .convertToSnakeCase
let data = try encoder.encode(test)
print(String(data: data, encoding: .utf8)!)
This will produce:
{"first_key":10,"image1000x1000":"secondKey"}
So if you have control over the JSON and can live with image1000x1000 as a key, then you're done. If not you'll have to do something like this:
struct Response: Codable {
var firstKey: Int
var image1000x1000: String
private enum CodingKeys: String, CodingKey {
case image1000x1000 = "image_1000x1000"
case firstKey = "first_key"
}
}
Another option is to implement a custom key encoding strategy. It may end up being less code. See KeyEncodingStrategy for more about this.
I am facing the same issue my json keys are iso639_1 and iso639_2. Without adding Coding key it will works.
make your variable optional
Here it's my Decodable model
struct CountryModel: Decodable{
var name: String
var borders: [String]
var region: String
var alpha2Code: String
var alpha3Code: String
var flag: String
var languages: [languages]
}
struct languages: Decodable {
var iso639_1: String?
var iso639_2: String?
var name: String
var nativeName: String
}
When I add optional to both underscore variables iso639_1 and iso639_2. Then it works fine may be due to Null Value!
Here, in your case add optional to your image1000x1000 variable. Like below
struct Response: Decodable {
var firstKey: Int
var image_1000x1000: String?
}
Hope it will works!

Concatenate literal with Optional String

Whats the proper way to implement this ? preferably in one line.
var name: String?
...
let username = "#" + name
Note: username must be String? I don't want to unwrap name for concatenation.
Edit: if name is nil, username should also be nil.
You can use the map method of Optional:
let username = name.map { "#" + $0 }
If name is nil then the closure is not executed and the result is nil. Otherwise the closure is evaluated with $0 set to the unwrapped name.
Try this:
let username = name.flatMap { "#\($0)" }
EDITED based on updated requirements:
You can do it a couple of ways. Here's one:
var name: String?
var username: String?
if let name = name {
username = "#" + name
}
Probably the simplest and more readable option is using if let but you could also define a method to prepend a String, opposed to String.append and then use optional chaining:
extension String {
func prepending(prefix: String) -> String {
return prefix + self
}
}
var name: String?
let username = name?.prepending("#")

SWIFT append data to dictionary

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
}

How do I correctly print a struct?

I'm trying to store an array of store structs within my users struct, but I can't get this to print correctly.
struct users {
var name: String = ""
var stores: [store]
}
struct store {
var name: String = ""
var clothingSizes = [String : String]()
}
var myFirstStore = store(name: "H&M", clothingSizes: ["Shorts" : "Small"])
var mySecondStore = store(name: "D&G", clothingSizes: ["Blouse" : "Medium"])
var me = users(name: "Me", stores: [myFirstStore, mySecondStore])
println(me.stores)
You’re initializing them just fine. The problem is your store struct is using the default printing, which is an ugly mangled version of the struct name.
If you make it conform to CustomStringConvertible, it should print out nicely:
// For Swift 1.2, use Printable rather than CustomStringConvertible
extension Store: CustomStringConvertible {
var description: String {
// create and return a String that is how
// you’d like a Store to look when printed
return name
}
}
let me = Users(name: "Me", stores: [myFirstStore, mySecondStore])
println(me.stores) // prints "[H&M, D&G]"
If the printing code is quite complex, sometimes it’s nicer to implement Streamable instead:
extension Store: Streamable {
func writeTo<Target : OutputStreamType>(inout target: Target) {
print(name, &target)
}
}
p.s. convention is to have types like structs start with a capital letter