客户服务器模型
协议://主机地址/路径
。GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT
。状态码 | 描述 | 含义 |
---|---|---|
200 | Ok | 请求成功 |
400 | Bad Request | 客户端请求的语法出现错误,服务端无法解析 |
404 | Not Found | 服务端无法根据客户端的请求找到对应的资源 |
500 | Internal Server Error | 服务端内部出现问题,无法完成响应 |
请求响应过程
key : value
形式存在。,
分隔。{}
保存对象。[]
保存数组。""
。"
表示)[]
表示){}
表示)class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// get()
// post()
}
func get() {
// 1. 确定URL
let url = URL(string: "http://v.juhe.cn/toutiao/index?type=top&key=申请的key")
// 2. 创建请求
let urlRequest = URLRequest(url: url!)
// cachePolicy: 缓存策略,App最常用的缓存策略是returnCacheDataElseLoad,表示先查看缓存数据,没有缓存再请求
// timeoutInterval:超时时间
// let urlRequest = URLRequest(url: url!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 5)
let config = URLSessionConfiguration.default
// 3. 创建URLSession
let session = URLSession(configuration: config)
// 4. 创建任务
let task = session.dataTask(with: urlRequest) { data, _, error in
if error != nil {
print(error!)
} else {
if let data = data {
print(String(data: data, encoding: .utf8)!)
}
}
}
// 5. 启动任务
task.resume()
}
func post() {
let url = URL(string: "http://v.juhe.cn/toutiao/index")
var urlRequest = URLRequest(url: url!)
// 指明请求方法
urlRequest.httpMethod = "POST"
// 指明参数
let params = "type=top&申请的key"
// 设置请求体
urlRequest.httpBody = params.data(using: .utf8)
let config = URLSessionConfiguration.default
// delegateQueue决定了代理方法在哪个线程中执行
let session = URLSession(configuration: config, delegate: self, delegateQueue: OperationQueue())
let task = session.dataTask(with: urlRequest)
task.resume()
}
}
// MARK:- URLSessionDataDelegate
extension ViewController: URLSessionDataDelegate {
// 开始接收数据
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive response: URLResponse, completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
// 允许接收服务器的数据,默认情况下请求之后不接收服务器的数据即不会调用后面获取数据的代理方法
completionHandler(URLSession.ResponseDisposition.allow)
}
// 获取数据
// 根据请求的数据量该方法可能会调用多次,这样data返回的就是总数据的一段,此时需要用一个全局的Data进行追加存储
func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
let result = String(data: data, encoding: .utf8)
if let result = result {
print(result)
}
}
// 获取结束
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print(error)
} else {
print("=======成功=======")
}
}
}
注意:如果网络请求是 HTTP 而非 HTTPS,默认情况下,iOS 会阻断该请求,此时需要在 Info.plist 中进行如下配置。
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
let url = "https://www.baidu.com?name=张三"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
print(url) // URL中文转码
print(url.removingPercentEncoding!) // URL中文解码
let str = "阿楚姑娘"
.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
let url = URL(string: "https://music.163.com/#/search/m/?s=\(str)&type=1")
class ViewController: UIViewController {
// 下载进度
@IBOutlet var downloadProgress: UIProgressView!
// 下载图片
@IBOutlet var downloadImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
download()
}
func download() {
let url = URL(string: "http://172.20.53.240:8080/AppTestAPI/wall.png")!
let request = URLRequest(url: url)
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue())
let task = session.downloadTask(with: request)
task.resume()
}
}
extension ViewController: URLSessionDownloadDelegate {
// 下载完成
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
// 存入沙盒
let savePath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
// 文件类型根据下载的内容决定
let fileName = "\(Int(Date().timeIntervalSince1970)).png"
let filePath = savePath + "/" + fileName
print(filePath)
do {
try FileManager.default.moveItem(at: location, to: URL(fileURLWithPath: filePath))
// 显示到界面
DispatchQueue.main.async {
self.downloadImageView.image = UIImage(contentsOfFile: filePath)
}
} catch {
print(error)
}
}
// 计算进度
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
DispatchQueue.main.async {
self.downloadProgress.setProgress(Float(totalBytesWritten) / Float(totalBytesExpectedToWrite), animated: true)
}
}
}
上传数据需要服务端配合,不同的服务端代码可能会不一样,下面的上传代码适用于本人所写的服务端代码。
上传数据格式
class ViewController: UIViewController {
let YFBoundary = "AnHuiWuHuYungFan"
@IBOutlet var uploadInfo: UILabel!
@IBOutlet var uploadProgress: UIProgressView!
override func viewDidLoad() {
super.viewDidLoad()
upload()
}
func upload() {
// 1. 确定URL
let url = URL(string: "http://172.20.53.240:8080/AppTestAPI/UploadServlet")!
// 2. 确定请求
var request = URLRequest(url: url)
// 3. 设置请求头
let head = "multipart/form-data;boundary=\(YFBoundary)"
request.setValue(head, forHTTPHeaderField: "Content-Type")
// 4. 设置请求方式
request.httpMethod = "POST"
// 5. 创建NSURLSession
let session = URLSession(configuration: URLSessionConfiguration.default, delegate: self, delegateQueue: OperationQueue())
// 6. 获取上传的数据(按照固定格式拼接)
var data = Data()
let header = headerString(mimeType: "image/png", uploadFile: "wall.png")
data.append(header.data(using: .utf8)!)
data.append(uploadData())
let tailer = tailerString()
data.append(tailer.data(using: .utf8)!)
// 7. 创建上传任务 上传的数据来自getData方法
let task = session.uploadTask(with: request, from: data) { _, _, error in
// 上传完毕后
if error != nil {
print(error!)
} else {
DispatchQueue.main.async {
self.uploadInfo.text = "上传成功"
}
}
}
// 8. 执行上传任务
task.resume()
}
// 开始标记
func headerString(mimeType: String, uploadFile: String) -> String {
var data = String()
// --Boundary\r\n
data.append("--" + YFBoundary + "\r\n")
// 文件参数名 Content-Disposition: form-data; name="myfile"; filename="wall.jpg"\r\n
data.append("Content-Disposition:form-data; name=\"myfile\";filename=\"\(uploadFile)\"\r\n")
// Content-Type 上传文件的类型 MIME\r\n\r\n
data.append("Content-Type:\(mimeType)\r\n\r\n")
return data
}
// 结束标记
func tailerString() -> String {
// \r\n--Boundary--\r\n
return "\r\n--" + YFBoundary + "--\r\n"
}
func uploadData() -> Data {
let image = UIImage(named: "wall.png")
let imageData = image!.pngData()
return imageData!
}
}
extension ViewController: URLSessionTaskDelegate {
// 上传进去
func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {
DispatchQueue.main.async {
self.uploadProgress.setProgress(Float(totalBytesSent) / Float(totalBytesExpectedToSend), animated: true)
}
}
// 上传出错
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
if let error = error {
print(error)
}
}
}
URLCache.shared
获取,也可以自定义。// 创建URLCache
// memoryCapacity:内存缓存容量
// diskCapacity:硬盘缓存容量
// directory:硬盘缓存路径
let cache = URLCache(memoryCapacity: 10 * 1024 * 1024, diskCapacity: 100 * 1024 * 1024, directory: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first)
// 替换默认的缓存管理对象
URLCache.shared = cache
let url = URL(string: "http://v.juhe.cn/toutiao/index?type=top&key=申请的key")
let urlRequest = URLRequest(url: url!, cachePolicy: .returnCacheDataElseLoad, timeoutInterval: 5)
let cache = URLCache.shared
// 内存缓存大小
cache.memoryCapacity
// 硬盘缓存大小
cache.diskCapacity
// 已用内存缓存大小
cache.currentMemoryUsage
// 已用硬盘缓存大小
cache.currentDiskUsage
// 获取某个请求的缓存
let cacheResponse = cache.cachedResponse(for: urlRequest)
// 删除某个请求的缓存
cache.removeCachedResponse(for: urlRequest)
// 删除某个时间点开始的缓存
cache.removeCachedResponses(since: Date().addingTimeInterval(-60 * 60 * 48))
// 删除所有缓存
cache.removeAllCachedResponses()
WebKit
模块。// 创建URL
let url = URL(string: "https://www.abc.edu.cn")
// 创建URLRequest
let request = URLRequest(url: url!)
// 创建WKWebView
let webView = WKWebView(frame: UIScreen.main.bounds)
// 加载网页
webView.load(request)
// 文件夹路径
let basePath = Bundle.main.path(forResource: "localWeb", ofType: nil)!
// 文件夹URL
let baseUrl = URL(fileURLWithPath: basePath, isDirectory: true)
// html路径
let filePath = basePath + "/index.html"
// 转成文件
let fileContent = try? NSString(contentsOfFile: filePath, encoding: String.Encoding.utf8.rawValue)
// 创建WKWebView
let webView = WKWebView(frame: UIScreen.main.bounds)
// 加载html
webView.loadHTMLString(fileContent! as String, baseURL: baseUrl)
注意:如果是本地资源是文件夹,拖进项目时,需要勾选
Create folder references
,然后用Bundle.main.path(forResource: "文件夹名", ofType: nil)
获取资源路径。
lazy var webView: WKWebView = {
// 创建WKPreferences
let preferences = WKPreferences()
// 开启JavaScript
preferences.javaScriptEnabled = true
// 创建WKWebViewConfiguration
let configuration = WKWebViewConfiguration()
// 设置WKWebViewConfiguration的WKPreferences
configuration.preferences = preferences
// 创建WKUserContentController
let userContentController = WKUserContentController()
// 配置WKWebViewConfiguration的WKUserContentController
configuration.userContentController = userContentController
// 给WKWebView与Swift交互起一个名字:callbackHandler,WKWebView给Swift发消息的时候会用到
// 此句要求实现WKScriptMessageHandler
configuration.userContentController.add(self, name: "callbackHandler")
// 创建WKWebView
var webView = WKWebView(frame: UIScreen.main.bounds, configuration: configuration)
// 让WKWebView翻动有回弹效果
webView.scrollView.bounces = true
// 只允许WKWebView上下滚动
webView.scrollView.alwaysBounceVertical = true
// 设置代理WKNavigationDelegate
webView.navigationDelegate = self
// 返回
return webView
}()
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0,user-scalable=no"/>
</head>
<body>
iOS传过来的值:<span id="name"></span>
<button onclick="responseSwift()">响应iOS</button>
<script type="text/javascript">
// 给Swift调用
function sayHello(name) {
document.getElementById("name").innerHTML = name
return "Swift你也好!"
}
// 调用Swift方法
function responseSwift() {
// 这里的callbackHandler是创建WKWebViewConfiguration是定义的
window.webkit.messageHandlers.callbackHandler.postMessage("JavaScript发送消息给Swift")
}
</script>
</body>
</html>
// 加载完毕以后执行
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
// 调用JavaScript方法
webView.evaluateJavaScript("sayHello('WebView你好!')") { (result, err) in
// result是JavaScript返回的值
print(result, err)
}
}
// Swift方法,可以在JavaScript中调用
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
print(message.body)
}
class ViewController: UIViewController {
// 懒加载WKWebView
...
// 加载本地html
let html = try! String(contentsOfFile: Bundle.main.path(forResource: "index", ofType: "html")!, encoding: String.Encoding.utf8)
override func viewDidLoad() {
super.viewDidLoad()
// 标题
title = "WebView与JavaScript交互"
// 加载html
webView.loadHTMLString(html, baseURL: nil)
view.addSubview(webView)
}
}
// 遵守两个协议
extension ViewController: WKNavigationDelegate, WKScriptMessageHandler {
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
...
}
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
...
}
}
SafariServices
模块。import SafariServices
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
showSafariViewController()
}
func showSafariViewController() {
// URL
let url = URL(string: "https://www.baidu.com")
// 创建SFSafariViewController
let sf = SFSafariViewController(url: url!)
// 设置代理
sf.delegate = self
// 显示
present(sf, animated: true, completion: nil)
}
}
extension ViewController: SFSafariViewControllerDelegate {
// 点击左上角的完成(done)
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
print(#function)
}
// 加载完成
func safariViewController(_ controller: SFSafariViewController, didCompleteInitialLoad didLoadSuccessfully: Bool) {
print(#function)
}
}