Xcode 10 Struct Initializer autoComplete not showing - swift

I am defining my viewModels in a separate files as structs, when i am trying to create an instance of this struct in another file the autocomplete of the memberwise initializer is not showing..
This problem wasn't before(i.e Xcode 9) is it a bug in Xcode 10?
I have seen similar questions like Xcode does not autocomplete memberwise initializer of a Struct
but it's old and the problem was in Xcode 7 or 8 maybe.
However if i place my struct in the file i am attempting to use it, the autocomplete will be shown, so what's the problem ?
I Also tried .init after the struct name with no luck.
Here's my ViewModels :
struct ProfileModels {
struct ViewModels {
struct profile{
let name : URL?
let positionAndCountry : String?
let briefDescription : String?
let hotelInfo : HotelInfo?
}
struct HotelInfo{
let hotelName : String?
let roomClasification : String?
let checkInDate : String?
let checkOutDate : String?
let isCheckInEarly : String?
let isCheckInLate : String?
}
}
}
Update:
As mentioned in the comments that it works fine if the init method called, however sometimes the autocomplete isn't showing with calling the init method...
Anyway for anybody who wants a quick workaround until this bug solved is to use the following:
Create an empty struct (don't worry about the error for now)
example : ProfileModels.viewModels.profile()
In a new line, call the struct and the autocomplete will be shown!, now you can delete the first one.
I don't know why this happened but maybe the compiler after the first line will recognize that this struct missing arguments, so they will appear when the same struct used later...

There has been a bug in Xcode for a while where autocompletion for structs would be finicky. Classes have never had this issue, but I was able to fix the autocompletion by typing .init after the struct name, triggering the autocompletion, and then deleting the .init after. Hope this helps

Related

SwiftUI FileDocument using Class instead of Struct

I've started a document-base macOS app in SwiftUI and am using a FileDocument (not a Reference FileDocument) as the type of document. In every tutorial I've seen, even in Apple's own WWDC video discussing it (https://developer.apple.com/wwdc20/10039), a struct is always used to define the FileDocument.
My question is: is there an issue with using a class in the struct defining the document. Doing so doesn't result in any Xcode warnings but I wanted to be sure I'm not creating any issues for my app before going down this path.
Below is some example code for what I'm talking about: declaring TestProjectData as a class for use within the DocumentDataAsClassInsteadOfStructDocument - struct as a FileDocument?
public class TestProjectData: Codable{
var anotherString: String
init(){
anotherString = "Hello world!"
}
}
struct DocumentDataAsClassInsteadOfStructDocument: FileDocument, Codable {
var project: TestProjectData
init() {
project = TestProjectData()
}
static var readableContentTypes: [UTType] { [.exampleText] }
init(configuration: ReadConfiguration) throws {
guard let data = configuration.file.regularFileContents,
let _ = String(data: data, encoding: .utf8)
else {
throw CocoaError(.fileReadCorruptFile)
}
let fileContents = try JSONDecoder().decode(Self.self, from: data)
self = fileContents
}
func fileWrapper(configuration: WriteConfiguration) throws -> FileWrapper {
let data = try JSONEncoder().encode(self)
return .init(regularFileWithContents: data)
}
}
It appears that yes, we need to use a struct for documents. See this post for a thorough example of the issues you can run into if you use a class instead of a struct.
SwiftUI View doesn't update
SwiftUI's architecture is all about using value types for speed and consistency. E.g. on a state change we create all the View structs and then SwiftUI diffs and uses the result to init/update/deinit UIView objects.
I believe the same thing happens with FileDocument. The struct is diffed on a change and the difference is used to init/update/deinit a UIDocument object.
If you init object vars inside these structs then basically it is a memory leak because a new object will be init every time the struct is created which is every time something changes. Also chances are you'll end up using the wrong instance of the object because there will be so many. You can see this type of problem surface when blocks are used inside body, the callback usually happens on an older version of the View struct, which isn't a problem when everything is value types but it is a big problem if referencing old objects.
Try to stick to value types in SwiftUI if you can, if you use objects you'll run into all kinds of headaches. And I don't think ReferenceFileDocument even works yet - I seem to remember it needs some kind of undo manager workaround.

SwiftUI ForEach Bindable List errors

I would appreciate help with SwiftUI bindable lists.
I read the following article and tried it on my app but I'm getting errors.
https://www.swiftbysundell.com/articles/bindable-swiftui-list-elements/
First, the following View including the non-bindable ordinary ForEach list works fine without any errors
#ObservedObject var notificationsViewModel = NotificationsViewModel.shared
//NotificationsViewModel does a API call and puts the fetched data in the Notifications Model
var body: some View {
VStack {
ForEach(notificationsViewModel.notifications?.notificationsDetails ?? [NotificationsDetail]()) { notificationsDetail in
---additional code here--- }
}
Model below:
struct Notifications: Codable, Identifiable {
let id = UUID()
let numberOfNotifications: Int
var notificationsDetails: [NotificationsDetail]
enum CodingKeys: String, CodingKey {
case numberOfNotifications = "number_of_notifications"
case notificationsDetails = "notifications"
}
}
struct NotificationsDetail: Codable, Identifiable, Equatable {
let id: Int
let notificationsCategoriesId: Int
let questionsUsersName: String?
enum CodingKeys: String, CodingKey {
case id = "notifications_id"
case notificationsCategoriesId = "notifications_categories_id"
case questionsUsersName = "questions_users_name"
}
}
When I try to change this ForEach to a bindable one, I start getting multiple errors.
ForEach($notificationsViewModel.notifications?.notificationsDetails ?? [NotificationsDetail]()) { $notificationsDetail in
---additional code here using $notificationsDetail---}
When I try to fix some of the errors such as "remove ?", I get a new error saying I need to add the ?.
When I delete the default value ?? NotificationsDetail, I still get errors
The Xcode build version is iOS15.
Does anyone know how to fix this? Thanks in advance.
ForEach($notificationsViewModel.notifications?.notificationsDetails ?? [NotificationsDetail]()
Is confusing the type system, because one side of the ?? is a binding to an array of values, and the other side is an array of values. There's also an optional in your key path to make things more complicated.
Try to rearrange the NotificationsViewModel type so that it just surfaces a non-optional array instead of having all this optional mess at the view level. Is it really meaningful to have an optional notification property, or can an empty one be used instead? Do you need the separate notifications struct? Are you just modelling your data types directly from API responses? Perhaps you can make changes to your model types to make them easier to work with?

How to fix Xcode error "Segmentation fault: 11" after adding didSet to #State var

I want to add a "didSet" function to a parameter of a SwiftUI's View struct, but every time I try to build the app I get the "Segmentation fault: 11" error.
I tried to rename the parameter, but nothing happened. I also tried to make it Optional but because it's a #State it didn't worked. What can I do?
#State var text: String {
didSet {
print(oldValue, text)
}
}
Try adding a default value to your var, which is necessary when defining a #State var.
#State var text: String = "" {
didSet {
print(oldValue, text)
}
}
I have this issue too, seems like a compiler bug or something. I have done some digging and found a bug raised by Apple which can be found here https://bugs.swift.org/browse/SR-10918
Instead of using didSet on a variable with a #State property wrapper you could have a view model that conforms to BindableObject (part of Combine) and use #ObjectBinding in your view so when anything within your view model is updated SwiftUI will update your UI
Here is a nice tutorial on how to do so...
https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-objectbinding-to-create-object-bindings

Is it possible to get the KVC-String from Swift 4 KeyPath?

For a project I am currently working on, it would be very useful to get the KVC-String from a KeyPath instance my method is receiving. Short example:
struct Person {
var name: String
}
let propertyCache = ["name": "something"]
func method<T>(_ keypath: KeyPath<Person, T>) -> T? {
let kvcName = keypath.kvc
return propertyCache[kvcName]
}
This might seem not very useful, but in my project it is :) I found a property on KeyPath called _kvcKeyPathString which is also public, but it returns nil every time I tried.
Or is their maybe a possibility to use reflection there? Thanks in advance for ideas/solutions!
I don't know of a pure Swift way to get the name of the property as a string yet.
But, if you add the #objc attribute to the property then _kvcKeyPathString will actually have a value instead of always being nil. Also, since Swift structs can't be represented in Objective-C, this method only works for classes.
A minimal working example usage:
class SomeClass {
#objc var someProperty = 5
}
let keyPath = \SomeClass.someProperty
print(keyPath._kvcKeyPathString)

Swift struct with 'Any' not copied correctly into a function

Here's my struct -
struct SettingsItem {
var id: String!
var defaultValue: Any!
init() {
}
}
Then it's being used -
var item2 = SettingsItem()
item2.id = "abcd"
item2.defaultVaule = "1234"
f(item2) // <-- breakpoint shows a good item
When executed, item looks good at the breakpoint shown above. But then inside function f, item is all messed up.
func f(item: SettingsItem) {
println(item) // <-- bad item!
}
It looks like item isn't copied correctly when calling f, but when I tried this on a playground it didn't reproduce.
Any ideas for what causes this?
Update
It seems to be working well when I change type of var defaultValue: Any! to anything else, like Int! or String!.
Also tried using a default constructor (removed my init()), didn't help.
Why does it fail to copy when using Any?
In Xcode 6.4 I get the same behaviour in a playground too.
Probably best not to rely on the built-in string-conversion functionality as that’s really only for debugging purposes. Instead, try giving your type an explicit Printable implementation:
extension SettingsItem: Printable {
var description: String {
// make this string whatever you think the appropriate
// string representation of your value is
return "{id: \(id), defaultValue: \(defaultValue)}"
}
}
If I add this, it now prints out this way inside f.
P.S. I’d suggest thinking about ways you can remove the ! and Any from your struct, they will lead to problems in the longer-term.