Non-failable initializer requirement cannot be satisfied by failable initializer - swift

I have this struct and I want its init to be failable because something could go wrong with the JSON dictionary I pass:
struct AdminModel:Interface{
var status:Any = ""
var l:String = ""
var p:String = ""
var url:String = ""
init?(json:NSDictionary){
if let status = json["status"] as? Any,
let l = json["l"] as? String,
let p = json["p"] as? String,
let url = json["url"] as? String
{
self.status = status
self.l = l
self.p = p
self.url = url
}else{
return nil
}
}
}
There's no issue until I add ? after init to make init failable: at that point XCode complains:
Non-failable initializer requirement 'init(json:)' cannot be satisfied by failable initializer ('init?')
Why my struct can't be failable? Should I declare failable even the protocol init?

Maybe your Interface is like:
protocol Interface {
init(json: JSON)
}
but your AdminModel's init is init?(json: JSON), so you should keep consistent:
protocol Interface {
init?(json: JSON)
}

Related

Swift - Return type of struct parameters without default values

I'm very new in Swift so i might be missing some basics.
I have struct:
struct MyStruct {
var a: Int
var b: String
var c: Bool
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
}
and function, that should iterate through given struct properties and return their types in json:
func structProps(){
let elm = MyStruct()
let mirror = Mirror(reflecting: elm)
var exampleDict: [String: String] = [:]
for child in mirror.children {
exampleDict[child.label!] = String(describing:type(of: child.value)) as String
}
if let theJSONData = try? JSONSerialization.data(
withJSONObject: exampleDict,
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
}
}
it kinda return what i need:
JSON string = {"a":"Int","b":"String","c":"Bool"}
Because i'm having a lot of structs and i want to export json from all of them, i'm wondering if there is a way to have generic initializer. Without passing default values.
It means without
init() {
a: Int = 1,
b: String? = "",
c: Bool? = false
}
If I understand correctly , you can create a base protocol and add as an extension to your structures like
protocol BaseFunction {
func getElements() -> [String : String]
func getDict() -> String
}
extension BaseFunction {
func getElements() -> [String : String] {
let mirror = Mirror(reflecting: self)
let propertiesRemoveNil = mirror.children.filter({!(($0.value as AnyObject) is NSNull)})
let properties = propertiesRemoveNil.compactMap({$0.label})
var types = [String]()
_ = mirror.children.forEach({
types.append(String(describing:type(of: $0.value)))
})
return Dictionary(uniqueKeysWithValues: zip(properties, types))
}
func getDict() -> String{
if let theJSONData = try? JSONSerialization.data(
withJSONObject: getElements(),
options: []) {
let theJSONText = String(data: theJSONData, encoding: .ascii)
return theJSONText ?? ""
}
return ""
}
}
And usage :
func structProps(){
let elm = MyStruct()
print(elm.getDict())
}
OUTPUT :
{"a":"Int","b":"String","c":"Bool"}
I was going to write a comment about using a protocol but I thought it would be easier to understand as an answer with some code.
To make the usage more generic so you don't need specific code for each type of struct you should use a protocol. Instead of having an init that might clash with already existing init in the struct I prefer a static method that returns a new object, also known as a factory method
protocol PropertyExtract {
static func createEmpty() -> PropertyExtract
}
Then we can make use of the default init for the struct or any supplied to create an object with some initial values by letting the struct conform to the protocol in an extension
extension MyStruct: PropertyExtract {
static func createEmpty() -> PropertyExtract {
MyStruct(a: 0, b: "", c: false)
}
}
And instead of hardcoding or passing a specific type of object to the encoding function we pass the type of it
func structProps(for structType: PropertyExtract.Type)
and use the protocol method to get an instance of the type
let object = structType.createEmpty()
The whole function (with some additional changes)
func structProps(for structType: PropertyExtract.Type) -> String? {
let object = structType.createEmpty()
let mirror = Mirror(reflecting: object)
let exampleDict = mirror.children.reduce(into: [String:String]()) {
guard let label = $1.label else { return }
$0[label] = String(describing:type(of: $1.value))
}
if let data = try? JSONEncoder().encode(exampleDict) {
return String(data: data, encoding: .utf8)
}
return nil
}
And this is then called with the type of the struct
let jsonString = structProps(for: MyStruct.self)
According to Creating a Swift Runtime Library, there is a way to access meta data types without initializer.
Solution for what i was asking for is possible with Runtime library.
Mirror(reflecting:) expects an instance of a type and not a type itself, ref.
One idea is to have a generic function that works with all types conforming to a protocol that provides an empty init. Something like:
protocol EmptyInitializable {
init()
}
struct StructOne {
let a: Bool
let b: String
}
struct StructTwo {
let c: Int
let d: Float
}
extension StructOne: EmptyInitializable {
init() {
a = false
b = ""
}
}
extension StructTwo: EmptyInitializable {
init() {
c = 1
d = 1.0
}
}
func print(subject: EmptyInitializable) -> String? {
let dictionary = Dictionary(uniqueKeysWithValues:
Mirror(reflecting: subject).children.map {
($0.label!, String(describing: type(of: $0.value)))
}
)
return (try? JSONSerialization.data(withJSONObject: dictionary)).flatMap {
String(data: $0, encoding: .utf8)
}
}
print(subject: StructOne()) // "{"a":"Bool","b":"String"}"
print(subject: StructTwo()) // "{"d":"Float","c":"Int"}"
You still have to implement the empty init and assign some values and I'm afraid there's no way currently to avoid that.

Inheritance and constructors. Self used before super.init

I am new at inheritance in swift and i'd like to understand how to create constructors in the best way.
I get an error Must call a designated initializer of the superclass 'Base'
class Base{
var id:String
var link:String
convenience init?(json: [String: Any]) {
guard let id = json["id"] as? String,
let link = json["link"] as? String else{
print("Failed Base Class Init!")
return nil
}
self.init(id: id, link: link)
}
init(id: String, link: String) {
self.id = id
self.link = link
}
}
class Image: Base{
func addImageToView(imageView: UIImageView){
Alamofire.request(self.link).responseImage { response in
if let img = response.result.value {
imageView.image = img
}
}
}
}
class Gallery:Base{
var images:[Image] = []
init?(json: [String: Any]){
super.init(json: json) // Here i get <- Must call a designated initializer of the superclass 'Base'
guard let tab = json["images"] as? [[String:Any]] else{
print("Failed Gallery Image Init!")
return nil
}
images = tab.compactMap{return Image(json: $0)}
}
}
How to do so that my class gallery creates a table of images from json and also initializes the id and link?
Make init?(json (also) a designated initializer
init?(json: [String: Any]) {
guard let id = json["id"] as? String,
let link = json["link"] as? String else{
print("Failed Base Class Init!")
return nil
}
self.id = id
self.link = link
}
And override it in the subclass
override init?(json: [String: Any]) {
super.init(json: json)
...

swift 4 model class error: Return from initializer without initializing all stored properties

I created a model class for notes that have the attributes title and content. I am getting an error in the second init that says: Return from initializer without initializing all stored properties. Im feel like there is something I am missing and I cant seem to see it. This is the code:
class Note{
private var _title: String
private var _content: String
var title: String!{
return self._title
}
var content: String!{
return self._content
}
init(title: String, content: String){
self._title = title
self._content = content
}
init(noteData: Dictionary<String, AnyObject>) {
if let title = noteData["title"] as? String {
self._title = title
}
if let content = noteData["content"] as? String {
self._content = content
}
}
}
You have to initialise all properties of your class in its initializer. Regarding your dictionary initializer you should make it fallible and return nil in case of missing key/value pairs:
class Note {
let title: String
let content: String
init(title: String, content: String) {
self.title = title
self.content = content
}
init?(dictionary: [String: Any]) {
guard
let title = dictionary["title"] as? String,
let content = dictionary["content"] as? String
else { return nil }
self.title = title
self.content = content
}
}
Note that if use a struct instead of a class you don't even have to implement an initializer for your custom object:
struct Note {
let title: String
let content: String
init?(dictionary: [String: Any]) {
guard
let title = dictionary["title"] as? String,
let content = dictionary["content"] as? String
else { return nil }
self.title = title
self.content = content
}
}
The problem is that in the second init, if the unwrap fails the properties will not be set. For the init to work correctly the properties that are not optional will all need values. The best way to fix this is to make the properties optional.
Optional properties:
private var title: String?
private var content: String?
By making the properties optional you can avoid doing the if let in the init:
self.title = noteDate["title"]
Make sure to unwrap safely when you use the properties though!

Using a designated initialiser VS a static method to populate a struct

I have been working on a struct that parses a JSON data, that data is from Dark sky. While I was working on it, something hit my brain and been pondering on it ever since.
Traditionally, we have been using designated initializers (although struct gives you a member-wise initializer for free) to instantiate an object. But, we could easily use a static function that returns itself and that function populates the properties.
like so:
struct WeatherForecastData {
// Weather data
var apparentTemperature: Double?
var icon: String?
var precipProbability: Double?
var pressure: Double?
static func map(_ data: [String: AnyObject]) -> WeatherForecastData {
var p = WeatherForecastData()
p.apparentTemperature = data["apparentTemperature"] as? Double ?? nil
p.icon = data["icon"] as? String ?? nil
p.precipProbability = data["precipProbability"] as? Double ?? nil
p.pressure = data["pressure"] as? Double ?? nil
return p
}
}
notice the static method, if we replace it with a designated initializer, it would be doing the same thing as a static method. My question is when should we use static methods to instantiate an object instead of the traditional designated initializer?
Why wouldn't you just use a custom initializer like this?
struct WeatherForecastData {
var apparentTemperature: Double
var icon: String
var precipProbability: Double
var pressure: Double
init?(data:Dictionary<String,Any>) {
guard
let apparentTemperature = data["apparentTemperature"] as? Double,
let icon = data["icon"] as? String,
let precipProbability = data["precipProbability"] as? Double,
let pressure = data["pressure"] as? Double
else {
print("Invalid Data")
return nil
}
self.apparentTemperature = apparentTemperature
self.icon = icon
self.precipProbability = precipProbability
self.pressure = pressure
}
}

Convenience initializer with non-optional property

An object of mine has an integer ID. Since this is a required property I am not defining it as an optional and I am requiring it in the designated initializer:
class Thing {
var uniqueID: Int
var name: String?
init (uniqueID: Int) {
self.uniqueID = uniqueID
}
}
Since I am creating one of these from some JSON, the usage is along the lines of:
if let uniqueID = dictionary["id"] as? Int {
let thing = Thing(uniqueID: unique)
}
Next, I would like to be able to add a convenience initializer to the Thing class that accepts the dictionary object and sets the properties accordingly. This includes the required uniqueID and some other optional properties. My best effort so far is:
convenience init (dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
//set other values here?
}
//or here?
}
But of course this isn't sufficient since the designated initializer isn't called on all paths of the conditional.
How should I be handling this scenario? Is it even possible? Or should I accept that uniqueID must be an optional?
You have a couple of options with this one. One is a failable initialisers:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
Technically this can be tweaked a bit (mainly depending on your preference/version of swift), but my person preference is something as follows:
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
All together, as a playground:
class Thing {
var uniqueID: Int
var name: String?
init(uniqueID: Int) {
self.uniqueID = uniqueID
}
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: -1)
return nil
}
}
class func fromDictionary(dictionary: [String: AnyObject]) -> Thing? {
if let uniqueID = dictionary["id"] as? Int {
return self.init(uniqueID: uniqueID)
}
return nil
}
}
let firstThing = Thing(uniqueID: 1)
let secondThing = Thing(dictionary: ["id": 2])
let thirdThing = Thing(dictionary: ["not_id": 3])
let forthThing = Thing.fromDictionary(["id": 4])
let fithThing = Thing.fromDictionary(["not_id": 4])
The best solution is probably to use a failable initializer, which will either return an instantiated object or nil.
Because Swift objects cannot be partially constructed and convenience initializers must call a non-convenience initializer, we must still do something in the failure case.
The result will look something like this:
convenience init?(dictionary: [String: AnyObject]) {
if let uniqueID = dictionary["id"] as? Int {
self.init(uniqueID: uniqueID)
} else {
self.init(uniqueID: 0)
return nil
}
}
Generally speaking, our non-convenience initializer(s) should be one that accepts all arguments, and convenience initializers should be methods which don't require some of the arguments.
For example, I might make my default initializer look like this:
init(uniqueID: Int, name: String? = nil) {
self.uniqueID = uniqueID
self.name = name
}
This allows us to call the constructor in several different ways:
let thing1 = Thing(1)
let thing2 = Thing(2, nil)
let thing3 = Thing(3, "foo")
let thing4 = Thing(4, myUnwrappedStringVar)
let thing5 = Thing(5, myWrappedStringOptional)
And that already covers a lot of use cases for us.
So, let's add another convenience initializer that accepts an optional Int.
convenience init?(uniqueID: Int? = nil, name: String? = nil) {
if let id = uniqueID {
self.init(uniqueID: id, name: name)
} else {
self.init(uniqueID: 0)
return nil
}
}
Now we can take an Int? for our uniqueID argument and just fail when it's nil.
So, one more to accept the dictionary.
convenience init?(dictionary: [String: AnyObject]) {
let uniqueID = dictionary["id"] as? Int
let name = dictionary["name"] as? String
self.init(uniqueID: uniqueID, name: name)
}
We still have the slightly weird initialize then return nil pattern in our first convenience constructor, but everything else we build on top of this can simply call that convenience initializer and doesn't require the weird pattern.
In the initializer that takes the dictionary, if there's no id key, or if it's something that's not an Int, then the let uniqueID will be nil, so when we call the other constructor, it will call the one that accepts an Int?, be passed nil, return nil, and therefore the one we called will return nil.