├── Color Picker Demo
├── Color Picker Demo.mov
├── Color Picker Demo.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── ale_patron.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── ale_patron.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Color Picker Demo
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Color_Picker_DemoApp.swift
│ ├── ContentView.swift
│ ├── Info.plist
│ └── Preview Content
│ └── Preview Assets.xcassets
│ └── Contents.json
├── Directions
├── Directions.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Directions
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SceneDelegate.swift
├── Expandable Button Panel
├── Expandable Button Panel.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Expandable Button Panel
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SceneDelegate.swift
├── Guess the Number
├── Final
│ ├── Guess the Number.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ ├── contents.xcworkspacedata
│ │ │ ├── xcshareddata
│ │ │ │ └── IDEWorkspaceChecks.plist
│ │ │ └── xcuserdata
│ │ │ │ └── alep.xcuserdatad
│ │ │ │ └── UserInterfaceState.xcuserstate
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── xcschemes
│ │ │ └── xcschememanagement.plist
│ └── Guess the Number
│ │ ├── AppDelegate.swift
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ └── Contents.json
│ │ └── Contents.json
│ │ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ │ ├── GuessingGame.swift
│ │ ├── Info.plist
│ │ ├── NumberGuessingGame.swift
│ │ ├── NumberGuessingGameView.swift
│ │ ├── Preview Content
│ │ └── Preview Assets.xcassets
│ │ │ └── Contents.json
│ │ └── SceneDelegate.swift
└── Skeleton
│ ├── Guess the Number.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
│ └── Guess the Number
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── GuessingGame.swift
│ ├── Info.plist
│ ├── NumberGuessingGame.swift
│ ├── NumberGuessingGameView.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SceneDelegate.swift
├── Lists and Navigation
├── Lists and Navigation.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Lists and Navigation
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SceneDelegate.swift
├── Music Search
├── Music Search.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── ale_patron.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ ├── xcshareddata
│ │ └── xcschemes
│ │ │ └── Music Search.xcscheme
│ └── xcuserdata
│ │ └── ale_patron.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Music Search
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── ImageCache.swift
│ ├── ImageLoader.swift
│ ├── Info.plist
│ ├── Music_SearchApp.swift
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Song.swift
│ └── SongListViewModel.swift
├── Programming Quotes
├── Programming Quotes.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Programming Quotes
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── Programming_QuotesApp.swift
├── Progress View
├── Progress View.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Progress View
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── Progress_ViewApp.swift
├── Random Joke UIKit
├── Random Joke UIKit.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Random Joke UIKit
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── Random Joke
├── Random Joke.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Random Joke
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ ├── SceneDelegate.swift
│ └── ViewController.swift
├── Todos
├── Todos.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── Todos
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── Base.lproj
│ └── LaunchScreen.storyboard
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ └── SceneDelegate.swift
├── TwitterUI
├── TwitterUI.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ ├── xcshareddata
│ │ │ └── IDEWorkspaceChecks.plist
│ │ └── xcuserdata
│ │ │ └── alep.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── TwitterUI
│ ├── Assets.xcassets
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── Contents.json
│ ├── ContentView.swift
│ ├── Info.plist
│ ├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
│ ├── Tweet.swift
│ └── TwitterUIApp.swift
└── Weather
├── Weather.xcodeproj
├── project.pbxproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
│ └── xcuserdata
│ │ └── alep.xcuserdatad
│ │ └── UserInterfaceState.xcuserstate
├── xcshareddata
│ └── xcschemes
│ │ └── Weather.xcscheme
└── xcuserdata
│ └── alep.xcuserdatad
│ └── xcschemes
│ └── xcschememanagement.plist
└── Weather
├── Assets.xcassets
├── AccentColor.colorset
│ └── Contents.json
├── AppIcon.appiconset
│ └── Contents.json
└── Contents.json
├── Info.plist
├── Preview Content
└── Preview Assets.xcassets
│ └── Contents.json
├── Weather.swift
├── WeatherApp.swift
├── WeatherService.swift
├── WeatherView.swift
└── WeatherViewModel.swift
/Color Picker Demo/Color Picker Demo.mov:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Color Picker Demo/Color Picker Demo.mov
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Color Picker Demo/Color Picker Demo.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Color Picker Demo.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Color_Picker_DemoApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Color_Picker_DemoApp.swift
3 | // Color Picker Demo
4 | //
5 | // Created by Alejandrina Patron on 6/24/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Color_Picker_DemoApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Color Picker Demo
4 | //
5 | // Created by Alejandrina Patron on 6/24/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | struct ContentView: View {
11 |
12 | @State private var selectedColor = Color.black
13 |
14 | var body: some View {
15 | VStack(alignment: .center) {
16 | Text("Color Picker Demo").foregroundColor(selectedColor).font(.largeTitle)
17 | ColorPicker(
18 | "Pick a color",
19 | selection: $selectedColor
20 | ).frame(width: 150, height: 150)
21 | Spacer()
22 | }.padding(.vertical, 70)
23 | }
24 | }
25 |
26 | struct ContentView_Previews: PreviewProvider {
27 | static var previews: some View {
28 | ContentView()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Color Picker Demo/Color Picker Demo/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Directions/Directions.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Directions/Directions.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Directions/Directions.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Directions/Directions.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Directions/Directions.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Directions.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Directions/Directions/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Directions
4 | //
5 | // Created by Alejandrina Patrón López on 9/11/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Directions/Directions/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Directions/Directions/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Directions/Directions/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Directions/Directions/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Directions
4 | //
5 |
6 | import MapKit
7 | import SwiftUI
8 |
9 | struct ContentView: View {
10 |
11 | @State private var directions: [String] = []
12 | @State private var showDirections = false
13 |
14 | var body: some View {
15 | VStack {
16 | MapView(directions: $directions)
17 |
18 | Button(action: {
19 | self.showDirections.toggle()
20 | }, label: {
21 | Text("Show directions")
22 | })
23 | .disabled(directions.isEmpty)
24 | .padding()
25 | }.sheet(isPresented: $showDirections, content: {
26 | VStack(spacing: 0) {
27 | Text("Directions")
28 | .font(.largeTitle)
29 | .bold()
30 | .padding()
31 |
32 | Divider().background(Color.blue)
33 |
34 | List(0.. MapViewCoordinator {
48 | return MapViewCoordinator()
49 | }
50 |
51 | func makeUIView(context: Context) -> MKMapView {
52 | let mapView = MKMapView()
53 | mapView.delegate = context.coordinator
54 |
55 | let region = MKCoordinateRegion(
56 | center: CLLocationCoordinate2D(latitude: 40.71, longitude: -74),
57 | span: MKCoordinateSpan(latitudeDelta: 0.5, longitudeDelta: 0.5))
58 | mapView.setRegion(region, animated: true)
59 |
60 | // NYC
61 | let p1 = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 40.71, longitude: -74))
62 |
63 | // Boston
64 | let p2 = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 42.36, longitude: -71.05))
65 |
66 | let request = MKDirections.Request()
67 | request.source = MKMapItem(placemark: p1)
68 | request.destination = MKMapItem(placemark: p2)
69 | request.transportType = .automobile
70 |
71 | let directions = MKDirections(request: request)
72 | directions.calculate { response, error in
73 | guard let route = response?.routes.first else { return }
74 | mapView.addAnnotations([p1, p2])
75 | mapView.addOverlay(route.polyline)
76 | mapView.setVisibleMapRect(
77 | route.polyline.boundingMapRect,
78 | edgePadding: UIEdgeInsets(top: 20, left: 20, bottom: 20, right: 20),
79 | animated: true)
80 | self.directions = route.steps.map { $0.instructions }.filter { !$0.isEmpty }
81 | }
82 | return mapView
83 | }
84 |
85 | func updateUIView(_ uiView: MKMapView, context: Context) {
86 | }
87 |
88 | class MapViewCoordinator: NSObject, MKMapViewDelegate {
89 | func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
90 | let renderer = MKPolylineRenderer(overlay: overlay)
91 | renderer.strokeColor = .systemBlue
92 | renderer.lineWidth = 5
93 | return renderer
94 | }
95 | }
96 | }
97 |
98 | struct ContentView_Previews: PreviewProvider {
99 | static var previews: some View {
100 | ContentView()
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Directions/Directions/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Directions/Directions/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Directions/Directions/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Directions
4 | //
5 | // Created by Alejandrina Patrón López on 9/11/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Expandable Button Panel/Expandable Button Panel.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Expandable Button Panel.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Expandable Button Panel
4 | //
5 | // Created by Alejandrina Patrón López on 8/4/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Expandable Button Panel
4 | //
5 | // Created by Alejandrina Patrón López on 8/4/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ContentView: View {
12 |
13 | @State private var showAlert: Bool = false
14 | @State private var alertLabel: String = ""
15 |
16 | var body: some View {
17 | NavigationView {
18 | ZStack {
19 | // List
20 | List(1...20, id: \.self) { i in
21 | Text("Item #\(i)")
22 | .padding()
23 | }
24 |
25 | // Floating Button Panel
26 | VStack {
27 | Spacer()
28 | HStack {
29 | Spacer()
30 | ExpandableButtonPanel(
31 | primaryItem: ExpandableButtonItem(label: "➕"),
32 | secondaryItems: [
33 | ExpandableButtonItem(label: "🌞") {
34 | self.alertLabel = "🌞"
35 | self.showAlert.toggle()
36 | },
37 | ExpandableButtonItem(label: "🥑") {
38 | self.alertLabel = "🥑"
39 | self.showAlert.toggle()
40 | }
41 | ]
42 | )
43 | .padding()
44 | }
45 | }
46 | }
47 | .alert(isPresented: $showAlert) {
48 | return Alert(title: Text("You selected \(self.alertLabel)"))
49 | }
50 | .navigationBarTitle("Numbers")
51 | }
52 | }
53 | }
54 |
55 | struct ExpandableButtonItem: Identifiable {
56 | let id = UUID()
57 | let label: String
58 | private(set) var action: (() -> Void)? = nil
59 | }
60 |
61 | struct ExpandableButtonPanel: View {
62 |
63 | let primaryItem: ExpandableButtonItem
64 | let secondaryItems: [ExpandableButtonItem]
65 |
66 | private let noop: () -> Void = {}
67 | private let size: CGFloat = 70
68 | private var cornerRadius: CGFloat {
69 | get { size / 2 }
70 | }
71 | private let shadowColor = Color.black.opacity(0.4)
72 | private let shadowPosition: (x: CGFloat, y: CGFloat) = (x: 2, y: 2)
73 | private let shadowRadius: CGFloat = 3
74 |
75 | @State private var isExpanded = false
76 |
77 | var body: some View {
78 | VStack {
79 | ForEach(secondaryItems) { item in
80 | Button(item.label, action: item.action ?? self.noop)
81 | .frame(
82 | width: self.isExpanded ? self.size : 0,
83 | height: self.isExpanded ? self.size : 0)
84 | }
85 |
86 | Button(primaryItem.label, action: {
87 | withAnimation {
88 | self.isExpanded.toggle()
89 | }
90 | self.primaryItem.action?()
91 | })
92 | .frame(width: size, height: size)
93 | }
94 | .background(Color(UIColor.systemPurple))
95 | .cornerRadius(cornerRadius)
96 | .font(.title)
97 | .shadow(
98 | color: shadowColor,
99 | radius: shadowRadius,
100 | x: shadowPosition.x,
101 | y: shadowPosition.y
102 | )
103 | }
104 | }
105 |
106 | struct ContentView_Previews: PreviewProvider {
107 | static var previews: some View {
108 | ContentView()
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Expandable Button Panel/Expandable Button Panel/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Expandable Button Panel
4 | //
5 | // Created by Alejandrina Patrón López on 8/4/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Guess the Number/Final/Guess the Number.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Guess the Number.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/GuessingGame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GuessingGame.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/25/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // Model
12 | struct GuessingGame {
13 | enum GameStatus {
14 | case won
15 | case lost
16 | case playing
17 | }
18 |
19 | var elementToGuess: GuessElement
20 | var maxGuesses: Int
21 |
22 | private(set) var gameStatus: GameStatus
23 | private var numberOfGuesses: Int
24 |
25 | init(elementToGuess: GuessElement, maxGuesses: Int) {
26 | self.elementToGuess = elementToGuess
27 | self.maxGuesses = maxGuesses
28 | gameStatus = .playing
29 | numberOfGuesses = 0
30 | }
31 |
32 | mutating func makeGuess(withElement element: GuessElement) {
33 | numberOfGuesses += 1
34 | if element == elementToGuess {
35 | gameStatus = .won
36 | } else if numberOfGuesses >= maxGuesses {
37 | gameStatus = .lost
38 | } else {
39 | gameStatus = .playing
40 | }
41 | }
42 |
43 | mutating func reset(with elementToGuess: GuessElement) {
44 | self.elementToGuess = elementToGuess
45 | numberOfGuesses = 0
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/NumberGuessingGame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumberGuessingGame.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/25/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // ViewModel
12 | class NumberGuessingGame: ObservableObject {
13 |
14 | @Published var currentGuess = ""
15 | @Published var showAlert = false
16 | @Published var alertTitle = ""
17 | @Published var guessLabel = "Guess the number!"
18 |
19 | private var model: GuessingGame
20 | private var maxGuesses = 3
21 |
22 | init() {
23 | model = GuessingGame(elementToGuess: NumberGuessingGame.generateGuess(), maxGuesses: maxGuesses)
24 | }
25 |
26 | func makeGuess(withNumber number: Int) {
27 | model.makeGuess(withElement: number)
28 | switch model.gameStatus {
29 | case .lost:
30 | alertTitle = "You lost! 😢"
31 | showAlert = true
32 | guessLabel = "Guess the Number!"
33 | case .won:
34 | alertTitle = "You won! 🥳"
35 | showAlert = true
36 | guessLabel = "Guess the Number!"
37 | case .playing:
38 | guessLabel = "Try again!"
39 | }
40 | currentGuess = ""
41 | }
42 |
43 | func reset() {
44 | model.reset(with: NumberGuessingGame.generateGuess())
45 | showAlert = false
46 | }
47 |
48 | private static func generateGuess() -> Int {
49 | return Int.random(in: 1...10)
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/NumberGuessingGameView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct NumberGuessingGameView: View {
12 |
13 | @ObservedObject private var gameViewModel: NumberGuessingGame
14 |
15 | public init(gameViewModel: NumberGuessingGame) {
16 | self.gameViewModel = gameViewModel
17 | }
18 |
19 | var body: some View {
20 | VStack {
21 | Text(self.gameViewModel.guessLabel)
22 | TextField(
23 | "Type a number",
24 | text: self.$gameViewModel.currentGuess
25 | )
26 | .keyboardType(.numberPad)
27 | .textFieldStyle(RoundedBorderTextFieldStyle())
28 | .multilineTextAlignment(.center)
29 |
30 | Button(
31 | "Enter guess",
32 | action: {
33 | if let userGuess = Int(self.gameViewModel.currentGuess) {
34 | self.gameViewModel.makeGuess(withNumber: userGuess)
35 | }
36 | }
37 | ).alert(isPresented: $gameViewModel.showAlert) {
38 | return Alert(
39 | title: Text(self.gameViewModel.alertTitle),
40 | message: nil,
41 | dismissButton: .default(Text("Start new game"), action: {
42 | self.gameViewModel.reset()
43 | })
44 | )
45 | }
46 | }.padding()
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Guess the Number/Final/Guess the Number/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = NumberGuessingGameView(gameViewModel: NumberGuessingGame())
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Guess the Number/Skeleton/Guess the Number.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Guess the Number.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/GuessingGame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GuessingGame.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/25/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // Model
12 | struct GuessingGame {
13 | enum GameStatus {
14 | case won
15 | case lost
16 | case playing
17 | }
18 |
19 | var elementToGuess: GuessElement
20 | var maxGuesses: Int
21 |
22 | // TODO
23 | // private(set) var gameStatus: GameStatus
24 | // private var numberOfGuesses: Int
25 |
26 | mutating func makeGuess(withElement element: GuessElement) {
27 | // TODO
28 | }
29 |
30 | mutating func reset(with elementToGuess: GuessElement) {
31 | // TODO
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/NumberGuessingGame.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NumberGuessingGame.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/25/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | // ViewModel
12 | class NumberGuessingGame: ObservableObject {
13 |
14 | @Published var currentGuess = ""
15 | @Published var showAlert = false
16 | @Published var alertTitle = ""
17 | @Published var guessLabel = "Guess the number!"
18 |
19 | // private var model: GuessingGame
20 | // private var maxGuesses = 3
21 |
22 | func makeGuess(withNumber number: Int) {
23 | // TODO
24 | }
25 |
26 | func reset() {
27 | // TODO
28 | }
29 |
30 | private static func generateGuess() -> Int {
31 | // TODO
32 | return 1
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/NumberGuessingGameView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct NumberGuessingGameView: View {
12 |
13 | @ObservedObject private var gameViewModel: NumberGuessingGame
14 |
15 | public init(gameViewModel: NumberGuessingGame) {
16 | self.gameViewModel = gameViewModel
17 | }
18 |
19 | var body: some View {
20 | // TODO
21 | EmptyView()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Guess the Number/Skeleton/Guess the Number/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Guess the Number
4 | //
5 | // Created by Alejandrina Patrón López on 7/19/20.
6 | // Copyright © 2020 Alejandrina Patrón López. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = NumberGuessingGameView(gameViewModel: NumberGuessingGame())
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Lists and Navigation/Lists and Navigation.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Lists and Navigation.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Lists and Navigation
4 | //
5 | // Created by Alejandrina Patrón López on 9/3/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Listas y Navegación
4 | //
5 |
6 |
7 | import SwiftUI
8 |
9 | struct ContentView: View {
10 |
11 | private let emojiList: [EmojiItem] = [
12 | EmojiItem(
13 | emoji: "👾",
14 | name: "Alien Monster",
15 | description: "A friendly-looking, tentacled space creature with two eyes."),
16 | EmojiItem(
17 | emoji: "🥑",
18 | name: "Avocado",
19 | description: "A pear-shaped avocado, sliced in half to show its yellow-green flesh and "
20 | + "large brown pit."),
21 | EmojiItem(
22 | emoji: "🍟",
23 | name: "French Fries",
24 | description: "Thin-cut, golden-brown French fries, served in a red carton, as at "
25 | + "McDonald’s."),
26 | EmojiItem(
27 | emoji: "🍕",
28 | name: "Pizza",
29 | description: "A slice of pepperoni pizza, topped with black olives on Google. WhatsApp "
30 | + "adds green pepper, Samsung white onion."),
31 | EmojiItem(
32 | emoji: "🧩",
33 | name: "Puzzle Piece",
34 | description: "Puzzle Piece was approved as part of Unicode 11.0 in 2018 under the name "
35 | + "“Jigsaw Puzzle Piece” and added to Emoji 11.0 in 2018."),
36 | EmojiItem(
37 | emoji: "🚀",
38 | name: "Rocket",
39 | description: "A rocket being propelled into space."),
40 | EmojiItem(
41 | emoji: "🗽",
42 | name: "Statue of Liberty",
43 | description: "The Statue of Liberty, often used as a depiction of New York City."),
44 | EmojiItem(
45 | emoji: "🧸",
46 | name: "Teddy Bear",
47 | description: "A classic teddy bear, as snuggled by a child when going to sleep."),
48 | EmojiItem(
49 | emoji: "🦄",
50 | name: "Unicorn",
51 | description: "The face of a unicorn, a mythical creature in the form of a white horse with "
52 | + "a single, long horn on its forehead."),
53 | EmojiItem(
54 | emoji: "👩🏽💻",
55 | name: "Woman Technologist",
56 | description: "A woman behind a computer screen, working in the field of technology."),
57 | EmojiItem(
58 | emoji: "🗺",
59 | name: "World Map",
60 | description: "A rectangular map of the world. Generally depicted as a paper map creased at "
61 | + "its folds, Earth’s surface shown in green on blue ocean."),
62 | ]
63 |
64 | var body: some View {
65 | NavigationView {
66 | List(emojiList) { emojiItem in
67 | NavigationLink(destination: DetailsView(emojiItem: emojiItem)) {
68 | HStack {
69 | EmojiCircleView(emojiItem: emojiItem)
70 | Text(emojiItem.name)
71 | .font(.headline)
72 | }.padding(7)
73 | }
74 | }
75 | .navigationBarTitle("Emoji")
76 | }
77 | }
78 | }
79 |
80 | struct DetailsView: View {
81 |
82 | let emojiItem: EmojiItem
83 |
84 | var body: some View {
85 | VStack(alignment: .leading) {
86 | HStack {
87 | EmojiCircleView(emojiItem: emojiItem)
88 | .padding(.trailing, 5)
89 |
90 | Text(emojiItem.name)
91 | .font(.largeTitle)
92 | .bold()
93 |
94 | Spacer()
95 | }
96 |
97 | Text(emojiItem.description)
98 | .padding(.top)
99 |
100 | Spacer()
101 | }
102 | .padding()
103 | .navigationBarTitle(Text(emojiItem.name), displayMode: .inline)
104 | }
105 | }
106 |
107 | struct EmojiCircleView: View {
108 | let emojiItem: EmojiItem
109 |
110 | var body: some View {
111 | ZStack {
112 | Text(emojiItem.emoji)
113 | .shadow(radius: 3)
114 | .font(.largeTitle)
115 | .frame(width: 65, height: 65)
116 | .overlay(
117 | Circle().stroke(Color.purple, lineWidth: 3)
118 | )
119 | }
120 | }
121 | }
122 |
123 | struct EmojiItem: Identifiable {
124 | let id = UUID()
125 | let emoji: String
126 | let name: String
127 | let description: String
128 | }
129 |
130 | struct ContentView_Previews: PreviewProvider {
131 | static var previews: some View {
132 | ContentView()
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Lists and Navigation/Lists and Navigation/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Lists and Navigation
4 | //
5 | // Created by Alejandrina Patrón López on 9/3/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/Music Search/Music Search.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Music Search/Music Search.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Music Search/Music Search.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Music Search/Music Search.xcodeproj/project.xcworkspace/xcuserdata/ale_patron.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Music Search/Music Search.xcodeproj/xcshareddata/xcschemes/Music Search.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/Music Search/Music Search.xcodeproj/xcuserdata/ale_patron.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Music Search.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | ED4EF5E825411B24006C9E60
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Music Search/Music Search/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Music Search
4 | //
5 | // SwiftUI Tutorial - Music Search App (JSON, URLSession, NSCache)
6 |
7 | import Combine
8 | import SwiftUI
9 | import UIKit
10 |
11 | struct ContentView: View {
12 |
13 | @State private var searchTerm: String = ""
14 |
15 | @ObservedObject var viewModel: SongListViewModel
16 |
17 | var body: some View {
18 | NavigationView {
19 | VStack {
20 | SearchBar(searchTerm: $viewModel.artistSearch)
21 | if viewModel.songs.isEmpty {
22 | ZeroStateView()
23 | } else {
24 | List(viewModel.songs) { song in
25 | SongView(song: song)
26 | }.listStyle(PlainListStyle())
27 | }
28 | }.navigationBarTitle("Music Search")
29 | }
30 | }
31 | }
32 |
33 | struct ZeroStateView: View {
34 | var body: some View {
35 | VStack {
36 | Spacer()
37 | Image(systemName: "music.note")
38 | .font(.system(size: 85))
39 | .padding(.bottom)
40 | Text("Start searching for music...")
41 | .font(.title)
42 | Spacer()
43 | }
44 | .padding()
45 | .foregroundColor(Color(.systemIndigo))
46 | }
47 | }
48 |
49 | struct SongView: View {
50 |
51 | public let song: Song
52 |
53 | @StateObject private var imageLoader: ImageLoader = ImageLoader()
54 |
55 | var body: some View {
56 | HStack {
57 | ArtworkView(image: imageLoader.image)
58 | .padding(.trailing)
59 | VStack(alignment: .leading) {
60 | Text(song.trackName)
61 | Text(song.artistName)
62 | .font(.footnote)
63 | .foregroundColor(.gray)
64 | }
65 | }
66 | .onAppear {
67 | imageLoader.loadImage(forSong: song)
68 | }
69 | .padding()
70 | }
71 | }
72 |
73 | struct ArtworkView: View {
74 | let image: Image?
75 |
76 | var body: some View {
77 | ZStack {
78 | if image != nil {
79 | image
80 | } else {
81 | Color(.systemIndigo)
82 | Image(systemName: "music.note")
83 | .font(.largeTitle)
84 | .foregroundColor(.white)
85 | }
86 | }
87 | .frame(width: 50, height: 50)
88 | .shadow(radius: 5)
89 | .padding(.trailing, 5)
90 | }
91 | }
92 |
93 | struct SearchBar: UIViewRepresentable {
94 | typealias UIViewType = UISearchBar
95 |
96 | @Binding var searchTerm: String
97 |
98 | func makeUIView(context: Context) -> UISearchBar {
99 | let searchBar = UISearchBar(frame: .zero)
100 | searchBar.delegate = context.coordinator
101 | searchBar.searchBarStyle = .minimal
102 | searchBar.placeholder = "Shawn Mendes"
103 | return searchBar
104 | }
105 |
106 | func updateUIView(_ uiView: UISearchBar, context: Context) {
107 | }
108 |
109 | func makeCoordinator() -> SearchBarCoordinator {
110 | return SearchBarCoordinator(searchTerm: $searchTerm)
111 | }
112 |
113 | class SearchBarCoordinator: NSObject, UISearchBarDelegate {
114 |
115 | @Binding var searchTerm: String
116 |
117 | init(searchTerm: Binding) {
118 | self._searchTerm = searchTerm
119 | }
120 |
121 | func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
122 | searchTerm = searchBar.text ?? ""
123 | UIApplication.shared.windows.first { $0.isKeyWindow }?.endEditing(true)
124 | }
125 | }
126 | }
127 |
128 | //struct ContentView_Previews: PreviewProvider {
129 | // static var previews: some View {
130 | // ContentView()
131 | // }
132 | //}
133 |
--------------------------------------------------------------------------------
/Music Search/Music Search/ImageCache.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageCache.swift
3 | // Music Search
4 | //
5 |
6 | import UIKit
7 |
8 | class ImageCache {
9 | public static var shared = ImageCache()
10 |
11 | private var cache = NSCache()
12 |
13 | func getValue(forKey key: String) -> UIImage? {
14 | return cache.object(forKey: NSString(string: key))
15 | }
16 |
17 | func setValue(_ image: UIImage, forKey key: String) {
18 | cache.setObject(image, forKey: NSString(string: key))
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Music Search/Music Search/ImageLoader.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ImageLoader.swift
3 | // Music Search
4 | //
5 | // Created by Alejandrina Patron on 10/25/20.
6 | //
7 |
8 | import Foundation
9 | import SwiftUI
10 |
11 | class ImageLoader: ObservableObject {
12 | @Published public private(set) var image: Image?
13 |
14 | private var dataTask: URLSessionDataTask?
15 | private let keyPrefix = "artwork-"
16 |
17 | deinit {
18 | dataTask?.cancel()
19 | }
20 |
21 | func loadImage(forSong song: Song) {
22 | if let cachedImage = ImageCache.shared.getValue(forKey: "\(keyPrefix)\(song.id)") {
23 | DispatchQueue.main.async {
24 | self.image = Image(uiImage: cachedImage)
25 | return
26 | }
27 | }
28 |
29 | guard let imageUrl = URL(string: song.artworkUrl) else { return }
30 | dataTask = URLSession.shared.dataTask(with: imageUrl) { data, _, _ in
31 | guard let data = data else { return }
32 | guard let remoteImage = UIImage(data: data) else { return }
33 | DispatchQueue.main.async {
34 | self.image = Image(uiImage: remoteImage)
35 | ImageCache.shared.setValue(remoteImage, forKey: "\(self.keyPrefix)\(song.id)")
36 | }
37 | }
38 | dataTask?.resume()
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Music_SearchApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Music_SearchApp.swift
3 | // Music Search
4 | //
5 | // Created by Alejandrina Patron on 10/21/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Music_SearchApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView(viewModel: SongListViewModel())
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Music Search/Music Search/Song.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Song.swift
3 | // Music Search
4 | //
5 | // Created by Alejandrina Patron on 10/21/20.
6 | //
7 |
8 | import Foundation
9 | import UIKit
10 |
11 | struct SongResponse: Decodable {
12 | var songs: [Song]
13 |
14 | enum CodingKeys: String, CodingKey {
15 | case songs = "results"
16 | }
17 | }
18 |
19 | struct Song: Decodable, Identifiable {
20 | let id: Int
21 | let trackName: String
22 | let artistName: String
23 | let artworkUrl: String
24 |
25 | enum CodingKeys: String, CodingKey {
26 | case id = "trackId"
27 | case trackName
28 | case artistName
29 | case artworkUrl = "artworkUrl60"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Music Search/Music Search/SongListViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SongListViewModel.swift
3 | // Music Search
4 | //
5 | // Created by Alejandrina Patron on 10/25/20.
6 | //
7 |
8 | import Combine
9 | import Foundation
10 |
11 | class SongListViewModel: ObservableObject {
12 | @Published var artistSearch: String = ""
13 | @Published public private(set) var songs: [Song] = []
14 |
15 | private var disposables = Set()
16 |
17 | init() {
18 | $artistSearch
19 | .sink(receiveValue: loadSongs(forArtist:))
20 | .store(in: &disposables)
21 | }
22 |
23 | private func loadSongs(forArtist artist: String) {
24 | guard let url = buildUrl(forArtist: artist) else { return }
25 |
26 | URLSession.shared.dataTask(with: url) { data, _, _ in
27 | guard let data = data else { return }
28 | if let songResponse = try? JSONDecoder().decode(SongResponse.self, from: data) {
29 | DispatchQueue.main.async {
30 | self.songs = songResponse.songs
31 | }
32 | }
33 | }.resume()
34 | }
35 |
36 | private func buildUrl(forArtist artist: String) -> URL? {
37 | guard !artist.isEmpty else { return nil }
38 |
39 | let queryItems = [
40 | URLQueryItem(name: "term", value: artist),
41 | URLQueryItem(name: "entity", value: "song"),
42 | ]
43 | var urlComps = URLComponents(string: "https://itunes.apple.com/search")
44 | urlComps?.queryItems = queryItems
45 |
46 | return urlComps?.url
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Programming Quotes/Programming Quotes.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Programming Quotes.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Programming Quotes
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ContentView: View {
9 |
10 | @State private var quoteData: QuoteData?
11 |
12 | var body: some View {
13 | HStack {
14 | Spacer()
15 |
16 | VStack(alignment: .trailing) {
17 | Spacer()
18 |
19 | Text(quoteData?.en ?? "")
20 | .font(.title2)
21 | Text("- \(quoteData?.author ?? "")")
22 | .font(.title2)
23 | .padding(.top)
24 |
25 | Spacer()
26 |
27 | Button(action: loadData) {
28 | Image(systemName: "arrow.clockwise")
29 | }
30 | .font(.title)
31 | .padding(.top)
32 | }
33 | }
34 | .multilineTextAlignment(.trailing)
35 | .padding()
36 | .onAppear(perform: loadData)
37 | }
38 |
39 | private func loadData() {
40 | guard let url = URL(string: "https://programming-quotes-api.herokuapp.com/quotes/random") else {
41 | return
42 | }
43 | URLSession.shared.dataTask(with: url) { data, response, error in
44 | guard let data = data else { return }
45 | if let decodedData = try? JSONDecoder().decode(QuoteData.self, from: data) {
46 | DispatchQueue.main.async {
47 | self.quoteData = decodedData
48 | }
49 | }
50 | }.resume()
51 | }
52 | }
53 |
54 | struct QuoteData: Decodable {
55 | var _id: String
56 | var en: String
57 | var author: String
58 | var id: String
59 | }
60 |
61 | struct ContentView_Previews: PreviewProvider {
62 | static var previews: some View {
63 | ContentView()
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Programming Quotes/Programming Quotes/Programming_QuotesApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Programming_QuotesApp.swift
3 | // Programming Quotes
4 | //
5 | // Created by Alejandrina Patrón López on 9/28/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Programming_QuotesApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Progress View/Progress View.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | F67709872533A00B006E67C9 /* Progress_ViewApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = F67709862533A00B006E67C9 /* Progress_ViewApp.swift */; };
11 | F67709892533A00B006E67C9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F67709882533A00B006E67C9 /* ContentView.swift */; };
12 | F677098B2533A00D006E67C9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F677098A2533A00D006E67C9 /* Assets.xcassets */; };
13 | F677098E2533A00D006E67C9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = F677098D2533A00D006E67C9 /* Preview Assets.xcassets */; };
14 | /* End PBXBuildFile section */
15 |
16 | /* Begin PBXFileReference section */
17 | F67709832533A00B006E67C9 /* Progress View.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Progress View.app"; sourceTree = BUILT_PRODUCTS_DIR; };
18 | F67709862533A00B006E67C9 /* Progress_ViewApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Progress_ViewApp.swift; sourceTree = ""; };
19 | F67709882533A00B006E67C9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
20 | F677098A2533A00D006E67C9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
21 | F677098D2533A00D006E67C9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
22 | F677098F2533A00D006E67C9 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
23 | /* End PBXFileReference section */
24 |
25 | /* Begin PBXFrameworksBuildPhase section */
26 | F67709802533A00B006E67C9 /* Frameworks */ = {
27 | isa = PBXFrameworksBuildPhase;
28 | buildActionMask = 2147483647;
29 | files = (
30 | );
31 | runOnlyForDeploymentPostprocessing = 0;
32 | };
33 | /* End PBXFrameworksBuildPhase section */
34 |
35 | /* Begin PBXGroup section */
36 | F677097A2533A00B006E67C9 = {
37 | isa = PBXGroup;
38 | children = (
39 | F67709852533A00B006E67C9 /* Progress View */,
40 | F67709842533A00B006E67C9 /* Products */,
41 | );
42 | sourceTree = "";
43 | };
44 | F67709842533A00B006E67C9 /* Products */ = {
45 | isa = PBXGroup;
46 | children = (
47 | F67709832533A00B006E67C9 /* Progress View.app */,
48 | );
49 | name = Products;
50 | sourceTree = "";
51 | };
52 | F67709852533A00B006E67C9 /* Progress View */ = {
53 | isa = PBXGroup;
54 | children = (
55 | F67709862533A00B006E67C9 /* Progress_ViewApp.swift */,
56 | F67709882533A00B006E67C9 /* ContentView.swift */,
57 | F677098A2533A00D006E67C9 /* Assets.xcassets */,
58 | F677098F2533A00D006E67C9 /* Info.plist */,
59 | F677098C2533A00D006E67C9 /* Preview Content */,
60 | );
61 | path = "Progress View";
62 | sourceTree = "";
63 | };
64 | F677098C2533A00D006E67C9 /* Preview Content */ = {
65 | isa = PBXGroup;
66 | children = (
67 | F677098D2533A00D006E67C9 /* Preview Assets.xcassets */,
68 | );
69 | path = "Preview Content";
70 | sourceTree = "";
71 | };
72 | /* End PBXGroup section */
73 |
74 | /* Begin PBXNativeTarget section */
75 | F67709822533A00B006E67C9 /* Progress View */ = {
76 | isa = PBXNativeTarget;
77 | buildConfigurationList = F67709922533A00D006E67C9 /* Build configuration list for PBXNativeTarget "Progress View" */;
78 | buildPhases = (
79 | F677097F2533A00B006E67C9 /* Sources */,
80 | F67709802533A00B006E67C9 /* Frameworks */,
81 | F67709812533A00B006E67C9 /* Resources */,
82 | );
83 | buildRules = (
84 | );
85 | dependencies = (
86 | );
87 | name = "Progress View";
88 | productName = "Progress View";
89 | productReference = F67709832533A00B006E67C9 /* Progress View.app */;
90 | productType = "com.apple.product-type.application";
91 | };
92 | /* End PBXNativeTarget section */
93 |
94 | /* Begin PBXProject section */
95 | F677097B2533A00B006E67C9 /* Project object */ = {
96 | isa = PBXProject;
97 | attributes = {
98 | LastSwiftUpdateCheck = 1200;
99 | LastUpgradeCheck = 1200;
100 | TargetAttributes = {
101 | F67709822533A00B006E67C9 = {
102 | CreatedOnToolsVersion = 12.0;
103 | };
104 | };
105 | };
106 | buildConfigurationList = F677097E2533A00B006E67C9 /* Build configuration list for PBXProject "Progress View" */;
107 | compatibilityVersion = "Xcode 9.3";
108 | developmentRegion = en;
109 | hasScannedForEncodings = 0;
110 | knownRegions = (
111 | en,
112 | Base,
113 | );
114 | mainGroup = F677097A2533A00B006E67C9;
115 | productRefGroup = F67709842533A00B006E67C9 /* Products */;
116 | projectDirPath = "";
117 | projectRoot = "";
118 | targets = (
119 | F67709822533A00B006E67C9 /* Progress View */,
120 | );
121 | };
122 | /* End PBXProject section */
123 |
124 | /* Begin PBXResourcesBuildPhase section */
125 | F67709812533A00B006E67C9 /* Resources */ = {
126 | isa = PBXResourcesBuildPhase;
127 | buildActionMask = 2147483647;
128 | files = (
129 | F677098E2533A00D006E67C9 /* Preview Assets.xcassets in Resources */,
130 | F677098B2533A00D006E67C9 /* Assets.xcassets in Resources */,
131 | );
132 | runOnlyForDeploymentPostprocessing = 0;
133 | };
134 | /* End PBXResourcesBuildPhase section */
135 |
136 | /* Begin PBXSourcesBuildPhase section */
137 | F677097F2533A00B006E67C9 /* Sources */ = {
138 | isa = PBXSourcesBuildPhase;
139 | buildActionMask = 2147483647;
140 | files = (
141 | F67709892533A00B006E67C9 /* ContentView.swift in Sources */,
142 | F67709872533A00B006E67C9 /* Progress_ViewApp.swift in Sources */,
143 | );
144 | runOnlyForDeploymentPostprocessing = 0;
145 | };
146 | /* End PBXSourcesBuildPhase section */
147 |
148 | /* Begin XCBuildConfiguration section */
149 | F67709902533A00D006E67C9 /* Debug */ = {
150 | isa = XCBuildConfiguration;
151 | buildSettings = {
152 | ALWAYS_SEARCH_USER_PATHS = NO;
153 | CLANG_ANALYZER_NONNULL = YES;
154 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
156 | CLANG_CXX_LIBRARY = "libc++";
157 | CLANG_ENABLE_MODULES = YES;
158 | CLANG_ENABLE_OBJC_ARC = YES;
159 | CLANG_ENABLE_OBJC_WEAK = YES;
160 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
161 | CLANG_WARN_BOOL_CONVERSION = YES;
162 | CLANG_WARN_COMMA = YES;
163 | CLANG_WARN_CONSTANT_CONVERSION = YES;
164 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
165 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
166 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
167 | CLANG_WARN_EMPTY_BODY = YES;
168 | CLANG_WARN_ENUM_CONVERSION = YES;
169 | CLANG_WARN_INFINITE_RECURSION = YES;
170 | CLANG_WARN_INT_CONVERSION = YES;
171 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
172 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
173 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
174 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
175 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
176 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
177 | CLANG_WARN_STRICT_PROTOTYPES = YES;
178 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
179 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
180 | CLANG_WARN_UNREACHABLE_CODE = YES;
181 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
182 | COPY_PHASE_STRIP = NO;
183 | DEBUG_INFORMATION_FORMAT = dwarf;
184 | ENABLE_STRICT_OBJC_MSGSEND = YES;
185 | ENABLE_TESTABILITY = YES;
186 | GCC_C_LANGUAGE_STANDARD = gnu11;
187 | GCC_DYNAMIC_NO_PIC = NO;
188 | GCC_NO_COMMON_BLOCKS = YES;
189 | GCC_OPTIMIZATION_LEVEL = 0;
190 | GCC_PREPROCESSOR_DEFINITIONS = (
191 | "DEBUG=1",
192 | "$(inherited)",
193 | );
194 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
195 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
196 | GCC_WARN_UNDECLARED_SELECTOR = YES;
197 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
198 | GCC_WARN_UNUSED_FUNCTION = YES;
199 | GCC_WARN_UNUSED_VARIABLE = YES;
200 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
201 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
202 | MTL_FAST_MATH = YES;
203 | ONLY_ACTIVE_ARCH = YES;
204 | SDKROOT = iphoneos;
205 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
206 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
207 | };
208 | name = Debug;
209 | };
210 | F67709912533A00D006E67C9 /* Release */ = {
211 | isa = XCBuildConfiguration;
212 | buildSettings = {
213 | ALWAYS_SEARCH_USER_PATHS = NO;
214 | CLANG_ANALYZER_NONNULL = YES;
215 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
216 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
217 | CLANG_CXX_LIBRARY = "libc++";
218 | CLANG_ENABLE_MODULES = YES;
219 | CLANG_ENABLE_OBJC_ARC = YES;
220 | CLANG_ENABLE_OBJC_WEAK = YES;
221 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
222 | CLANG_WARN_BOOL_CONVERSION = YES;
223 | CLANG_WARN_COMMA = YES;
224 | CLANG_WARN_CONSTANT_CONVERSION = YES;
225 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
226 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
227 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
228 | CLANG_WARN_EMPTY_BODY = YES;
229 | CLANG_WARN_ENUM_CONVERSION = YES;
230 | CLANG_WARN_INFINITE_RECURSION = YES;
231 | CLANG_WARN_INT_CONVERSION = YES;
232 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
233 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
234 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
235 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
236 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
237 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
238 | CLANG_WARN_STRICT_PROTOTYPES = YES;
239 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
240 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
241 | CLANG_WARN_UNREACHABLE_CODE = YES;
242 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
243 | COPY_PHASE_STRIP = NO;
244 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
245 | ENABLE_NS_ASSERTIONS = NO;
246 | ENABLE_STRICT_OBJC_MSGSEND = YES;
247 | GCC_C_LANGUAGE_STANDARD = gnu11;
248 | GCC_NO_COMMON_BLOCKS = YES;
249 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
250 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
251 | GCC_WARN_UNDECLARED_SELECTOR = YES;
252 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
253 | GCC_WARN_UNUSED_FUNCTION = YES;
254 | GCC_WARN_UNUSED_VARIABLE = YES;
255 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
256 | MTL_ENABLE_DEBUG_INFO = NO;
257 | MTL_FAST_MATH = YES;
258 | SDKROOT = iphoneos;
259 | SWIFT_COMPILATION_MODE = wholemodule;
260 | SWIFT_OPTIMIZATION_LEVEL = "-O";
261 | VALIDATE_PRODUCT = YES;
262 | };
263 | name = Release;
264 | };
265 | F67709932533A00D006E67C9 /* Debug */ = {
266 | isa = XCBuildConfiguration;
267 | buildSettings = {
268 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
269 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
270 | CODE_SIGN_STYLE = Automatic;
271 | DEVELOPMENT_ASSET_PATHS = "\"Progress View/Preview Content\"";
272 | ENABLE_PREVIEWS = YES;
273 | INFOPLIST_FILE = "Progress View/Info.plist";
274 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
275 | LD_RUNPATH_SEARCH_PATHS = (
276 | "$(inherited)",
277 | "@executable_path/Frameworks",
278 | );
279 | PRODUCT_BUNDLE_IDENTIFIER = "alep.Progress-View";
280 | PRODUCT_NAME = "$(TARGET_NAME)";
281 | SWIFT_VERSION = 5.0;
282 | TARGETED_DEVICE_FAMILY = "1,2";
283 | };
284 | name = Debug;
285 | };
286 | F67709942533A00D006E67C9 /* Release */ = {
287 | isa = XCBuildConfiguration;
288 | buildSettings = {
289 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
290 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
291 | CODE_SIGN_STYLE = Automatic;
292 | DEVELOPMENT_ASSET_PATHS = "\"Progress View/Preview Content\"";
293 | ENABLE_PREVIEWS = YES;
294 | INFOPLIST_FILE = "Progress View/Info.plist";
295 | IPHONEOS_DEPLOYMENT_TARGET = 14.0;
296 | LD_RUNPATH_SEARCH_PATHS = (
297 | "$(inherited)",
298 | "@executable_path/Frameworks",
299 | );
300 | PRODUCT_BUNDLE_IDENTIFIER = "alep.Progress-View";
301 | PRODUCT_NAME = "$(TARGET_NAME)";
302 | SWIFT_VERSION = 5.0;
303 | TARGETED_DEVICE_FAMILY = "1,2";
304 | };
305 | name = Release;
306 | };
307 | /* End XCBuildConfiguration section */
308 |
309 | /* Begin XCConfigurationList section */
310 | F677097E2533A00B006E67C9 /* Build configuration list for PBXProject "Progress View" */ = {
311 | isa = XCConfigurationList;
312 | buildConfigurations = (
313 | F67709902533A00D006E67C9 /* Debug */,
314 | F67709912533A00D006E67C9 /* Release */,
315 | );
316 | defaultConfigurationIsVisible = 0;
317 | defaultConfigurationName = Release;
318 | };
319 | F67709922533A00D006E67C9 /* Build configuration list for PBXNativeTarget "Progress View" */ = {
320 | isa = XCConfigurationList;
321 | buildConfigurations = (
322 | F67709932533A00D006E67C9 /* Debug */,
323 | F67709942533A00D006E67C9 /* Release */,
324 | );
325 | defaultConfigurationIsVisible = 0;
326 | defaultConfigurationName = Release;
327 | };
328 | /* End XCConfigurationList section */
329 | };
330 | rootObject = F677097B2533A00B006E67C9 /* Project object */;
331 | }
332 |
--------------------------------------------------------------------------------
/Progress View/Progress View.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Progress View/Progress View.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Progress View/Progress View.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Progress View/Progress View.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Progress View/Progress View.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Progress View.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Progress View/Progress View/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Progress View
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct ContentView: View {
9 |
10 | @State private var progress: Double = 0
11 | private let total: Double = 1
12 |
13 | @State private var dataTask: URLSessionDataTask?
14 | @State private var observation: NSKeyValueObservation?
15 | @State private var image: UIImage?
16 |
17 | var body: some View {
18 | VStack {
19 | ZStack {
20 | if image == nil {
21 | ProgressView("Descargando imagen...", value: progress, total: total)
22 | .progressViewStyle(LinearProgressViewStyle())
23 | .padding()
24 | } else {
25 | Image(uiImage: image!)
26 | .resizable()
27 | }
28 | }
29 |
30 | Spacer()
31 |
32 | HStack {
33 | Spacer()
34 | Button(action: {
35 | reset()
36 | downloadPhoto()
37 | }) {
38 | Image(systemName: "arrow.clockwise")
39 | }
40 | .font(.largeTitle)
41 | }
42 | .padding()
43 | }.onAppear(perform: downloadPhoto)
44 | }
45 |
46 | private func downloadPhoto() {
47 | guard let url = URL(string: "https://source.unsplash.com/random/4000x8000") else { return }
48 |
49 | dataTask = URLSession.shared.dataTask(with: url) { data, _, _ in
50 | observation?.invalidate()
51 | guard let data = data else { return }
52 | DispatchQueue.main.async {
53 | image = UIImage(data: data)
54 | }
55 | }
56 |
57 | observation = dataTask?.progress.observe(\.fractionCompleted) { observationProgress, _ in
58 | DispatchQueue.main.async {
59 | progress = observationProgress.fractionCompleted
60 | }
61 | }
62 |
63 | dataTask?.resume()
64 | }
65 |
66 | private func reset() {
67 | dataTask?.cancel()
68 | progress = 0
69 | image = nil
70 | }
71 | }
72 |
73 | struct ContentView_Previews: PreviewProvider {
74 | static var previews: some View {
75 | ContentView()
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Progress View/Progress View/Progress_ViewApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Progress_ViewApp.swift
3 | // Progress View
4 | //
5 | // Created by Alejandrina Patrón López on 10/11/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct Progress_ViewApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Random Joke UIKit/Random Joke UIKit.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Random Joke UIKit.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Random Joke UIKit
4 | //
5 | // Created by Ale Patrón on 11/11/20.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UIApplicationSupportsIndirectInputEvents
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIMainStoryboardFile
47 | Main
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Random Joke UIKit
4 | //
5 | // Created by Ale Patrón on 11/11/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | guard let _ = (scene as? UIWindowScene) else { return }
20 | }
21 |
22 | func sceneDidDisconnect(_ scene: UIScene) {
23 | // Called as the scene is being released by the system.
24 | // This occurs shortly after the scene enters the background, or when its session is discarded.
25 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
27 | }
28 |
29 | func sceneDidBecomeActive(_ scene: UIScene) {
30 | // Called when the scene has moved from an inactive state to an active state.
31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
32 | }
33 |
34 | func sceneWillResignActive(_ scene: UIScene) {
35 | // Called when the scene will move from an active state to an inactive state.
36 | // This may occur due to temporary interruptions (ex. an incoming phone call).
37 | }
38 |
39 | func sceneWillEnterForeground(_ scene: UIScene) {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 | func sceneDidEnterBackground(_ scene: UIScene) {
45 | // Called as the scene transitions from the foreground to the background.
46 | // Use this method to save data, release shared resources, and store enough scene-specific state information
47 | // to restore the scene back to its current state.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/Random Joke UIKit/Random Joke UIKit/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Programming Quotes UIKit
4 | //
5 | // Created by Ale Patrón on 11/11/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class ViewController: UIViewController {
11 |
12 | private lazy var label: UILabel = {
13 | let view = UILabel(frame: .zero)
14 | view.translatesAutoresizingMaskIntoConstraints = false
15 | view.numberOfLines = 0
16 | view.text = "Hello world!"
17 | return view
18 | }()
19 |
20 | private lazy var refreshButton: UIButton = {
21 | let button = UIButton(frame: .zero)
22 | let image = UIImage(systemName: "arrow.clockwise")
23 | button.setImage(image, for: .normal)
24 | button.addTarget(self, action: #selector(loadData), for: .touchUpInside)
25 | button.translatesAutoresizingMaskIntoConstraints = false
26 | return button
27 | }()
28 |
29 | private var joke: Joke? {
30 | didSet {
31 | guard let joke = joke else { return }
32 | label.text = "\(joke.setup)\n\(joke.punchline)"
33 | label.sizeToFit()
34 | }
35 | }
36 |
37 | override func viewDidLoad() {
38 | super.viewDidLoad()
39 |
40 | // Label and button setup
41 | view.addSubview(label)
42 | view.addSubview(refreshButton)
43 | addConstraints()
44 |
45 | // Get joke from API
46 | loadData()
47 | }
48 |
49 | private func addConstraints() {
50 | label.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
51 | label.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
52 | label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
53 | refreshButton.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 10).isActive = true
54 | refreshButton.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
55 | }
56 |
57 | @objc private func loadData() {
58 | guard let url = URL(string: "https://official-joke-api.appspot.com/random_joke") else {
59 | return
60 | }
61 | URLSession.shared.dataTask(with: url) { data, response, error in
62 | guard let data = data else { return }
63 | if let decodedData = try? JSONDecoder().decode(Joke.self, from: data) {
64 | DispatchQueue.main.async {
65 | self.joke = decodedData
66 | }
67 | }
68 | }.resume()
69 | }
70 | }
71 |
72 | struct Joke: Decodable {
73 | var id: Int
74 | var type: String
75 | var setup: String
76 | var punchline: String
77 | }
78 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Random Joke/Random Joke.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Random Joke/Random Joke.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Random Joke.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Random Joke
4 | //
5 | // Created by Alejandrina Patrón López on 12/14/20.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 |
14 |
15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | // Override point for customization after application launch.
17 | return true
18 | }
19 |
20 | // MARK: UISceneSession Lifecycle
21 |
22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
23 | // Called when a new scene session is being created.
24 | // Use this method to select a configuration to create the new scene with.
25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
26 | }
27 |
28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
29 | // Called when the user discards a scene session.
30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
32 | }
33 |
34 |
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 | UISceneStoryboardFile
37 | Main
38 |
39 |
40 |
41 |
42 | UIApplicationSupportsIndirectInputEvents
43 |
44 | UILaunchStoryboardName
45 | LaunchScreen
46 | UIMainStoryboardFile
47 | Main
48 | UIRequiredDeviceCapabilities
49 |
50 | armv7
51 |
52 | UISupportedInterfaceOrientations
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationLandscapeLeft
56 | UIInterfaceOrientationLandscapeRight
57 |
58 | UISupportedInterfaceOrientations~ipad
59 |
60 | UIInterfaceOrientationPortrait
61 | UIInterfaceOrientationPortraitUpsideDown
62 | UIInterfaceOrientationLandscapeLeft
63 | UIInterfaceOrientationLandscapeRight
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Random Joke
4 | //
5 | // Created by Alejandrina Patrón López on 12/14/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 |
15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
19 | guard let _ = (scene as? UIWindowScene) else { return }
20 | }
21 |
22 | func sceneDidDisconnect(_ scene: UIScene) {
23 | // Called as the scene is being released by the system.
24 | // This occurs shortly after the scene enters the background, or when its session is discarded.
25 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
27 | }
28 |
29 | func sceneDidBecomeActive(_ scene: UIScene) {
30 | // Called when the scene has moved from an inactive state to an active state.
31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
32 | }
33 |
34 | func sceneWillResignActive(_ scene: UIScene) {
35 | // Called when the scene will move from an active state to an inactive state.
36 | // This may occur due to temporary interruptions (ex. an incoming phone call).
37 | }
38 |
39 | func sceneWillEnterForeground(_ scene: UIScene) {
40 | // Called as the scene transitions from the background to the foreground.
41 | // Use this method to undo the changes made on entering the background.
42 | }
43 |
44 | func sceneDidEnterBackground(_ scene: UIScene) {
45 | // Called as the scene transitions from the foreground to the background.
46 | // Use this method to save data, release shared resources, and store enough scene-specific state information
47 | // to restore the scene back to its current state.
48 | }
49 |
50 |
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/Random Joke/Random Joke/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // Random Joke
4 | //
5 | // Created by Alejandrina Patrón López on 12/14/20.
6 | //
7 |
8 | import UIKit
9 |
10 | class ViewController: UIViewController {
11 |
12 | private lazy var label: UILabel = {
13 | let label = UILabel(frame: .zero)
14 | label.translatesAutoresizingMaskIntoConstraints = false
15 | label.numberOfLines = 0
16 | label.text = "Loading joke..."
17 | label.sizeToFit()
18 | return label
19 | }()
20 |
21 | private lazy var refreshButton: UIButton = {
22 | let button = UIButton(frame: .zero)
23 | let image = UIImage(systemName: "arrow.clockwise")
24 | button.setImage(image, for: .normal)
25 | button.addTarget(self, action: #selector(loadData), for: .touchUpInside)
26 | button.translatesAutoresizingMaskIntoConstraints = false
27 | return button
28 | }()
29 |
30 | private var dataTask: URLSessionDataTask?
31 |
32 | private var joke: Joke? {
33 | didSet {
34 | guard let joke = joke else { return }
35 | label.text = "\(joke.setup)\n\(joke.punchline)"
36 | label.sizeToFit()
37 | }
38 | }
39 |
40 | override func viewDidLoad() {
41 | super.viewDidLoad()
42 | // Do any additional setup after loading the view.
43 |
44 | view.addSubview(label)
45 | view.addSubview(refreshButton)
46 | setConstraints()
47 |
48 | loadData()
49 | }
50 |
51 | private func setConstraints() {
52 | label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
53 | label.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
54 | label.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
55 | refreshButton.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 10).isActive = true
56 | refreshButton.leadingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
57 | }
58 |
59 | @objc private func loadData() {
60 | guard let url = URL(string: "https://official-joke-api.appspot.com/jokes/random") else {
61 | return
62 | }
63 |
64 | dataTask?.cancel()
65 | dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
66 | guard let data = data else { return }
67 | if let decodedData = try? JSONDecoder().decode(Joke.self, from: data) {
68 | DispatchQueue.main.async {
69 | self.joke = decodedData
70 | }
71 | }
72 | }
73 | dataTask?.resume()
74 | }
75 | }
76 |
77 | struct Joke: Decodable {
78 | let id: Int
79 | let type: String
80 | let setup: String
81 | let punchline: String
82 | }
83 |
--------------------------------------------------------------------------------
/Todos/Todos.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Todos/Todos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Todos/Todos.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Todos/Todos.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Todos/Todos.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Todos.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/Todos/Todos/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Todos
4 | //
5 | // Created by Alejandrina Patrón López on 8/25/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | @UIApplicationMain
12 | class AppDelegate: UIResponder, UIApplicationDelegate {
13 |
14 |
15 |
16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
17 | // Override point for customization after application launch.
18 | return true
19 | }
20 |
21 | // MARK: UISceneSession Lifecycle
22 |
23 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
24 | // Called when a new scene session is being created.
25 | // Use this method to select a configuration to create the new scene with.
26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
27 | }
28 |
29 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
30 | // Called when the user discards a scene session.
31 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
32 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
33 | }
34 |
35 |
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/Todos/Todos/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Todos/Todos/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Todos/Todos/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Todos/Todos/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Todos
4 | //
5 | // Created by Alejandrina Patrón López on 8/25/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ContentView: View {
12 |
13 | @State private var newTodo = ""
14 | @State private var allTodos: [TodoItem] = []
15 | private let todosKey = "todosKey"
16 |
17 | var body: some View {
18 | NavigationView {
19 | VStack {
20 | HStack {
21 | TextField("Add todo...", text: $newTodo)
22 | .textFieldStyle(RoundedBorderTextFieldStyle())
23 |
24 | Button(action: {
25 | guard !self.newTodo.isEmpty else { return }
26 | self.allTodos.append(TodoItem(todo: self.newTodo))
27 | self.newTodo = ""
28 | self.saveTodos()
29 | }) {
30 | Image(systemName: "plus")
31 | }
32 | .padding(.leading, 5)
33 | }.padding()
34 |
35 | List {
36 | ForEach(allTodos) { todoItem in
37 | Text(todoItem.todo)
38 | }.onDelete(perform: deleteTodo)
39 | }
40 | }
41 | .navigationBarTitle("Todos")
42 | }.onAppear(perform: loadTodos)
43 | }
44 |
45 | private func saveTodos() {
46 | UserDefaults.standard.set(try? PropertyListEncoder().encode(self.allTodos), forKey: todosKey)
47 | }
48 |
49 | private func loadTodos() {
50 | if let todosData = UserDefaults.standard.value(forKey: todosKey) as? Data {
51 | if let todosList = try? PropertyListDecoder().decode(Array.self, from: todosData) {
52 | self.allTodos = todosList
53 | }
54 | }
55 | }
56 |
57 | private func deleteTodo(at offsets: IndexSet) {
58 | self.allTodos.remove(atOffsets: offsets)
59 | saveTodos()
60 | }
61 | }
62 |
63 | struct TodoItem: Codable, Identifiable {
64 | let id = UUID()
65 | let todo: String
66 | }
67 |
68 | struct ContentView_Previews: PreviewProvider {
69 | static var previews: some View {
70 | ContentView()
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/Todos/Todos/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 | UISceneConfigurations
28 |
29 | UIWindowSceneSessionRoleApplication
30 |
31 |
32 | UISceneConfigurationName
33 | Default Configuration
34 | UISceneDelegateClassName
35 | $(PRODUCT_MODULE_NAME).SceneDelegate
36 |
37 |
38 |
39 |
40 | UILaunchStoryboardName
41 | LaunchScreen
42 | UIRequiredDeviceCapabilities
43 |
44 | armv7
45 |
46 | UISupportedInterfaceOrientations
47 |
48 | UIInterfaceOrientationPortrait
49 | UIInterfaceOrientationLandscapeLeft
50 | UIInterfaceOrientationLandscapeRight
51 |
52 | UISupportedInterfaceOrientations~ipad
53 |
54 | UIInterfaceOrientationPortrait
55 | UIInterfaceOrientationPortraitUpsideDown
56 | UIInterfaceOrientationLandscapeLeft
57 | UIInterfaceOrientationLandscapeRight
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/Todos/Todos/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Todos/Todos/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Todos
4 | //
5 | // Created by Alejandrina Patrón López on 8/25/20.
6 | // Copyright © 2020 Ale Patrón. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 |
14 | var window: UIWindow?
15 |
16 |
17 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
18 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
19 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
20 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
21 |
22 | // Create the SwiftUI view that provides the window contents.
23 | let contentView = ContentView()
24 |
25 | // Use a UIHostingController as window root view controller.
26 | if let windowScene = scene as? UIWindowScene {
27 | let window = UIWindow(windowScene: windowScene)
28 | window.rootViewController = UIHostingController(rootView: contentView)
29 | self.window = window
30 | window.makeKeyAndVisible()
31 | }
32 | }
33 |
34 | func sceneDidDisconnect(_ scene: UIScene) {
35 | // Called as the scene is being released by the system.
36 | // This occurs shortly after the scene enters the background, or when its session is discarded.
37 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
38 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
39 | }
40 |
41 | func sceneDidBecomeActive(_ scene: UIScene) {
42 | // Called when the scene has moved from an inactive state to an active state.
43 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
44 | }
45 |
46 | func sceneWillResignActive(_ scene: UIScene) {
47 | // Called when the scene will move from an active state to an inactive state.
48 | // This may occur due to temporary interruptions (ex. an incoming phone call).
49 | }
50 |
51 | func sceneWillEnterForeground(_ scene: UIScene) {
52 | // Called as the scene transitions from the background to the foreground.
53 | // Use this method to undo the changes made on entering the background.
54 | }
55 |
56 | func sceneDidEnterBackground(_ scene: UIScene) {
57 | // Called as the scene transitions from the foreground to the background.
58 | // Use this method to save data, release shared resources, and store enough scene-specific state information
59 | // to restore the scene back to its current state.
60 | }
61 |
62 |
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/TwitterUI/TwitterUI.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TwitterUI.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // TwitterUI
4 | //
5 |
6 | import SwiftUI
7 |
8 | extension Color {
9 | static var twitterBlue: Color = Color(red: 29/255, green: 161/255, blue: 241/255)
10 | }
11 |
12 | struct ContentView: View {
13 |
14 | private let tweets: [Tweet] = [
15 | Tweet(authorName: "Alejandrina Patrón",
16 | authorUsername: "ale_patron",
17 | timestampText: "4h",
18 | text: "good morning 🌞",
19 | numberOfReplies: 2,
20 | numberOfRetweets: 0,
21 | numberOfLikes: 0),
22 | Tweet(authorName: "Jack",
23 | authorUsername: "jack",
24 | timestampText: "15h",
25 | text: "just setting up my twttr",
26 | numberOfReplies: 589,
27 | numberOfRetweets: 368,
28 | numberOfLikes: 450),
29 | Tweet(authorName: "Donald J. Trump",
30 | authorUsername: "realDonaldTrump",
31 | timestampText: "6h",
32 | text: "Despite the negative press covfefe",
33 | numberOfReplies: 2890,
34 | numberOfRetweets: 4565,
35 | numberOfLikes: 896),
36 | Tweet(authorName: "Jack",
37 | authorUsername: "jack",
38 | timestampText: "15h",
39 | text: "this is a tweet with a lot of text because I need to test multi-line tweets in my new SwiftUI twitter app :)",
40 | numberOfReplies: 589,
41 | numberOfRetweets: 368,
42 | numberOfLikes: 450),
43 | Tweet(authorName: "Barack Obama",
44 | authorUsername: "BarackObama",
45 | timestampText: "12h",
46 | text: "No one is born hating another person because of the color of his skin or his background or his religion...",
47 | numberOfReplies: 5589,
48 | numberOfRetweets: 3568,
49 | numberOfLikes: 4350),
50 | Tweet(authorName: "Jack",
51 | authorUsername: "jack",
52 | timestampText: "15h",
53 | text: "this is a tweet with a lot of text because I need to test multi-line tweets in my new SwiftUI twitter app :)",
54 | numberOfReplies: 589,
55 | numberOfRetweets: 368,
56 | numberOfLikes: 450),
57 | Tweet(authorName: "Jack",
58 | authorUsername: "jack",
59 | timestampText: "15h",
60 | text: "this is a tweet with a lot of text because I need to test multi-line tweets in my new SwiftUI twitter app :)",
61 | numberOfReplies: 589,
62 | numberOfRetweets: 368,
63 | numberOfLikes: 450),
64 | ]
65 |
66 |
67 | @State private var selectedTab = 0
68 |
69 | var body: some View {
70 | ZStack {
71 | TabView(selection: $selectedTab) {
72 | FeedView(tweets: tweets).tabItem {
73 | Image(systemName: "house")
74 | }.tag(0)
75 | Text("Tab Content 1").tabItem {
76 | Image(systemName: "magnifyingglass")
77 | }.tag(1)
78 | Text("Tab Content 2").tabItem {
79 | Image(systemName: "bell")
80 | }.tag(2)
81 | Text("Tab Content 3").tabItem {
82 | Image(systemName: "envelope")
83 | }.tag(3)
84 | }.accentColor(.twitterBlue)
85 |
86 | VStack {
87 | Spacer()
88 | HStack {
89 | Spacer()
90 | NewTweetButton()
91 | .padding(.bottom, 65)
92 | .padding(.trailing)
93 | }
94 | }
95 | }
96 | }
97 | }
98 |
99 | struct NewTweetButton: View {
100 | var body: some View {
101 | Button(action: {}) {
102 | Image(systemName: "pencil")
103 | .font(.largeTitle)
104 | .foregroundColor(.white)
105 | .padding()
106 | }
107 | .background(Color.twitterBlue)
108 | .mask(Circle())
109 | .shadow(radius: 5)
110 | }
111 | }
112 |
113 | struct FeedView: View {
114 | let tweets: [Tweet]
115 |
116 | var body: some View {
117 | NavigationView {
118 | List(tweets) { tweet in
119 | TweetView(tweet: tweet)
120 | }
121 | .listStyle(PlainListStyle())
122 | .navigationBarTitle("Twitter", displayMode: .inline)
123 | .navigationBarItems(
124 | leading:
125 | Button(action: {}) {
126 | Image(systemName: "person.crop.circle.fill")
127 | .foregroundColor(.twitterBlue)
128 | },
129 | trailing:
130 | Button(action: {}) {
131 | Image(systemName: "moon.stars")
132 | .foregroundColor(.twitterBlue)
133 | }
134 | )
135 | }
136 | }
137 | }
138 |
139 | struct TweetView: View {
140 | let tweet: Tweet
141 |
142 | var body: some View {
143 | HStack(alignment: .top) {
144 | Image(systemName: "person.crop.circle.fill")
145 | .font(.system(size: 55))
146 | .padding(.top)
147 | .padding(.trailing, 5)
148 | .foregroundColor(.twitterBlue)
149 |
150 | VStack(alignment: .leading) {
151 | HStack {
152 | Text(tweet.authorName)
153 | .bold()
154 | .lineLimit(1)
155 | Text("@\(tweet.authorUsername) • \(tweet.timestampText)")
156 | .lineLimit(1)
157 | .truncationMode(.middle)
158 | .foregroundColor(.gray)
159 | }
160 | .padding(.top, 5)
161 |
162 | Text(tweet.text)
163 | .lineLimit(nil)
164 | .multilineTextAlignment(.leading)
165 |
166 | TweetActionsView(tweet: tweet)
167 | .foregroundColor(.gray)
168 | .padding([.bottom, .top], 10)
169 | .padding(.trailing, 30)
170 | }
171 | }
172 | }
173 | }
174 |
175 | struct TweetActionsView: View {
176 | let tweet: Tweet
177 |
178 | var body: some View {
179 | HStack {
180 | Button(action: {}) {
181 | Image(systemName: "message")
182 | }
183 | Text(tweet.numberOfReplies > 0 ? "\(tweet.numberOfReplies)" : "")
184 | Spacer()
185 |
186 | Button(action: {}) {
187 | Image(systemName: "arrow.2.squarepath")
188 | }
189 | Text(tweet.numberOfRetweets > 0 ? "\(tweet.numberOfRetweets)" : "")
190 | Spacer()
191 |
192 | Button(action: {}) {
193 | Image(systemName: "heart")
194 | }
195 | Text(tweet.numberOfLikes > 0 ? "\(tweet.numberOfLikes)" : "")
196 | Spacer()
197 |
198 | Button(action: {}) {
199 | Image(systemName: "square.and.arrow.up")
200 | }
201 | }
202 | }
203 | }
204 |
205 | struct ContentView_Previews: PreviewProvider {
206 | static var previews: some View {
207 | ContentView()
208 | }
209 | }
210 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleVersion
20 | 1
21 | LSRequiresIPhoneOS
22 |
23 | UIApplicationSceneManifest
24 |
25 | UIApplicationSupportsMultipleScenes
26 |
27 |
28 | UIApplicationSupportsIndirectInputEvents
29 |
30 | UILaunchScreen
31 |
32 | UIRequiredDeviceCapabilities
33 |
34 | armv7
35 |
36 | UISupportedInterfaceOrientations
37 |
38 | UIInterfaceOrientationPortrait
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UISupportedInterfaceOrientations~ipad
43 |
44 | UIInterfaceOrientationPortrait
45 | UIInterfaceOrientationPortraitUpsideDown
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/Tweet.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Tweet.swift
3 | // TwitterUI
4 | //
5 | // Created by Alejandrina Patrón López on 11/1/20.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Tweet: Identifiable {
11 | let id = UUID()
12 | let authorName: String
13 | let authorUsername: String
14 | let timestampText: String
15 | let text: String
16 | let numberOfReplies: Int
17 | let numberOfRetweets: Int
18 | let numberOfLikes: Int
19 | }
20 |
--------------------------------------------------------------------------------
/TwitterUI/TwitterUI/TwitterUIApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TwitterUIApp.swift
3 | // TwitterUI
4 | //
5 | // Created by Alejandrina Patrón López on 10/24/20.
6 | //
7 |
8 | import SwiftUI
9 |
10 | @main
11 | struct TwitterUIApp: App {
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/Weather/Weather.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Weather/Weather.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Weather/Weather.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/apatronl/Medium/981046b3b77800adf42387fe9511d9dfd0f3c6f6/Weather/Weather.xcodeproj/project.xcworkspace/xcuserdata/alep.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/Weather/Weather.xcodeproj/xcshareddata/xcschemes/Weather.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
56 |
57 |
58 |
64 |
66 |
72 |
73 |
74 |
75 |
77 |
78 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/Weather/Weather.xcodeproj/xcuserdata/alep.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Weather.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | F69FE14E2550E51A0025E8AA
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Weather/Weather/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/Weather/Weather/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "iphone",
5 | "scale" : "2x",
6 | "size" : "20x20"
7 | },
8 | {
9 | "idiom" : "iphone",
10 | "scale" : "3x",
11 | "size" : "20x20"
12 | },
13 | {
14 | "idiom" : "iphone",
15 | "scale" : "2x",
16 | "size" : "29x29"
17 | },
18 | {
19 | "idiom" : "iphone",
20 | "scale" : "3x",
21 | "size" : "29x29"
22 | },
23 | {
24 | "idiom" : "iphone",
25 | "scale" : "2x",
26 | "size" : "40x40"
27 | },
28 | {
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "40x40"
32 | },
33 | {
34 | "idiom" : "iphone",
35 | "scale" : "2x",
36 | "size" : "60x60"
37 | },
38 | {
39 | "idiom" : "iphone",
40 | "scale" : "3x",
41 | "size" : "60x60"
42 | },
43 | {
44 | "idiom" : "ipad",
45 | "scale" : "1x",
46 | "size" : "20x20"
47 | },
48 | {
49 | "idiom" : "ipad",
50 | "scale" : "2x",
51 | "size" : "20x20"
52 | },
53 | {
54 | "idiom" : "ipad",
55 | "scale" : "1x",
56 | "size" : "29x29"
57 | },
58 | {
59 | "idiom" : "ipad",
60 | "scale" : "2x",
61 | "size" : "29x29"
62 | },
63 | {
64 | "idiom" : "ipad",
65 | "scale" : "1x",
66 | "size" : "40x40"
67 | },
68 | {
69 | "idiom" : "ipad",
70 | "scale" : "2x",
71 | "size" : "40x40"
72 | },
73 | {
74 | "idiom" : "ipad",
75 | "scale" : "1x",
76 | "size" : "76x76"
77 | },
78 | {
79 | "idiom" : "ipad",
80 | "scale" : "2x",
81 | "size" : "76x76"
82 | },
83 | {
84 | "idiom" : "ipad",
85 | "scale" : "2x",
86 | "size" : "83.5x83.5"
87 | },
88 | {
89 | "idiom" : "ios-marketing",
90 | "scale" : "1x",
91 | "size" : "1024x1024"
92 | }
93 | ],
94 | "info" : {
95 | "author" : "xcode",
96 | "version" : 1
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/Weather/Weather/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Weather/Weather/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | NSLocationWhenInUseUsageDescription
10 | This app requires your location to provide weather data.
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | $(PRODUCT_NAME)
17 | CFBundlePackageType
18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
19 | CFBundleShortVersionString
20 | 1.0
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UIApplicationSceneManifest
26 |
27 | UIApplicationSupportsMultipleScenes
28 |
29 |
30 | UIApplicationSupportsIndirectInputEvents
31 |
32 | UILaunchScreen
33 |
34 | UIRequiredDeviceCapabilities
35 |
36 | armv7
37 |
38 | UISupportedInterfaceOrientations
39 |
40 | UIInterfaceOrientationPortrait
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | UISupportedInterfaceOrientations~ipad
45 |
46 | UIInterfaceOrientationPortrait
47 | UIInterfaceOrientationPortraitUpsideDown
48 | UIInterfaceOrientationLandscapeLeft
49 | UIInterfaceOrientationLandscapeRight
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/Weather/Weather/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Weather/Weather/Weather.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Weather.swift
3 | // Weather
4 | //
5 |
6 | import Foundation
7 |
8 | public struct Weather {
9 | let city: String
10 | let temperature: String
11 | let description: String
12 | let iconName: String
13 |
14 | init(response: APIResponse) {
15 | city = response.name
16 | temperature = "\(Int(response.main.temp))"
17 | description = response.weather.first?.description ?? ""
18 | iconName = response.weather.first?.iconName ?? ""
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Weather/Weather/WeatherApp.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherApp.swift
3 | // Weather
4 | //
5 |
6 | import SwiftUI
7 |
8 | @main
9 | struct WeatherApp: App {
10 | var body: some Scene {
11 | WindowGroup {
12 | let weatherService = WeatherService()
13 | WeatherView(viewModel: WeatherViewModel(weatherService: weatherService))
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/Weather/Weather/WeatherService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherService.swift
3 | // Weather
4 | //
5 |
6 | import CoreLocation
7 | import Foundation
8 |
9 | // Sample URL:
10 | // https://api.openweathermap.org/data/2.5/weather?lat=51.50998&lon=-0.1337&appid=YOUR_API_KEY&units=metric
11 |
12 | public final class WeatherService: NSObject {
13 |
14 | private let locationManager = CLLocationManager()
15 | private let API_KEY = "YOUR_API_KEY" // Replace with your own API key
16 | private var completionHandler: ((Weather?, LocationAuthError?) -> Void)?
17 | private var dataTask: URLSessionDataTask?
18 |
19 | public override init() {
20 | super.init()
21 | locationManager.delegate = self
22 | }
23 |
24 | public func loadWeatherData(
25 | _ completionHandler: @escaping((Weather?, LocationAuthError?) -> Void)
26 | ) {
27 | self.completionHandler = completionHandler
28 | loadDataOrRequestLocationAuth()
29 | }
30 |
31 | private func makeDataRequest(forCoordinates coordinates: CLLocationCoordinate2D) {
32 | guard let urlString =
33 | "https://api.openweathermap.org/data/2.5/weather?lat=\(coordinates.latitude)&lon=\(coordinates.longitude)&appid=\(API_KEY)&units=metric"
34 | .addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else { return }
35 | guard let url = URL(string: urlString) else { return }
36 |
37 | // Cancel previous task
38 | dataTask?.cancel()
39 |
40 | dataTask = URLSession.shared.dataTask(with: url) { data, response, error in
41 | guard error == nil, let data = data else { return }
42 |
43 | if let response = try? JSONDecoder().decode(APIResponse.self, from: data) {
44 | self.completionHandler?(Weather(response: response), nil)
45 | }
46 | }
47 | dataTask?.resume()
48 | }
49 |
50 | private func loadDataOrRequestLocationAuth() {
51 | switch locationManager.authorizationStatus {
52 | case .authorizedAlways, .authorizedWhenInUse:
53 | locationManager.startUpdatingLocation()
54 | case .denied, .restricted:
55 | completionHandler?(nil, LocationAuthError())
56 | default:
57 | locationManager.requestWhenInUseAuthorization()
58 | }
59 | }
60 | }
61 |
62 | extension WeatherService: CLLocationManagerDelegate {
63 | public func locationManager(
64 | _ manager: CLLocationManager,
65 | didUpdateLocations locations: [CLLocation]
66 | ) {
67 | guard let location = locations.first else { return }
68 | makeDataRequest(forCoordinates: location.coordinate)
69 | }
70 |
71 | public func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
72 | loadDataOrRequestLocationAuth()
73 | }
74 | public func locationManager(
75 | _ manager: CLLocationManager,
76 | didFailWithError error: Error
77 | ) {
78 | print("Something went wrong: \(error.localizedDescription)")
79 | }
80 | }
81 |
82 | struct APIResponse: Decodable {
83 | let name: String
84 | let main: APIMain
85 | let weather: [APIWeather]
86 | }
87 |
88 | struct APIMain: Decodable {
89 | let temp: Double
90 | }
91 |
92 | struct APIWeather: Decodable {
93 | let description: String
94 | let iconName: String
95 |
96 | enum CodingKeys: String, CodingKey {
97 | case description
98 | case iconName = "main"
99 | }
100 | }
101 |
102 | public struct LocationAuthError: Error {}
103 |
--------------------------------------------------------------------------------
/Weather/Weather/WeatherView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // Weather
4 | //
5 |
6 | import SwiftUI
7 |
8 | struct WeatherView: View {
9 |
10 | @ObservedObject var viewModel: WeatherViewModel
11 |
12 | var body: some View {
13 | VStack {
14 | Text(viewModel.cityName)
15 | .font(.largeTitle)
16 | .padding()
17 | Text(viewModel.temperature)
18 | .font(.system(size: 70))
19 | .bold()
20 | Text(viewModel.weatherIcon)
21 | .font(.largeTitle)
22 | .padding()
23 | Text(viewModel.weatherDescription)
24 | }
25 | .alert(isPresented: $viewModel.shouldShowLocationError) {
26 | Alert(
27 | title: Text("Error"),
28 | message: Text("To see the weather, provide location access in Settings."),
29 | dismissButton: .default(Text("Open Settings")) {
30 | guard let settingsURL = URL(string: UIApplication.openSettingsURLString) else { return }
31 | UIApplication.shared.open(settingsURL)
32 | }
33 | )
34 | }
35 | .onAppear(perform: viewModel.refresh)
36 | }
37 | }
38 |
39 |
40 | struct ContentView_Previews: PreviewProvider {
41 | static var previews: some View {
42 | WeatherView(viewModel: WeatherViewModel(weatherService: WeatherService()))
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Weather/Weather/WeatherViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeatherViewModel.swift
3 | // Weather
4 | //
5 |
6 | import Foundation
7 |
8 | private let defaultIcon = "❓"
9 | private let iconMap = [
10 | "Drizzle" : "🌧",
11 | "Thunderstorm" : "⛈",
12 | "Rain": "🌧",
13 | "Snow": "❄️",
14 | "Clear": "☀️",
15 | "Clouds" : "☁️",
16 | ]
17 |
18 | class WeatherViewModel: ObservableObject {
19 | @Published var cityName: String = "City Name"
20 | @Published var temperature: String = "--"
21 | @Published var weatherDescription: String = "--"
22 | @Published var weatherIcon: String = defaultIcon
23 | @Published var shouldShowLocationError: Bool = false
24 |
25 | public let weatherService: WeatherService
26 |
27 | init(weatherService: WeatherService) {
28 | self.weatherService = weatherService
29 | }
30 |
31 | func refresh() {
32 | weatherService.loadWeatherData { weather, error in
33 | DispatchQueue.main.async {
34 | if let _ = error {
35 | self.shouldShowLocationError = true
36 | return
37 | }
38 |
39 | self.shouldShowLocationError = false
40 | guard let weather = weather else { return }
41 | self.cityName = weather.city
42 | self.temperature = "\(weather.temperature)ºC"
43 | self.weatherDescription = weather.description.capitalized
44 | self.weatherIcon = iconMap[weather.iconName] ?? defaultIcon
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------