Hi,
So sorry for that I must have been sleepy 4 A.M didn’t realize how I did Command + C didn’t work. I repasted them here.
import SwiftUI
@main
struct CitySightsApp: App {
var body: some Scene {
WindowGroup {
LaunchView()
.environmentObject(ContentModel())
}
}
}
import SwiftUI
import CoreLocation
struct LaunchView: View {
@EnvironmentObject var model: ContentModel
var body: some View {
// Detect the authorization status of geolocating the user
if model.authorizationState == .notDetermined {
// If undetermined, show onboarding
OnboardingView()
}
else if model.authorizationState == .authorizedAlways ||
model.authorizationState == .authorizedWhenInUse {
// If approved, show home view
HomeView()
}
else {
// If denied show denied view
LocationDeniedView()
}
}
}
struct LaunchView_Previews: PreviewProvider {
static var previews: some View {
LaunchView()
}
}
import SwiftUI
struct YelpAttribution: View {
var link: String
var body: some View {
Link(destination: URL(string: link)!) {
Image("yelp")
.resizable()
.scaledToFit()
.frame(height: 36)
}
}
}
struct YelpAttribution_Previews: PreviewProvider {
static var previews: some View {
YelpAttribution(link:"https://yelp.ca")
}
}
import SwiftUI
struct DashedDivider: View {
var body: some View {
GeometryReader { geometry in
Path { path in
path.move(to: CGPoint.zero)
path.addLine(to: CGPoint(x: geometry.size.width, y: 0))
}
.stroke(style: StrokeStyle(lineWidth: 1, dash: [5]))
.foregroundColor(.gray)
}
.frame(height: 1)
}
}
struct DashedDivider_Previews: PreviewProvider {
static var previews: some View {
DashedDivider()
}
}
import SwiftUI
struct HomeView: View {
@EnvironmentObject var model: ContentModel
@State var isMapShowing = false
@State var selectedBusiness: Business?
var body: some View {
if model.restaurants.count != 0 || model.sights.count != 0 {
NavigationView {
// Determine if we should show list or map
if !isMapShowing {
// Show list
VStack (alignment: .leading) {
HStack {
Image(systemName: "location")
Text(model.placemark?.locality ?? "")
Spacer()
Button("Switch to map view") {
self.isMapShowing = true
}
}
Divider()
ZStack (alignment: .top) {
BusinessList()
HStack {
Spacer()
YelpAttribution(link: "https://yelp.ca")
}
.padding(.trailing, -20)
}
}
.padding([.horizontal, .top])
.navigationBarHidden(true)
Button(action: {
// Handle button tap
navigateToLoginSignupView()
}) {
Text("Login/Sign Up")
.font(.headline)
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(10)
}
}
else {
ZStack (alignment: .top) {
// Show map
BusinessMap(selectedBusiness: $selectedBusiness)
.ignoresSafeArea()
.sheet(item: $selectedBusiness) { business in
// Create a business detail view instance
// Pass in the selected business
BusinessDetail(business: business)
}
// Rectangle overlay
ZStack {
Rectangle()
.foregroundColor(.white)
.cornerRadius(5)
.frame(height: 48)
HStack {
Image(systemName: "location")
Text(model.placemark?.locality ?? "")
Spacer()
Button("Switch to list view") {
self.isMapShowing = false
}
}
.padding()
}
.padding()
}
}
}
}
else {
// Still waiting for data so show spinner
ProgressView()
}
}
func navigateToLoginSignupView() {
}
}
struct HomeView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}
import SwiftUI
struct BusinessDetail: View {
var business: Business
@State private var showDirections = false
var body: some View {
VStack (alignment: .leading) {
VStack (alignment:.leading, spacing:0) {
GeometryReader() { geometry in
// Business image
let uiImage = UIImage(data: business.imageData ?? Data())
Image(uiImage: uiImage ?? UIImage())
.resizable()
.scaledToFill()
.frame(width: geometry.size.width, height: geometry.size.height)
.clipped()
}
.ignoresSafeArea(.all, edges: .top)
// Open / closed indicator
ZStack (alignment: .leading) {
Rectangle()
.frame(height: 36)
.foregroundColor(business.isClosed! ? .gray : .blue)
Text(business.isClosed! ? "Closed" : "Open")
.foregroundColor(.white)
.bold()
.padding(.leading)
}
}
Group {
HStack {
BusinessTitle(business: business)
.padding()
Spacer()
YelpAttribution(link: business.url!)
}
DashedDivider()
.padding(.horizontal)
// Phone
HStack {
Text("Phone:")
.bold()
Text(business.displayPhone ?? "")
Spacer()
Link("Call", destination: URL(string: "tel:\(business.phone ?? "")")!)
}
.padding()
DashedDivider()
.padding(.horizontal)
// Reviews
HStack {
Text("Reviews:")
.bold()
Text(String(business.reviewCount ?? 0))
Spacer()
Link("Read", destination: URL(string: "\(business.url ?? "")")!)
}
.padding()
DashedDivider()
.padding(.horizontal)
// Website
HStack {
Text("Website:")
.bold()
Text(business.url ?? "")
.lineLimit(1)
Spacer()
Link("Visit", destination: URL(string: "\(business.url ?? "")")!)
}
.padding()
DashedDivider()
.padding(.horizontal)
}
// Get directions button
Button {
// Show directions
showDirections = true
} label: {
ZStack {
Rectangle()
.frame(height: 48)
.foregroundColor(.blue)
.cornerRadius(10)
Text("Get Directions")
.foregroundColor(.white)
.bold()
}
}
.padding()
.sheet(isPresented: $showDirections) {
DirectionsView(business: business)
}
}
}
}
import SwiftUI
struct DirectionsView: View {
var business: Business
var body: some View {
VStack (alignment: .leading) {
// Business title
HStack {
BusinessTitle(business: business)
Spacer()
if let lat = business.coordinates?.latitude,
let long = business.coordinates?.longitude,
let name = business.name {
Link("Open in Maps", destination: URL(string:"http://maps.apple.com/?ll=\(lat),\(long)&q=\(name.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!)")!)
}
}
.padding()
// Directions map
DirectionsMap(business: business)
}
.ignoresSafeArea(.all, edges: .bottom)
}
}
import SwiftUI
struct BusinessTitle: View {
var business: Business
var body: some View {
VStack (alignment: .leading) {
// Business Name
Text(business.name!)
.font(.title2)
.bold()
// Loop through display address
if business.location?.displayAddress != nil {
ForEach(business.location!.displayAddress!, id: \.self) { displayLine in
Text(displayLine)
}
}
// Rating
Image("regular_\(business.rating ?? 0)")
}
}
}
import SwiftUI
import MapKit
struct BusinessMap: UIViewRepresentable {
@EnvironmentObject var model: ContentModel
@Binding var selectedBusiness: Business?
var locations:[MKPointAnnotation] {
var annotations = [MKPointAnnotation]()
// Create a set of annotations from our list of businesses
for business in model.restaurants + model.sights {
// If the business has a lat/long, create an MKPointAnnotation for it
if let lat = business.coordinates?.latitude, let long = business.coordinates?.longitude {
// Create a new annotation
let a = MKPointAnnotation()
a.coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
a.title = business.name ?? ""
annotations.append(a)
}
}
return annotations
}
func makeUIView(context: Context) -> MKMapView {
let mapView = MKMapView()
mapView.delegate = context.coordinator
// Make the user show up on the map
mapView.showsUserLocation = true
mapView.userTrackingMode = .followWithHeading
return mapView
}
func updateUIView(_ uiView: MKMapView, context: Context) {
// Remove all annotations
uiView.removeAnnotations(uiView.annotations)
// Add the ones based on the business
uiView.showAnnotations(self.locations, animated: true)
}
static func dismantleUIView(_ uiView: MKMapView, coordinator: ()) {
uiView.removeAnnotations(uiView.annotations)
}
// MARK - Coordinator class
func makeCoordinator() -> Coordinator {
return Coordinator(map: self)
}
class Coordinator: NSObject, MKMapViewDelegate {
var map: BusinessMap
init(map: BusinessMap) {
self.map = map
}
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
// If the annotation is the user blue dot, return nil
if annotation is MKUserLocation {
return nil
}
// Check if there's a reusable annotation view first
var annotationView = mapView.dequeueReusableAnnotationView(withIdentifier: Constants.annotationReuseId)
if annotationView == nil {
// Create a new one
annotationView = MKMarkerAnnotationView(annotation: annotation, reuseIdentifier: Constants.annotationReuseId)
annotationView!.canShowCallout = true
annotationView!.rightCalloutAccessoryView = UIButton(type: .detailDisclosure)
}
else {
// We got a resuable one
annotationView!.annotation = annotation
}
// Return it
return annotationView
}
func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
// User tapped on the annotation view
// Get the business object that this annotation represents
// Loop through businesses in the model and find a match
for business in map.model.restaurants + map.model.sights {
if business.name == view.annotation?.title {
// Set the selectedBusiness property to that business object
map.selectedBusiness = business
return
}
}
}
}
}
import SwiftUI
import MapKit
struct DirectionsMap : UIViewRepresentable {
@EnvironmentObject var model: ContentModel
var business: Business
var start:CLLocationCoordinate2D {
return model.locationManager.location?.coordinate ?? CLLocationCoordinate2D()
}
var end:CLLocationCoordinate2D {
if let lat = business.coordinates?.latitude, let long = business.coordinates?.longitude {
return CLLocationCoordinate2D(latitude: lat, longitude: long)
}
else {
return CLLocationCoordinate2D()
}
}
func makeUIView(context: Context) -> MKMapView {
// Create map
let map = MKMapView()
map.delegate = context.coordinator
// Show the user location
map.showsUserLocation = true
map.userTrackingMode = .followWithHeading
// Create directions request
let request = MKDirections.Request()
request.source = MKMapItem(placemark: MKPlacemark(coordinate: start))
request.destination = MKMapItem(placemark: MKPlacemark(coordinate: end))
// Create directions object
let directions = MKDirections(request: request)
// Calculate route
directions.calculate { (response, error) in
if error == nil && response != nil {
// Plot the routes on the map
for route in response!.routes {
map.addOverlay(route.polyline)
// Zoom into the region
map.setVisibleMapRect(route.polyline.boundingMapRect,
edgePadding: UIEdgeInsets(top: 100, left: 100, bottom: 100, right: 100),
animated: true)
}
}
}
// Place annotation for the end point
let annotation = MKPointAnnotation()
annotation.coordinate = end
annotation.title = business.name ?? ""
map.addAnnotation(annotation)
return map
}
func updateUIView(_ uiView: MKMapView, context: Context) {
}
static func dismantleUIView(_ uiView: MKMapView, coordinator: ()) {
uiView.removeAnnotations(uiView.annotations)
uiView.removeOverlays(uiView.overlays)
}
// MARK: - Coordinator
func makeCoordinator() -> Coordinator {
return Coordinator()
}
class Coordinator: NSObject, MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
renderer.strokeColor = .blue
renderer.lineWidth = 5
return renderer
}
}
}
import SwiftUI
struct BusinessList: View {
@EnvironmentObject var model: ContentModel
var body: some View {
ScrollView (showsIndicators: false) {
LazyVStack (alignment: .leading, pinnedViews:[.sectionHeaders] ) {
BusinessSection(title: "Restaurants", businesses: model.restaurants)
BusinessSection(title: "Sights", businesses: model.sights)
}
}
}
}
struct BusinessList_Previews: PreviewProvider {
static var previews: some View {
BusinessList()
}
}
import SwiftUI
struct BusinessSectionHeader: View {
var title: String
var body: some View {
ZStack (alignment: .leading) {
Rectangle()
.foregroundColor(.white)
.frame(height: 45)
Text(title)
.font(.headline)
}
}
}
struct BusinessSectionHeader_Previews: PreviewProvider {
static var previews: some View {
BusinessSectionHeader(title: "Restaurants")
}
}
import SwiftUI
struct BusinessSection: View {
var title: String
var businesses: [Business]
var body: some View {
Section (header: BusinessSectionHeader(title: title)) {
ForEach(businesses) { business in
NavigationLink(destination: BusinessDetail(business: business)) {
BusinessRow(business: business)
}
}
}
}
}
import SwiftUI
struct BusinessRow: View {
@ObservedObject var business: Business
var body: some View {
VStack (alignment: .leading) {
HStack {
// Image
let uiImage = UIImage(data: business.imageData ?? Data())
Image(uiImage: uiImage ?? UIImage())
.resizable()
.frame(width:58, height:58)
.cornerRadius(5)
.scaledToFit()
// Name and distance
VStack (alignment: .leading) {
Text(business.name ?? "")
.bold()
Text(String(format:"%.1f km away", (business.distance ?? 0)/1000 ))
.font(.caption)
}
Spacer()
// Star rating and number of reviews
VStack (alignment: .leading) {
Image("regular_\(business.rating ?? 0)")
Text("\(business.reviewCount ?? 0) Reviews")
.font(.caption)
}
}
DashedDivider()
.padding(.vertical)
}
.foregroundColor(.black)
}
}
import SwiftUI
struct LocationDeniedView: View {
let backgroundColor = Color(red: 34/255, green: 141/255, blue: 138/255)
var body: some View {
VStack (spacing: 20) {
Spacer()
Text("Whoops!")
.font(.title)
Text("We need to access your location to provide you with the best sights in the city. You can change your decision at any time in Settings.")
Spacer()
Button {
// Open settings by getting the settings url
if let url = URL(string: UIApplication.openSettingsURLString) {
if UIApplication.shared.canOpenURL(url) {
// If we can open this settings url, then open it
UIApplication.shared.open(url, options: [:], completionHandler: nil)
}
}
} label: {
ZStack {
Rectangle()
.foregroundColor(.white)
.frame(height: 48)
.cornerRadius(10)
Text("Go to Settings")
.bold()
.foregroundColor(backgroundColor)
.padding()
}
}
.padding()
Spacer()
}
.foregroundColor(.white)
.multilineTextAlignment(.center)
.background(backgroundColor)
.ignoresSafeArea(.all, edges: .all)
}
}
struct LocationDeniedView_Previews: PreviewProvider {
static var previews: some View {
LocationDeniedView()
}
}
import SwiftUI
struct OnboardingView: View {
@EnvironmentObject var model: ContentModel
@State private var tabSelection = 0
private let blue = Color(red: 0/255, green: 130/255, blue: 167/255)
private let turquoise = Color(red: 55/255, green: 197/255, blue: 192/255)
var body: some View {
VStack {
// Tab View
TabView(selection: $tabSelection) {
// First tab
VStack (spacing: 20) {
Image("city2")
.resizable()
.scaledToFit()
Text("Welcome to City Sights!")
.bold()
.font(.title)
Text("City Sights helps you find the best of the city!")
}
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.white)
.tag(0)
// Second tab
VStack (spacing: 20) {
Image("city1")
.resizable()
.scaledToFit()
Text("Ready to discover your city?")
.bold()
.font(.title)
Text("We’ll show you the best restaurants, venues and more, based on your location!")
}
.multilineTextAlignment(.center)
.padding()
.foregroundColor(.white)
.tag(1)
}
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
// Button
Button {
// Detect which tab it is
if tabSelection == 0 {
tabSelection = 1
}
else {
// Request for geolocation permission
model.requestGeolocationPermission()
}
} label: {
ZStack {
Rectangle()
.foregroundColor(.white)
.frame(height: 48)
.cornerRadius(10)
Text( tabSelection == 0 ? "Next" : "Get My Location")
.bold()
.padding()
}
}
.accentColor(tabSelection == 0 ? blue : turquoise)
.padding()
Spacer()
}
.background(tabSelection == 0 ? blue : turquoise)
.ignoresSafeArea(.all, edges: .all)
}
}
struct OnboardingView_Previews: PreviewProvider {
static var previews: some View {
OnboardingView()
}
}
import SwiftUI
struct LoginSignupView: View {
@ObservedObject var viewModel = LoginSignupViewModel()
var emailTextField: some View {
TextField("Email", text: $viewModel.emailText)
.textFieldStyle(.roundedBorder)
}
var passwordTextField: some View {
SecureField("Password", text: $viewModel.passwordText)
.textFieldStyle(.roundedBorder)
}
var actionButton: some View {
Button(viewModel.buttonTitle) {
//action
}.padding()
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color(.systemPink))
.cornerRadius(16)
.padding()
}
var body: some View {
VStack {
Text(viewModel.title)
.font(.largeTitle)
.fontWeight(.bold)
Text(viewModel.subtitle)
.font(.title2)
.fontWeight(.semibold)
.foregroundColor(Color(.systemGray2))
Spacer().frame(height: 50)
emailTextField
passwordTextField
actionButton
Button {
if viewModel.mode == .login {
viewModel.mode = .signup
} else {
viewModel.mode = .login
}
} label: {
Text("Switch to \(viewModel.mode == .signup ? "Login" : "Sign-Up")")
}
Spacer()
}
.padding(.horizontal)
}
}
struct LoginSignupView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
LoginSignupView()
}.environment(\.colorScheme, .dark)
}
}