Issue with declaring class variables - swift3

I'm implementing Google Maps in my project, because MKLocalSearch doesn't have a complete list of restaurants/bars. I'm following Google's documentation. I believe I've uncovered an issue and seek advice on how to solve.
The fist step is variable declaration at the class level:
var locationManager = CLLocationManager()
var currentLocation: CLLocation?
var mapView: GMSMapView!
var placesClient: GMSPlacesClient!
var zoomLevel: Float = 15.0
After following the remaining instructions, my program keeps failing on the mapView line:
let camera = GMSCameraPosition.camera(withLatitude: defaultLocation.coordinate.latitude,
longitude: defaultLocation.coordinate.longitude,
zoom: zoomLevel)
mapView = GMSMapView.map(withFrame: view.bounds, camera: camera) //Fails here!
The error message in the debugger is: Fatal error: Unexpectedly found Nil while unwrapping optional value.
So the question is why does the Google documentation declare the mapView variable the way it does? Don't all variables that ARE NOT optional have to initialize with a value? Should I declare the variable as Optional? Seems odd to me that Google Documentation would be incorrect.
Thanks.

By declaring an optional with ! you are telling the compiler whenever I access this optional it will have a value. It means you don't have to stick ! at the end everytime you use it. But it does mean if you haven't set the optional to have a value you will get an error when you access it.
An example of using one would a class member of a UIViewController that you setup in viewDidLoad(). You aren't required to setup a separate init() function for the view controller to initialise the variable but you can pretty much guarantee the entry point for your code is viewDidLoad() and any subsequent code run will have a valid version of this class member.
Search for Implicitly Unwrapped Optionals for more details

you should take optional mapView when there is possibility of having mapView or not. You should always take like this
let mapView = GMSMapView(frame: self.view.bounds)

Related

Why does the following code get an error with a Date but not a String

The following code gets a compiler error "Variable 'self.d' used before being initialized"
it's easy to fix, by initializing the #State variable like you're supposed to, e.g.
_d = State(initialValue: test)
but I'm trying to understand the language better, and SwiftUI better, and don't see why it fails, but if I replace Date with String, it compiles just fine.
My best guess is that maybe the compiler is doing some optimization that is reforming the normally invalid syntax into something that works, and it can't do this in some cases.
I'm hoping someone else can give me a better idea about exactly what's going on. Thanks much for your attention :)
import SwiftUI
typealias Foo = Date
struct Test {
#State private var d: Foo
init(_ test: Foo) {
d = test // Variable 'self.d' used before being initialized
}
}
Edit: This works not just with primitive types, but a class that I define myself will work also, if it doesn't contain a Date.

How to convert a complex UIKit View into a SwiftUI View?

there's a lot of code in here but please be patient I will try to explain my question
so i am trying to convert Apple's AvDepthCapture project which is in UIKit as a SwiftUI project, so i copied all the supporting files, i will attach the image for the same below, the shaders folder contain the metal files for applying filters
so I created a class same as the cameraviewcontroller and added all the properties as follows -
class CameraViewController: UIViewController, AVCapturePhotoCaptureDelegate, AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureDepthDataOutputDelegate, AVCaptureDataOutputSynchronizerDelegate, ObservableObject {
private var previewView: PreviewMetalView!
}
now when i run my app on my device it asks for camera permission but then shows a white screen instead of opening the camera and the PreviewMetalView file that i copied earlier directly from the original UIKit project,
the declaration is causing error
private var previewView: PreviewMetalView!
this line, it quits my app and says unexpectedly found nil when unwrapping an optional value but the code is the same as that of the UIKit project and UIKit project works perfectly fine on my device then why is it throwing this error!?, please help i can provide both the UIKit project and my SwiftUI project file if you want to see i really can't figure it out
there's a lot of code but this specific line is throwing error, I declared a type of PreviewMetalView but now PreviewMetalView has an initialiser which takes in a type NSCoder so what should I write there ??
like I would be declaring my variable as
var previewView = PreviewMetalView(coder: NSCoder)
what to put in place of NSCoder ?
when I opened the original UIKit project the previewView is defined as an IBOutlet to a Container View which is then linked to the CameraViewController but I cannot find where is it initialised please help!

SwiftUI: Why does ForEach need an ID?

Im using a ForEach loop in my SwiftUI View and I am getting strange warnings.
It works fine like this:
ForEach(0..<7) { i in
// do something
}
Then I changed 7 to a constant:
let numberOfElements = 7
ForEach(0..<numberOfElements) { i in
// do something
}
And got the following warning:
Non-constant range: argument must be an integer literal
I googled an found the following solution which works:
let numberOfElements = 7
ForEach(0..<numberOfElements, id:\.self) { i in
// do something
}
However, I have no idea why it works. Why do I have to give an ID to the ForEach loop, and what is the ID for?
ForEach(0..<numberOfElements) { i in
// do something
}
The reason why using the above ForEach init pops the using literal value warning is because SwiftUI doesn't expect to re-render anything when using the Range<Int> init method. This is a documented requirement / feature. The exceptions are the init methods with id:.
A hashable id matters in SwiftUI as well as in many other view-tree based frameworks like React is because these UI frameworks needs to use these ids to track updates for views inside the ForEach or any other "Container Views", to help the framework achieve usable performance. If you want to dig deeper, take a look at this WWDC video: Demystify SwiftUI.

How to use Identifiable in a ForEach loop

I am trying to iterate through an array of objects. This object is conforming to the Identifiable protocol. When using a ForEach loop, I get the following error: Type of expression is ambiguous without more context
I've included the block of code that is throwing the error. The error is specifically underlining \.name. Am I missing something?
Another note: This code worked in Xcode 11 Beta 2 but broke in Xcode 11 Beta 3...
struct ItemRow : View {
var categoryName:String
var items:[Item]
var body: some View {
VStack {
Text(self.categoryName)
.font(.title)
ScrollView(showsHorizontalIndicator: false) {
HStack (alignment: .top){
ForEach (self.items.identified(by: \.name)) { item in
NavigationLink(destination: ItemDetail(item: item)) {
ItemView(item: item)
.frame(width:300)
.padding(.trailing, 30)
}
}
}
}
}
}
}
Here is the Identifiable Object:
struct Item:Hashable, Codable, Identifiable {
var id:Int
var name:String
var category:Category
var description:String
}
(This code has been abstracted)
The first thing you need to know, is that when building views, compile errors can be very misleading. An error may show at the bottom of your code, but the cause may be at the top. I expect this will be fixed sometime in the future, but for the time being, you need to be careful.
Your code compiles just fine. Because of what I said about misleading errors, one brute but effective technique to debug the problem, is to start commenting bits of code until the error goes away. This will let pin point where the root of the problem may be.
A good way of updating your question is including enough code, so that people can reproduce the problem just by copy and paste into their own Xcode. It may be a lot of work for you, but I found that most of the time, you understand the problem during that process and you may not even need to post the question in the first place. Reducing an issue to its minimum expression, is also a great way of understading/fixing a problem.
UPDATE
Since you added more code, the error is showing where you would have not expected:
The ScrollView initialiser you were using, was deprecated. It now looks like this:
ScrollView(.horizontal, showsIndicators: false)
Also something that may potentially be a problem. You are using:
self.items.identified(by: \.name)
But don't you mean:
self.items.identified(by: \.id)
If so, then you do not need to use identified, since Item is already Identifiable and as such, it is already identified by id.
self.items
The issue was actually with the following line:
ScrollView(showsHorizontalIndicator: false)
ScrollView doesn't work like that anymore in Beta 3. The arguments now look something like this:
ScrollView(.horizontal, showsIndicators: false)
That will give you a horizontal scrolling view and will not show the scrolling indicators.

Swift3 and addTarget Selector code converter suggestion

In my old Swift iOS app project I use code like his:
button!.addTarget(self,action:"ratingButtonTapped:",for:.touchDown)
where ratingButtonTapped is a function in the same class
The code converter gives error
"No method declared with Objective-C selector 'ratingButtonTapped:'"
and then suggests this solution
button!.addTarget(self,action:Selector("ratingButtonTapped:"),for:.touchDown)
The only problem is that even after applying the fix, it continues to give warning
"No method declared with Objective-C selector 'ratingButtonTapped:'"
where is then suggest to wrap it in parentheses to hide the warning
...
This is my function declaration in the same classd:
func ratingButtonTapped(_ sender: UIButton) {
}
...
I guess the way I did this dated and wrong in Swift 3 - but what is the correct way then? The class has a function with name ratingButtonTapped
button.addTarget(self, action: #selector(YourClassController. ratingButtonTapped(_:)), for: .touchUpInside)
func ratingButtonTapped(_ sender:UIButton){
}