实战 
安装依赖 
在 Xcode 项目中的 Package Dependencies 可添加依赖项目

点击添加之后输入项目的 Git 仓库地址即可实现安装依赖
网络请求库 
下载 Alamofire,这是一个在 swift 中的一个 http 请求库,类似前端使用的 axios。
安装之后可以导入:
import Alamofire封装网络请求类 
先定义了一个发送请求的 Http 类,里吗定义接口地址和请求
然后 Index 类里面是一个一个的接口请求方法,它需要继承 Http,通过回调的方式将响应结果传递给视图
class Http {
    private let baseURL = "https://api.thecatapi.com/v1"
    /// 发起网络请求
    /// - Parameters:
    ///   - url: 请求路径
    ///   - method: 请求方法,GET、POST等
    ///   - parameters: 请求参数
    ///   - completion: 请求完成后的回调闭包,包含一个 AFDataResponse<Data> 类型的参数
    func request(url: String, method: HTTPMethod, parameters: [String: Any]?, completion: @escaping (AFDataResponse<Data>) -> Void) {
        AF.request(baseURL + url, method: method, parameters: parameters)
            .responseData { response in
                completion(response)
            }
    }
}
class Index: Http {
    func getCatList(data: [String: Any], completion: @escaping ([CatImage]) -> Void) {
        request(url: "/images/search", method: .get, parameters: data) { response in
            switch response.result {
            case .success(let data):
                do {
                    let catImages = try JSONDecoder().decode([CatImage].self, from: data)
                    completion(catImages)
                } catch {
                    print("Error decoding cat images: \(error)")
                    completion([])
                }
            case .failure(let error):
                print("Error fetching cat images: \(error)")
                completion([])
            }
        }
    }
}在试图中调用方法展示返回数据:
import SwiftUI
struct CatImage: Identifiable, Decodable {
    let id: String
    let url: URL
    let width: Int
    let height: Int
}
struct DemoView: View {
    @State private var catImages: [CatImage] = []
    let API = Index()
    var body: some View {
        List(catImages) { catImage in
            VStack(alignment: .leading) {
                Text("ID: \(catImage.id)")
                    .aspectRatio(contentMode: .fit)
                Text("Width: \(catImage.width), Height: \(catImage.height)")
            }
        }
        .onAppear {
            API.getCatList(data: ["limit": 10, "page": 1]) { images in
                catImages = images
            }
        }
    }
}
#Preview {
    DemoView()
}这样就实现了一个 GET 请求展示了列表内容
添加请求头 
AF.request 方法接收一个 headers 的参数字典,可以讲 token 等信息携带在其中
/// 发起网络请求
/// - Parameters:
///   - url: 请求路径
///   - method: 请求方法,GET、POST等
///   - parameters: 请求参数
///   - completion: 请求完成后的回调闭包,包含一个 AFDataResponse<Data> 类型的参数
func request(url: String, method: HTTPMethod, parameters: [String: Any]?, completion: @escaping (AFDataResponse<Data>) -> Void) {
    var headers: HTTPHeaders = [:] // 初始化一个空的 HTTPHeaders 字典
    headers["token"] = "xxxxx.xxxxxx.xxxxx"
    AF.request(baseURL + url, method: method, parameters: parameters, headers: headers)
        .responseData { response in
            completion(response)
        }
}设置缓存 
swift 中一般使用 UserDefaults
import Foundation
/// 用户身份信息缓存
class UserCache {
    static let shared = UserCache() // 创建单例模式
    private let USER_INFO = "USER_INFO"
    private let userDefaults = UserDefaults.standard
    /// 存储信息
    func saveInfo(info: EmailLoginEmailData) {
        do {
            let codeData = try JSONEncoder().encode(info)
            userDefaults.set(codeData, forKey: USER_INFO)
        } catch {
            print("格式转换错误")
        }
    }
    /// 获取信息
    func getInfo() -> EmailLoginEmailData? {
        if let data = userDefaults.data(forKey: USER_INFO),
           let userData = try? JSONDecoder().decode(EmailLoginEmailData.self, from: data)
        {
            return userData
        }
        return nil
    }
    /// 删除信息
    func deleteInfo() {
        userDefaults.removeObject(forKey: USER_INFO)
    }
}获取缓存信息:
let userInfo = UserCache.shared.getInfo()页面跳转 
现在需要跳转到首页 NavigationLink 方法的 destination 参数是跳转的目标,回调中是显示的内容,可以实现点击跳转
NavigationLink(destination: HomeView()) {
    Text("首页")
}生命周期 
- onAppear 在视图出现时候执行的方法
- onDisappear 当视图隐藏时候执行的方法
渲染地图 
参考文档 creating-and-combining-views
// 1. 引入 MapKit
import MapKit
import SwiftUI
struct MapView: View {
    var body: some View {
        // 3. 使用 Map 为采用您使用该区域初始化的相机位置的视图
        Map(initialPosition: .region(region))
    }
    // 2. 创建一个私有计算变量来保存地图的区域信息
    private var region: MKCoordinateRegion {
        MKCoordinateRegion(
            center: CLLocationCoordinate2D(latitude: 30, longitude: 120),
            span: MKCoordinateSpan(latitudeDelta: 0.2, longitudeDelta: 0.2)
        )
    }
}
#Preview {
    MapView()
}真机调试 
- 首先需要手机和电脑进行连接,需要使用数据线进行连接
数据线连接之后,通过顶部菜单栏进入 window > Devices and Simulators
这时候手机会提示是否信任该设备:

点击信任,并输入手机密码确认。确认之后 xcode 会显示手机的名字和正在共享缓存数据中:

- 登录 Apple 账号
通过顶部菜单栏进入 Xcode > Setting,在 Accounts 中点击 + 添加 Apple 账号,选择和手机一样的账号登录。

- 选择编译设备
通过顶部菜单选择刚刚添加的设备进行编译

- 版本兼容和团队
这时候 Xcode 提示版本不兼容无法运行,所以需要在 Xcode 中将项目版本和手机版本改为一致:

更改团队:

- 重新编译
接下来尝试重新编译,就会进入编译阶段,手机上会自动安装测试 App,可以直接运行


JSON 处理 
首先需要将返回的字符串转换为 utf8 格式,需要使用字符串的 data 方法:
import Foundation
var jsonData = """
[{"id":"ala","url":"https://cdn2.thecatapi.com/images/ala.gif","width":333,"height":200}]
"""
if let data = jsonData.data(using: .utf8) {
    print("转换成功", data)
} else {
    print("格式转换失败啦")
}后面需要使用 Swift 的 JSONDecoder 来将 JSON 数据转换为实例对象。
完整代码:
先创建一个 json 编译器,在使用 decode 方法,传入解析的类型和需要解析的数据,再使用 do 来补货解析错误的问题。
定义的方法必须的遵循 Decodable 协议。
import Foundation
var json = """
[{"id":"ala","url":"https://cdn2.thecatapi.com/images/ala.gif","width":333,"height":200}]
"""
let decoder = JSONDecoder()
struct CatImage:  Decodable {
    let id: String
    let url: String
    let width: Int
    let height: Int
}
if let jsonData = json.data(using: .utf8) {
    do {
        let data = try decoder.decode([CatImage].self, from: jsonData)
        print("格式转换成功的数据", data)
    } catch {
        print("JSON 格式转换错误")
    }
}else {
    print("格式转换失败啦")
}封装成为通用方法:
import Foundation
var json = """
[{"id":"ala","url":"https://cdn2.thecatapi.com/images/ala.gif","width":333,"height":200}]
"""
let decoder = JSONDecoder()
struct CatImage: Decodable {
    let id: String
    let url: String
    let width: Int
    let height: Int
}
func jsonToObject <T: Decodable>(type: T.Type, json: String) -> T? {
    if let jsonData = json.data(using: .utf8) {
        do {
            let data = try decoder.decode(T.self, from: jsonData)
            print("格式转换成功的数据", data)
            return data
        } catch {
            print("JSON 格式转换错误")
            return nil
        }
    } else {
        print("格式转换失败啦")
        return nil
    }
}
let res = jsonToObject(type: [CatImage].self, json: json)
if let result = res {
    print("成功的处理", result)
} else {
    print("失败的处理")
}获取经纬度 
资料参考来源 教程:将核心位置连接到 SwiftUI 应用程序
核心代码,LocationDataManager.swift:
import CoreLocation
import Foundation
/// 定位服务
class LocationDataManager: NSObject, ObservableObject, CLLocationManagerDelegate {
    var locationManager = CLLocationManager()
    @Published var authorizationStatus: CLAuthorizationStatus?
    override init() {
        super.init()
        locationManager.delegate = self
    }
    func locationManagerDidChangeAuthorization(_ manager: CLLocationManager) {
        switch manager.authorizationStatus {
        // 提供定位服务。
        case .authorizedWhenInUse:
            authorizationStatus = .authorizedWhenInUse
            locationManager.requestLocation()
        // 定位服务当前不可用。
        case .restricted:
            authorizationStatus = .restricted
        // 定位服务当前不可用。
        case .denied:
            authorizationStatus = .denied
        // 授权尚未确定。
        case .notDetermined:
            authorizationStatus = .notDetermined
            manager.requestWhenInUseAuthorization()
        default:
            break
        }
    }
    func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
        // 插入代码以处理位置更新
    }
    func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
        print("获取位置信息错误: \(error.localizedDescription)")
    }
}视图中获取位置信息:
import CoreLocation
import SwiftUI
struct HomeView: View {
    @State private var locationManager = CLLocationManager()
    @State private var authorizationStatus: CLAuthorizationStatus = .notDetermined
    @StateObject private var locationDataManager = LocationDataManager()
    var body: some View {
        NavigationView {
            VStack {
                VStack {
                    switch locationDataManager.locationManager.authorizationStatus {
                    case .authorizedWhenInUse: Text("当前经纬度信息:")
                        Text("纬度: \(locationDataManager.locationManager.location?.coordinate.latitude.description ?? "Error loading")")
                        Text("经度: \(locationDataManager.locationManager.location?.coordinate.longitude.description ?? "Error loading")")
                    case .restricted, .denied:
                        Text("当前位置数据被限制或拒绝。")
                    case .notDetermined:
                        Text("正在获取位置信息...")
                        ProgressView()
                    default:
                        ProgressView()
                    }
                }
            }
        }
        .navigationBarBackButtonHidden(true)
        // 当视图出现时执行的方法
        .onAppear {
            self.requestLocationAuthorization() // 请求位置权限
        }
    }
    /// 请求位置权限
    public func requestLocationAuthorization() {
        locationManager.requestWhenInUseAuthorization()
    }
}
#Preview {
    HomeView()
}另外还需要配置 Info.plist 用于在授权弹窗中的文案:
<key>NSLocationWhenInUseUsageDescription</key>
<string>需要使用您的位置信息</string>
<key>NSLocationAlwaysUsageDescription</key>
<string>需要在后台使用您的位置信息</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>需要使用您的位置信息</string>
配置完整之后运行项目就会启动提示弹窗申请用户授权了:

安装和删除依赖包 
申请网络请求 
需要配置 Info.plist 用于在授权弹窗中的文案:
<key>NSNetworkAccessDescription</key>
<string>需要访问网络</string>获取请求:
import Network
import SwiftUI
struct LaunchView: View {
    @State private var networkAuthorizationStatus: NWPath.Status = .requiresConnection
    var body: some View {
        NavigationStack {
            VStack {
              Text("hello")
            }
        }
        .toolbar(.hidden)
        .ignoresSafeArea(.all)
        .navigationBarHidden(true) // 隐藏导航栏
        .onAppear {
            // 请求网络权限
            let monitor = NWPathMonitor()
            monitor.pathUpdateHandler = { path in
                DispatchQueue.main.async {
                    self.networkAuthorizationStatus = path.status
                }
            }
            let queue = DispatchQueue(label: "NetworkMonitor")
            monitor.start(queue: queue)
        }
    }
}
#Preview {
    LaunchView()
}启动页面 
设置 App 的启动页面一般有两种方式:
Launch Screen.storyboard
在项目根目录新建文件,选择 Launch Screen,文件名就可以叫 Launch Screen

进入 Launch Screen.storyboard 可以在试图中点击右上家的加号添加一些组件,实现布局样式。
最后在 app 的信息设置中选择刚才编辑好的文件即可。

单独的 View
上面的做法可能扩展性不是很强,你也可以使用一个单独的 view 来实现启动页面
可以使用一个变量控制是否在启动页面还是主页面
ZStack {
    if navigateRunView == true {
        LayoutView() // 主页面
    } else {
        LaunchView() // 启动页面
    }
}