- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试使用 Alamofire 上传图像。另外,我正在使用 ReachabilitySwift 来了解互联网连接的状态。现在,当我尝试上传图像并在中间关闭网络时,我会删除所有 alamofire 请求。下面是代码:
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach { $0.cancel() }
uploadTasks.forEach { $0.cancel() }
downloadTasks.forEach { $0.cancel() }
}
当互联网再次打开时,我再次开始上传过程。代码如下:
func internetAvailable(){
DispatchQueue.global(qos: .userInitiated).async {
DispatchQueue.main.async{
self.uploadImage()
}
}
}
func uploadImagesToServer(){
DispatchQueue.main.async{
self.uploadImage()
}
}
首先在viewDidLoad
中,uploadImagesToServer()
被调用。在该方法的中间,当它仍在上传图像时,互联网被关闭。当互联网重新打开时,它会转到 internetAvailable()
,上传图像,但是当我尝试重新加载表格时,它会转到 numberOfRowsInSection
,但不在 cellForRow
方法。
以下是我尝试过的方法:
numberOfRowsInSection
中的计数,正确。尝试在主线程中使用以下方法调用tableview.reloadData()
:
DispatchQueue.main.async {
self.tableView.reloadData()
}
表格 View 代码:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return numberOfImages
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//places image onto imageView
}
如有任何帮助,我们将不胜感激。
最佳答案
可达性的替代方案是使用后台 URLSession
进行上传。这样,您无需对 Reachability 执行任何操作。重新建立连接后,您启动的上传将自动发送(即使您的应用当时未运行)。
后台 session 涉及一些限制:
上传任务必须使用基于文件的上传(不是Data
或Stream
)。这意味着一旦您构建了请求,您必须在上传之前将其保存到文件中。 (如果您构建分段上传请求,Alamofire 会为您执行此操作。)
后台 session 的整体思想是,即使您的应用程序已暂停(或终止),它们也会继续运行。因此,您不能使用我们非常熟悉的完成处理程序模式(因为在上传请求时这些闭包可能已被丢弃)。因此,您必须依靠 SessionDelegate
的 taskDidComplete
关闭来确定请求是否成功完成。
您必须在应用委托(delegate)中实现handleEventsForBackgroundURLSession
,以保存完成处理程序。如果上传完成时您的应用程序未运行,操作系统将调用此方法,必须在处理完成后调用该方法。您必须向 Alamofire 提供此完成处理程序,以便它可以为您执行此操作。
如果您忽略保存此完成处理程序(因此如果 Alamofire 无法代表您调用它),您的应用程序将被立即终止。请务必保存此完成处理程序,以便您的应用程序可以在上传完成后透明地执行所需操作,然后再次正常挂起。
只是警告您:如果用户强制退出您的应用程序(双击主页按钮并向上滑动应用程序),这将取消任何待处理的后台上传。下次启动应用程序时,它会通知您所有已取消的任务。
但是,如果用户刚刚离开您的应用程序(例如,只需点击主页按钮),则任何待处理的上传都将成功保留。您的应用程序甚至可能在其正常生命周期中终止(例如,由于用户随后可能使用的其他应用程序的内存压力),并且上传不会被取消。而且,当重新建立网络连接时,这些上传将开始,并且您的应用程序将以后台模式启动,以在上传完成时通知您。
但我做了类似以下的操作,重新建立连接时会自动发送上传内容。
import Alamofire
import os.log
import MobileCoreServices
/// The `OSLog` which we'll use for logging
///
/// Note, this class will log via `os_log`, not `print` because it's possible that the
/// app will have terminated (and thus not connected to Xcode debugger) by the time the
/// upload is done and we want to see our log statements.
///
/// By using `os_log`, we can watch what's going on in an app that is running
/// in background on the device through the macOS `Console` app. And I specify the
/// `subsystem` and `category` to simplify the filtering of the `Console` log.
private let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "BackgroundSession")
/// Background Session Singleton
///
/// This class will facilitate background uploads via `URLSession`.
class BackgroundSession {
/// Singleton for BackgroundSession
static var shared = BackgroundSession()
/// Saved version of `completionHandler` supplied by `handleEventsForBackgroundURLSession`.
var backgroundCompletionHandler: (() -> Void)? {
get { return manager.backgroundCompletionHandler }
set { manager.backgroundCompletionHandler = backgroundCompletionHandler }
}
/// Completion handler that will get called when uploads are done.
var uploadCompletionHandler: ((URLSessionTask, Data?, Error?) -> Void)?
/// Alamofire `SessionManager` for background session
private var manager: SessionManager
/// Dictionary to hold body of the responses. This is keyed by the task identifier.
private var responseData = [Int: Data]()
/// Dictionary to hold the file URL of original request body. This is keyed by the task identifier.
///
/// Note, if the app terminated between when the request was created and when the
/// upload later finished, we will have lost reference to this temp file (and thus
/// the file will not be cleaned up). But the same is true with Alamofire's own temp
/// files. You theoretically could save this to (and restore from) persistent storage
/// if that bothers you.
///
/// This is used only for `uploadJSON` and `uploadURL`. The `uploadMultipart` takes
/// advantage of Alamofire's own temp file process, so we don't have visibility to
/// the temp files it uses.
private var tempFileURLs = [Int: URL]()
private init() {
let configuration = URLSessionConfiguration.background(withIdentifier: Bundle.main.bundleIdentifier!)
manager = SessionManager(configuration: configuration)
// handle end of task
manager.delegate.taskDidComplete = { [unowned self] session, task, error in
self.uploadCompletionHandler?(task, self.responseData[task.taskIdentifier], error)
if let fileURL = self.tempFileURLs[task.taskIdentifier] {
try? FileManager.default.removeItem(at: fileURL)
}
self.responseData[task.taskIdentifier] = nil
self.tempFileURLs[task.taskIdentifier] = nil
}
// capture body of response
manager.delegate.dataTaskDidReceiveData = { [unowned self] session, task, data in
if self.responseData[task.taskIdentifier] == nil {
self.responseData[task.taskIdentifier] = data
} else {
self.responseData[task.taskIdentifier]!.append(data)
}
}
}
let iso8601Formatter = ISO8601DateFormatter()
/// Submit multipart/form-data request for upload.
///
/// Note, Alamofire's multipart uploads automatically save the contents to a file,
/// so this routine doesn't do that part.
///
/// Alamofire's implementation begs a few questions:
///
/// - It would appear that Alamofire uses UUID (so how can it clean up the file
/// if the download finishes after the app has been terminated and restarted ...
/// it doesn't save this filename anywhere in persistent storage, AFAIK); and
///
/// - Alamofire uses "temp" directory (so what protection is there if there was
/// pressure on persistent storage resulting in the temp folder being purged
/// before the download was done; couldn't that temp folder get purged before
/// the file is sent?).
///
/// This will generate the mimetype on the basis of the file extension.
///
/// - Parameters:
/// - url: The `URL` to which the request should be sent.
/// - parameters: The parameters of the request.
/// - fileData: The contents of the file being included.
/// - filename: The filename to be supplied to the web service.
/// - name: The name/key to be used to identify this file on the web service.
func uploadMultipart(url: URL, parameters: [String: Any], fileData: Data, filename: String, name: String) {
manager.upload(multipartFormData: { multipart in
for (key, value) in parameters {
if let string = value as? String {
if let data = string.data(using: .utf8) {
multipart.append(data, withName: key)
}
} else if let date = value as? Date {
let string = self.iso8601Formatter.string(from: date)
if let data = string.data(using: .utf8) {
multipart.append(data, withName: key)
}
} else {
let string = "\(value)"
if let data = string.data(using: .utf8) {
multipart.append(data, withName: key)
}
}
multipart.append(fileData, withName: name, fileName: filename, mimeType: self.mimeType(for: URL(fileURLWithPath: filename)))
}
}, to: url, encodingCompletion: { encodingResult in
switch(encodingResult) {
case .failure(let error):
os_log("encodingError: %{public}@", log: log, type: .error, "\(error)")
case .success:
break
}
})
}
/// Determine mime type on the basis of extension of a file.
///
/// This requires MobileCoreServices framework.
///
/// - parameter url: The file `URL` of the local file for which we are going to determine the mime type.
///
/// - returns: Returns the mime type if successful. Returns application/octet-stream if unable to determine mime type.
private func mimeType(for url: URL) -> String {
let pathExtension = url.pathExtension
if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue(),
let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
return mimetype as String
}
return "application/octet-stream";
}
/// Submit JSON request for upload.
///
/// - Parameters:
/// - url: The `URL` to which the request should be sent.
/// - parameters: The parameters of the request.
func uploadJSON(url: URL, parameters: [String: Any]) {
upload(url: url, parameters: parameters, encoding: JSONEncoding.default)
}
/// Submit `x-www-form-urlencoded` request for upload.
///
/// - Parameters:
/// - url: The `URL` to which the request should be sent.
/// - parameters: The parameters of the request.
func uploadURL(url: URL, parameters: [String: Any]) {
upload(url: url, parameters: parameters, encoding: URLEncoding.default)
}
/// Starts a request for the specified `urlRequest` to upload a file.
///
/// - Parameters:
/// - fileURL: The file `URL` of the file on your local file system to be uploaded.
/// - urlRequest: The `URLRequest` of request to be sent to remote service.
func uploadFile(fileURL: URL, with urlRequest: URLRequest) {
manager.upload(fileURL, with: urlRequest)
}
/// Starts a request for the specified `URL` to upload a file.
///
/// - Parameters:
/// - fileURL: The file `URL` of the file on your local file system to be uploaded.
/// - url: The `URL` to be used when preparing the request to be sent to remote service.
func uploadFile(fileURL: URL, to url: URL) {
manager.upload(fileURL, to: url)
}
/// Submit request for upload.
///
/// - Parameters:
/// - url: The `URL` to which the request should be sent.
/// - parameters: The parameters of the request.
/// - encoding: Generally either `JSONEncoding` or `URLEncoding`.
private func upload(url: URL, parameters: [String: Any], encoding: ParameterEncoding) {
let request = try! URLRequest(url: url, method: .post)
var encodedRequest = try! encoding.encode(request, with: parameters)
let fileURL = BackgroundSession.tempFileURL()
guard let data = encodedRequest.httpBody else {
fatalError("encoding failure")
}
try! data.write(to: fileURL)
encodedRequest.httpBody = nil
let actualRequest = manager.upload(fileURL, with: encodedRequest)
if let task = actualRequest.task {
tempFileURLs[task.taskIdentifier] = fileURL
}
}
/// Create URL for temporary file to hold body of request.
///
/// - Returns: The file `URL` for the temporary file.
private class func tempFileURL() -> URL {
let folder = URL(fileURLWithPath: NSTemporaryDirectory())
.appendingPathComponent(Bundle.main.bundleIdentifier! + "/BackgroundSession")
try? FileManager.default.createDirectory(at: folder, withIntermediateDirectories: true, attributes: nil)
return folder.appendingPathComponent(UUID().uuidString)
}
}
然后,您可以使用 multipart/form-data
在后台上传文件,如下所示:
let parameters = ["foo": "bar"]
guard let imageData = UIImagePNGRepresentation(image) else { ... }
BackgroundSession.shared.uploadMultipart(url: url, parameters: parameters, fileData: imageData, filename: "test.png", name: "image")
或者您可以使用 JSON 在后台上传文件,如下所示:
let parameters = [
"foo": "bar",
"image": imageData.base64EncodedString()
]
BackgroundSession.shared.uploadJSON(url: url, parameters: parameters)
例如,如果您希望在上传完成时通知您的 View Controller ,则可以使用 uploadCompletionHandler
:
override func viewDidLoad() {
super.viewDidLoad()
BackgroundSession.shared.uploadCompletionHandler = { task, data, error in
if let error = error {
os_log("responseObject: %{public}@", log: log, type: .debug, "\(error)")
return
}
if let data = data {
if let json = try? JSONSerialization.jsonObject(with: data) {
os_log("responseObject: %{public}@", log: log, type: .debug, "\(json)")
} else if let string = String(data: data, encoding: .utf8) {
os_log("responseString: %{public}@", log: log, type: .debug, string)
}
}
}
}
这只是记录结果,但您可以随意做任何您想做的事情。
关于ios - 使用 ReachabilitySwift 时无法重新加载表,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45896877/
我有一台 MySQL 服务器和一台 PostgreSQL 服务器。 需要从多个表中复制或重新插入一组数据 MySQL 流式传输/同步到 PostgreSQL 表。 这种复制可以基于时间(Sync)或事
如果两个表的 id 彼此相等,我尝试从一个表中获取数据。这是我使用的代码: SELECT id_to , email_to , name_to , status_to
我有一个 Excel 工作表。顶行对应于列名称,而连续的行每行代表一个条目。 如何将此 Excel 工作表转换为 SQL 表? 我使用的是 SQL Server 2005。 最佳答案 这取决于您使用哪
我想合并两个 Django 模型并创建一个模型。让我们假设我有第一个表表 A,其中包含一些列和数据。 Table A -------------- col1 col2 col3 col
我有两个表:table1,table2,如下所示 table1: id name 1 tamil 2 english 3 maths 4 science table2: p
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 1 年前。 Improve th
下面两个语句有什么区别? newTable = orginalTable 或 newTable.data(originalTable) 我怀疑 .data() 方法具有性能优势,因为它在标准 AX 中
我有一个表,我没有在其中显式定义主键,它并不是真正需要的功能......但是一位同事建议我添加一个列作为唯一主键以随着数据库的增长提高性能...... 谁能解释一下这是如何提高性能的? 没有使用索引(
如何将表“产品”中的产品记录与其不同表“图像”中的图像相关联? 我正在对产品 ID 使用自动增量。 我觉得不可能进行关联,因为产品 ID 是自动递增的,因此在插入期间不可用! 如何插入新产品,获取产品
我有一个 sql 表,其中包含关键字和出现次数,如下所示(尽管出现次数并不重要): ____________ dog | 3 | ____________ rat | 7 | ____
是否可以使用目标表中的LAST_INSERT_ID更新源表? INSERT INTO `target` SELECT `a`, `b` FROM `source` 目标表有一个自动增量键id,我想将其
我正在重建一个搜索查询,因为它在“我看到的”中变得多余,我想知道什么 (albums_artists, artists) ( ) does in join? is it for boosting pe
以下是我使用 mysqldump 备份数据库的开关: /usr/bin/mysqldump -u **** --password=**** --single-transaction --databas
我试图获取 MySQL 表中的所有行并将它们放入 HTML 表中: Exam ID Status Assigned Examiner
如何查询名为 photos 的表中的所有记录,并知道当前用户使用单个查询将哪些结果照片添加为书签? 这是我的表格: -- -- Table structure for table `photos` -
我的网站都在 InnoDB 表上运行,目前为止运行良好。现在我想知道在我的网站上实时发生了什么,所以我将每个页面浏览量(页面、引荐来源网址、IP、主机名等)存储在 InnoDB 表中。每秒大约有 10
我在想我会为 mysql 准备两个表。一个用于存储登录信息,另一个用于存储送货地址。这是传统方式还是所有内容都存储在一张表中? 对于两个表...有没有办法自动将表 A 的列复制到表 B,以便我可以引用
我不是程序员,我从这个表格中阅读了很多关于如何解决我的问题的内容,但我的搜索效果不好 我有两张 table 表 1:成员 id*| name | surname -------------------
我知道如何在 ASP.NET 中显示真实表,例如 public ActionResult Index() { var s = db.StaffInfoDBSet.ToList(); r
我正在尝试运行以下查询: "insert into visits set source = 'http://google.com' and country = 'en' and ref = '1234
我是一名优秀的程序员,十分优秀!