iOS高级分享 — 谈谈 IOS 13

为了纪念上周发布的iOS13,我们来看一看你现在可以在你的应用程序中使用的一些模糊的(基本上是没有文档的)API。我们已经清除了最好的部分IOS13发行说明API差异现在把它们呈现给你。
下面是我们最喜欢的一些从iOS 13开始可以做的事情:

生成URL的富表示

新的IOS 13,链接呈现框架提供一种方便的内置方式来复制您在消息中看到的URL的丰富预览。如果您的应用程序有任何聊天或消息功能,您肯定会想要检查这一点。

丰富的URL预览至少可以追溯到20世纪初,随着微格式由语义web先驱和早期先驱使用khtml2png若要生成网页的缩略图图像,请执行以下操作。快到2010年,随着社交媒体和用户生成的内容的兴起,facebook创建了OpenGraphProtocol允许网络出版商自定义其页面的外观时,张贴在新闻提要。

如今,大多数网站都有开放图形标签在他们的网站上,为社会网络,搜索引擎,以及任何其他链接被贩运的地方提供他们的内容摘要。例如,如果您对这个网页进行了“查看源”,您将看到以下内容:

<meta property="og:site_name" content="NSHipster" />
<meta property="og:image" content="https://nshipster.com/logo.png" />
<meta property="og:type" content="article" />
<meta property="og:title" content="iOS 13" />
<meta property="og:url" content="https://nshipster.com/ios-13/" />
<meta property="og:description" content="To mark last week's release of iOS 13, we're taking a look at some obscure (largely undocumented) APIs that you can now use in your apps." />

如果您想在应用程序中使用这些信息,现在可以使用LinkPresting框架的LPMetadataProvider类来获取元数据,并可选择地构造一个表示:

import LinkPresentation

let metadataProvider = LPMetadataProvider()
let url = URL(string: "https://nshipster.com/ios-13/")!

let metadataProvider = LPMetadataProvider()
metadataProvider.startFetchingMetadata(for: url) { [weak self] metadata, error in
    guard let metadata = metadata else { return }

    let linkView = LPLinkView(metadata: metadata)
    self?.view.addSubview(linkView)
}

在设置适当的约束(或者调用sizeToFit()),您将得到以下内容,用户可以点击它预览链接的网页:

在startFetchingMetadata(for:)完成处理程序,您可以检查和变异从服务器检索的元数据。您可能会利用这个机会为没有这些内容的页面添加默认的图像/视频,将文本翻译成用户首选的语言之一,或者检查显式文本和媒体。

或者,如果您已经在-app中拥有元数据,或者不能或不希望远程获取元数据,则可以构造一个LPLinkMetadata直接:

let metadata = LPLinkMetadata()
metadata.url = url
metadata.title = "iOS 13"
metadata.iconProvider = ...

let linkView = LPLinkView(metadata: metadata)

LPMetadataProvider在IOS上做广告,但MacOS客户端必须具有com.apple.security.network.client权限,以便从远程URL获取元数据。

执行设备上语音识别

SFSpeechRecognizer在iOS 13中获得一次重大升级-最显著的是它增加了对设备上语音识别的支持。

以前,抄写需要一个互联网连接,并且被限制在最多1分钟的时间内,每天的请求都有限制。但是现在,你可以完全在设备上和离线下进行语音识别,没有任何限制.唯一要注意的是,离线转录不如服务器连接的效果好,而且只适用于某些语言。

若要确定脱机转录是否可用于用户的区域设置,请检查SFSpeechRecognizer财产supportsOnDeviceRecognition…在出版时,所支持的语文清单如下:

根据IOS 13发布说明:“supportsOnDeviceRecognition属性总是返回false第一次被访问的时候。几秒钟后,再次访问它将返回正确的值。“

但这并不是iOS 13中的全部语音识别!SFSpeechRecognizer现在提供信息,包括说话速率和平均停顿时间,以及语音分析功能,如抖动(音高的变化)和闪光器(振幅的变化)。

import Speech

guard SFSpeechRecognizer.authorizationStatus() == .authorized
    let recognizer = SFSpeechRecognizer()
else {
    fatalError()
}

let url: URL = ...
let request = SFSpeechURLRecognitionRequest(url: url)

recognizer.recognitionTask(with: request) { (result, error) in
    guard let result = result else { return }

    for segment in result.bestTranscription.segments {
        guard let voiceAnalytics = segment.voiceAnalytics else { continue }

        let pitch = voiceAnalytics.pitch.acousticFeatureValuePerFrame
        let voicing = voiceAnalytics.voicing.acousticFeatureValuePerFrame
        let jitter = voiceAnalytics.jitter.acousticFeatureValuePerFrame
        let shimmer = voiceAnalytics.shimmer.acousticFeatureValuePerFrame
    }
}

您的应用程序可以使用有关音调、语音和其他功能的信息(可能)。协同CoreML)区分说话人或从说话人的拐弯处确定潜台词。

发送和接收Web套接字消息

说到FoundationURL加载系统,我们现在已经有了本机支持的东西,这些东西在我们的愿望列表中占据了很多年的首位:Web套接字.

多亏了新的URLSessionWebSocketTask在IOS 13中,您现在可以像发送HTTP请求一样轻松可靠地在应用程序中集成实时通信-所有这些都没有任何第三方库或框架:

let url = URL(string: "wss://...")!
let webSocketTask = URLSession.shared.webSocketTask(with: url)
webSocketTask.resume()

// Configure how messages are received
webSocketTask.receive { result in
    guard let .success(message) = result else { return }
    ...
}

// Send a message
let message: URLSessionWebSocketTask.Message = .string("Hello, world!")
webSocketTask.send(message) { error in
    ...
}

// Eventually...
webSocketTask.cancel(with: .goingAway, reason: nil)

对于web套接字的低级控制,包括客户端和服务器支持,请查看网络框架.

多年来,网络可能一直是整个苹果技术体系中移动最快的部分。每个WWDC,都有太多要谈论的话题,以至于他们不得不在两个不同的会议上打破他们的内容。2019年也不例外,我们强烈建议你花点时间看看今年的“网络进步”会议(第一部分, 第二部分).

用地图做更多

MapKit是Apple SDK的另一个组成部分,它在WWDC上年复一年地表现强劲。在我们日常生活中,影响最大的往往是那些小小的触碰。

例如,新的MKMapView.CameraBoundaryiOS 13中的API使得在不完全锁定地图的情况下将地图的视口限制到特定区域变得容易得多。

let region = MKCoordinateRegion(center: mapView.center,
                                        latitudinalMeters: 1000,
                                        longitudinalMeters: 1000)
mapView.cameraBoundary = MKMapView.CameraBoundary(coordinateRegion: region)

新的MKPointOfInterestFilterapi,您现在可以自定义地图视图的外观,只显示特定类型的兴趣点。(而以前是全无命题).

let filter = MKPointOfInterestFilter(including: [.cafe])
mapView.pointOfInterestFilter = filter // only show cafés

最后,用MKGeoJSONDecoder,我们现在有一个内置的方式来拉进来。GeoJSON来自Web服务和其他数据源的形状。

let decoder = MKGeoJSONDecoder()

if let url = URL(string: "..."),
    let data = try? Data(contentsOfURL: url),
    let geoJSONObjects = try? decoder.decode(data) {

    for case let overlay as MKOverlay in geoJSONObjects {
        mapView.addOverlay(overlay)
    }
}

用JavaScript信守诺言

如果你喜欢我们的文章JavaScriptCore,你会很高兴知道JSValue对象现在本地支持承诺.

对于不熟悉的人:在JavaScript中,Promise表示异步操作的最终完成(或拒绝)及其结果值的对象。承诺是现代JS开发的支柱-也许最值得注意的是fetchAPI

iOS 13中JavaScriptCore的另一个补充是对符号(不,不是那些符号)。有关init(newSymbolFromDescription:in:), 参考文档 猜猜怎么用。

对目标C相关对象的响应(?)

在云雀上,我们决定看看目标C今年是否有什么新发现,并惊讶地发现objc_setHook_setAssociatedObject…同样,除了声明之外,我们没有什么可继续进行的,但是现在您似乎可以配置一个块,以便在关联对象已经设定好了。对于任何仍深入目标C运行时的人来说,这听起来很方便。

驯服活动项目(?)

关于失踪文件的问题:UIActivityItemsConfiguration似乎就像在新的iOS13共享表中管理操作的一个很有吸引力的选项,但是我们不知道从哪里开始…

遗憾的是,我们还没有足够的信息来利用这一点。

格式列表和相关时间

中讨论过的前一篇文章,IOS 13为基金会带来了两个新的格式:ListFormatter和RelativeDateTimeFormatter.

不是为了唠叨这件事,但他们都是仍然因此,如果您想了解更多信息,我们建议您从7月份开始查看这篇文章。或者,如果您很着急,下面是一个演示如何将两者结合使用的快速示例:

import Foundation

let relativeDateTimeFormatter = RelativeDateTimeFormatter()
relativeDateTimeFormatter.dateTimeStyle = .named

let listFormatter = ListFormatter()
listFormatter.string(from: [
    relativeDateTimeFormatter.localizedString(from: DateComponents(day: -1)),
    relativeDateTimeFormatter.localizedString(from: DateComponents(day: 0)),
    relativeDateTimeFormatter.localizedString(from: DateComponents(day: 1))
]) // "yesterday, today, and tomorrow"

跟踪排队操作的进展

从iOS 13开始,OperationQueue现在有一个progress财产。

当然,(NS)Progress对象并不是最简单、最方便的东西(我们一直打算在某个时候写一篇关于它们的文章),但是它们有一个完整的、经过深思熟虑的API,甚至在应用程序框架中也有一些方便的插槽。

例如,检查连接一个UIProgressView若要以其方式显示操作队列的实时更新进度,请执行以下操作:observedProgress财产:

import UIKit

fileprivate class DownloadOperation: Operation { ... }

class ViewController: UIViewController {
    private let operationQueue = {
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
    }()

    @IBOutlet private var progressView: UIProgressView!

    @IBAction private func startDownloading(_ sender: Any) {
        operationQueue.cancelAllOperations()
        progressView.observedProgress = operationQueue.progress

        for url in [...] {
            let operation = DownloadOperation(url: url)
            operationQueue.addOperation(operation)
        }
    }
}

还值得一提的是13岁时出现的其他一些API,如schedule(after:interval:tolerance:options:?,哪条线索OperationQueue进入新的组合框架以一种很好的方式,而且addBarrierBlock(?,它的工作原理可能是调度屏障(虽然没有文档,但这是任何人的猜测)。

轻松管理后台任务

定义应用程序与竞争对手的区别之一是,它们使用后台任务来确保应用程序在下一次进入前台时得到充分同步和更新。

iOS 7是第一个提供用于调度后台任务的官方API (尽管在此之前,开发人员使用了各种创造性的方法)…但在这段时间里,多个因素-从iOS应用程序能力和复杂性的提高,到对应用程序的性能、效率和隐私的日益重视-催生了对更全面解决方案的需求。

该解决方案是通过新的iOS 13实现的。背景测试框架.

如今年WWDC会议所述“应用程序后台执行的进展”该框架区分了两大类背景任务:

  • 应用程序刷新任务:短命的任务,使应用程序一整天都保持最新。
  • 背景处理任务*用于执行可推迟的维护任务的长期任务

WWDC会话和附带的示例代码项目很好地解释了如何将这两者结合到您的应用程序中。但是,如果您想要它的快速要点,下面是一个应用程序从Web服务器定期刷新的一个小例子:

import UIKit
import BackgroundTasks

fileprivate let backgroundTaskIdentifier = "com.nshipster.example.task.refresh"

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    lazy var backgroundURLSession = {
        let configuration = URLSessionConfiguration.background(withIdentifier: "com.nshipster.url-session.background")
        configuration.discretionary = true
        configuration.timeoutIntervalForRequest = 30

        return URLSession(configuration: configuration, delegate: ..., delegateQueue: ...)
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
       ...

        BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }

        return true
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        scheduleAppRefresh()
    }

    func scheduleAppRefresh() {
        let request = BGAppRefreshTaskRequest(identifier: backgroundTaskIdentifier)
        request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 10)

        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Couldn't schedule app refresh: \(error)")
        }
    }

    func handleAppRefresh(task: BGAppRefreshTask) {
        scheduleAppRefresh()

        let url: URL = ...
        var dataTask = backgroundURLSession.dataTask(with: url) { (data, response, error) in
            ...
            let success = (200..<300).contains(response?.statusCode)
            task.setTaskCompleted(success: success)
        }

        task.expirationHandler = {
            dataTask.cancel()
        }

        dataTask.resume()
    }

    ...
}

以前进行背景更新的方法-即,UIApplication.setMinimumBackgroundFetchInterval(:)和UIApplicationDelegate.application(:performFetchWithCompletionHandler:)-现在已在IOS 13中被否决。

注释文本内容类型以获得更好的可访问性。
你知道听到一些人读出网址是多么令人沮丧吗?“eɪʧtipiˈkoʊlənslʃˈdʌbəljuˈdʌbəljudɑt”…)这就是当画外音试图在不了解更多信息的情况下阅读一些东西什么在看书。

iOS 13承诺通过新的accessibilityTextualContext财产和UIAccessibilityTextAttributeContextNSAttributedString属性键只要有可能,一定要用最能描述所显示的文本类型的常量对视图和属性字符串进行注释:

  • UIAccessibilityTextualContextConsole
  • UIAccessibilityTextualContextFileSystem
  • UIAccessibilityTextualContextMessaging
  • UIAccessibilityTextualContextNarrative
  • UIAccessibilityTextualContextSourceCode
  • UIAccessibilityTextualContextSpreadsheet
  • UIAccessibilityTextualContextWordProcessing

隐式删除从情节提要初始化的视图控制器中的未包装选项

SwiftUI可能已经预示了故事板的最终结束,但这并不意味着事情不是也不会继续好转,直到那一天到来。

对于斯威夫特纯粹主义者来说,在使用故事板进行iOS项目时,最令人恼火的反模式之一就是视图控制器初始化。由于Interface Builder的“准备”方法与SWIFT的对象初始化规则之间的阻抗不匹配,我们经常不得不在使所有属性成为非私有、变量和(隐式展开)选项之间进行选择,或者完全选择前面的故事板。

Xcode 11和IOS 13允许这些范例通过新的方式来协调它们之间的差异。@IBSegueAction属性和一些新的UIStoryboard班级方法:

首先,@IBSegueAction属性可以应用视图控制器方法声明来指定自己为API,负责创建segue的目标视图控制器。(即destinationViewController的属性segue参数中的prepare(for:sender:)方法).

@IBSegueAction
func makeProfileViewController(coder: NSCoder, sender: Any?, segueIdentifier: String?) -> ProfileViewController? {
    ProfileViewController(
        coder: coder,
        name: self.selectedName,
        avatarImageURL: self.selectedAvatarImageURL
    )
}

第二,UIStoryboard类方法instantiateInitialViewController(creator:)和instantiateViewController(identifier:creator:)提供一个方便的基于块的定制点来实例化Storyboard的视图控制器.

import UIKit

struct Person { ... }

class ProfileViewController: UIViewController {
    let name: String
    let avatarImageURL: URL?

    init?(coder: NSCoder, name: String, avatarImageURL: URL?) {
        self.name = name
        self.avatarImageURL = avatarImageURL

        super.init(coder: coder)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

let storyboard = UIStoryboard(name: "ProfileViewController", bundle: nil)
storyboard.instantiateInitialViewController(creator: { decoder in
    ProfileViewController(
        coder: decoder,
        name: "Johnny Appleseed",
        avatarImageURL: nil
    )
})

和新的UIKit场景在我们等待SwiftUI成熟和稳定的时候,iOS 13提供了大量的工作。

这是为了我们的iOS 13功能的总结性,你可能已经错过了。但是请放心-我们计划在未来的NSHipster文章中涵盖更多的新API。

如果遗漏了什么你想让我们来掩护,请通过加我们的交流群 点击此处进交流群 ,来一起交流或者发布您的问题,意见或反馈。

原文地址 https://nshipster.com/ios-13/#track-the-progress-of-enqueued-operations

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值