Если хотели разобраться в многопоточности - статья для вас - все достаточно хорошо и понятно написано.
Ниже приведу пару тезисов.
EasySwift iOS🍏 - это канал, где вы найдете все самое интересное в мире разработки для iOS. Если вы разрабатываете приложения для устройств Apple или интересуетесь этой темой, то этот канал станет вашим незаменимым помощником. Здесь вы найдете актуальные статьи, новости, обзоры и советы от опытных разработчиков
Каждый день мы публикуем свежие материалы, которые помогут вам быть в курсе последних тенденций и новостей в мире iOS. Если у вас есть собственные идеи или хотите предложить статью для публикации, обращайтесь к @EasySwiftBot. Все вопросы также можно адресовать @itereznikov. Присоединяйтесь к нам и узнавайте все об iOS разработке первыми!
14 Feb, 07:01
12 Feb, 07:03
10 Feb, 07:04
ListFormatter
позволяет объединять списки строк в читаемые предложения, учитывая локализацию пользователя.
// Используя joined
let languages = ["Swift", "Kotlin", "Rust"]
let joinedLanguages = languages.dropLast().joined(separator: ", ")
+ (languages.count > 1 ? " and " : "")
+ (languages.last ?? "")
// "Swift, Kotlin and Rust"
// Используя ListFormatter
let listFormatter = ListFormatter()
// "Swift, Kotlin, and Rust"
listFormatter.string(from: ["Swift", "Kotlin", "Rust"])
string(from:)
класса ListFormatter
преобразует массив элементов в строку, учитывая локаль, например, 'Swift, Kotlin y Rust' для испанского языка.let listFormatter = ListFormatter()
listFormatter.locale = Locale(identifier: "es-ES")
// "Swift, Kotlin y Rust"
listFormatter.string(from: ["Swift", "Kotlin", "Rust"])
ListFormatter
может работать не только со строками, но и с любыми типами, которые могут быть представлены как строки, при этом пользовательские типы должны соответствовать протоколу CustomStringConvertible
.formatted
на массиве строк предлагает более лаконичный способ форматирования списков с дополнительными параметрами, такими как тип соединения (например, 'или' или 'и'). 07 Feb, 07:00
Core Image
— это мощный фреймворк для обработки и анализа изображений, разработанный Apple, который позволяет разработчикам применять фильтры и выполнять сложные манипуляции с изображениями в реальном времени.CIImage
(представляет данные изображения), CIFilter
(применяет эффекты) и CIContext
(управляет рендерингом изображений).Core Image
предлагает широкий набор встроенных фильтров, которые можно комбинировать для создания сложных эффектов, а также возможность создания пользовательских фильтров с использованием языка Core Image Kernel
или Metal.
05 Feb, 07:01
03 Feb, 07:00
@MainActor
, что позволяет избежать ошибок, связанных с различиями в изоляции актора.setUp
и tearDown
, чтобы избежать предупреждений о синхронном контексте.setUp
и tearDown
, создавая объекты непосредственно в тестах, или перейти на Swift Testing для лучшей совместимости с актерами. 31 Jan, 07:00
func testFibonacciSequenceThrowsError() async throws {
do {
let _ = try await fibonacciSequence(count: -1)
XCTFail("Expected an error for negative count, but no error was thrown")
} catch FibonacciError.invalidCount {
// Success! The expected error was thrown.
} catch {
XCTFail("Unexpected error thrown: \(error)")
}
}
29 Jan, 07:00
Core Location:
CLLocationManager
вместо CLMonitor
и CLLocationUpdate
, так как он предлагает больше возможностей и меньше проблем с совместимостью.CLServiceSession
, так как он упрощает управление разрешениями на использование местоположения.CLBackgroundActivitySession
для получения обновлений местоположения.27 Jan, 07:00
24 Jan, 07:03
let button = UIButton(frame: buttonFrame)
view.addSubview(button)
button.setTitle("This is a button", for: .normal)
button.addTarget(
self,
action: #selector(handleTap),
for: .touchUpInside
)
@objc func handleTap() async {
Task {
debugPrint("handle Tap")
}
}
И в итоге получил краш. 22 Jan, 07:04
Background Assets
, который позволяет разработчикам хранить и автоматически загружать ресурсы приложений вне App Store, улучшая время загрузки и уменьшая размер приложения.On-Demand Resources
.info.plist
и создать манифест для управления загрузками, чтобы обеспечить корректную работу фреймворка.20 Jan, 14:03
20 Jan, 07:00
borrowing
, inout
или consuming
, что определяет уровень доступа к значению.discard
позволяет избежать дублирования логики очистки при использовании потребляющих методов.struct SingleUseTicket: ~Copyable {
let ticketID: Int
consuming func invalidate() {
print("Ticket \(ticketID) invalidated.")
// cleanup logic
discard self
}
deinit {
print("Ticket deinitialized.")
// cleanup logic
}
}
17 Jan, 07:03
15 Jan, 07:00
final
в Swift используется для ограничения переопределения классов, методов и свойств, обеспечивая стабильность и предсказуемость кода.final
для методов и свойств, которые должны оставаться неизменными, но следует учитывать баланс между безопасностью и гибкостью кода. 13 Jan, 08:30
rethrows
в Swift используется для функций, которые принимают функции, способные выбрасывать ошибки, и указывает, что функция выбросит ошибку только в случае, если одна из переданных функций выбросит ошибку.throws
, которое указывает, что функция может выбрасывать ошибку независимо от аргументов, rethrows позволяет функции выбрасывать ошибку только в зависимости от поведения переданных замыканий.rethrows
упрощает обработку ошибок в функциях высшего порядка, таких как map
, позволяя избежать необходимости всегда использовать try
или do-catch
блоки. 10 Jan, 07:02
08 Jan, 07:01
sending
, которое заменяет @Sendable
для обеспечения безопасности передачи замыканий и значений между контекстами изоляции.Sendable
позволяет передавать только безопасные для многопоточного доступа объекты, в то время как sending
позволяет передавать объекты, которые не могут быть использованы после захвата в замыкании.sending
и @Sendable
в Swift 6, а также объясняется, как компилятор обрабатывает ошибки при неправильном использовании.func exampleFunc() async {
let isNotSendable = MyClass()
// Value of non-Sendable type ... accessed after being transferred;
// later accesses could race
Task {
isNotSendable.count += 1
}
// Access can happen concurrently
print(isNotSendable.count)
}
30 Dec, 07:01
// This struct is not sendable
struct Movie {
let formatterCache = FormatterCache() // This is class
let releaseDate = Date()
var formattedReleaseDate: String {
let formatter = formatterCache.formatter(for: "YYYY")
return formatter.string(from: releaseDate)
}
}
// This struct is sendable
struct Movie {
var formattedReleaseDate = "2022"
}
27 Dec, 07:01
25 Dec, 08:00
23 Dec, 09:02
20 Dec, 07:00
private extension HTTPClient: URLSessionDelegate {
/// Handles server authentication challenges for Certificate Pinning.
func urlSession(
_ session: URLSession,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
) {
// We will write our pinning business logic here.
}
}
18 Dec, 07:01
protocol Registrable {
static func register()
}
extension Registrable {
static func register() {
print("Registering \(Self.self)")
}
}
class Service: Registrable {}
// Prints `Registering Service`
Service.register()
16 Dec, 07:01
let closedRange = Int.min...3
let partialRange = ...3
let numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers[closedRange]) // 💣💣💣 ERROR: Negative Array index is out of range
let numbers = [10, 20, 30, 40, 50, 60, 70]
print(numbers[partialRange]) // from the first index in the sequence, all the way to index: 3
// Prints "[10, 20, 30, 40]"
13 Dec, 07:01
11 Dec, 06:00
class ViewController: UIViewController {
...
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.startAnimating()
}
private func startAnimating() {
let animation = SwiftUI.Animation
.linear(duration: 1.3)
.repeatForever()
UIView.animate(animation) { [weak self] in
self?.animatingView?.transform = .init(scaleX: 2, y: 2)
}
}
}
Правда одно но: не все могут позволить себе апнуть минимальную поддерживаемую версию до 18 🫠
09 Dec, 06:00
07 Dec, 08:01
05 Dec, 08:00
03 Dec, 08:00
30 Nov, 08:00
func fetchImage(for url: URL, completion: @escaping (Result<UIImage, Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data, let image = UIImage(data: data) else {
DispatchQueue.main.async {
completion(.failure(ImageFetchingError.imageDecodingFailed))
}
return
}
DispatchQueue.main.async {
completion(.success(image))
}
}.resume()
}
Может превратиться в такую:
@MainActor
func fetchImage(for url: URL) async throws -> UIImage {
let (data, _) = try await URLSession.shared.data(from: url)
guard let image = UIImage(data: data) else {
throw ImageFetchingError.imageDecodingFailed
}
return image
}
28 Nov, 08:01
25 Nov, 08:01
22 Nov, 08:00
20 Nov, 08:00
18 Nov, 08:30
var urlComponents = URLComponents(string: "https://httpbin.org/get")!
/// Define the parameters.
let parameters: [String: String] = [
"name": "Antoine van der Lee",
"age": "33"
]
/// Add the query parameters to the URL.
urlComponents.queryItems = parameters.map { key, value in
URLQueryItem(name: key, value: value)
}
/// Ensure we have a valid URL and throw a URLError if it fails.
guard let url = urlComponents.url else {
throw URLError(.badURL)
}
/// Use URLSession to fetch the data asynchronously.
let (data, response) = try await URLSession.shared.data(from: url)
17 Nov, 08:01
14 Nov, 09:00
12 Nov, 08:00
08 Nov, 08:01
07 Nov, 08:01
05 Nov, 08:01
03 Nov, 08:00
func completeTodo(
request: ServerRequest<Todos_TodoID>,
context: ServerContext
) async throws -> ServerResponse<Todos_Todo> {
guard
var todo = todos.first(where: { $0.todoID == request.message.todoID })
else {
return .init(
error: RPCError.init(
code: .notFound,
message: "Todo not found."
)
)
}
todo.completed = true
todos = todos.filter { $0.todoID != request.message.todoID }
todos.append(todo)
return .init(message: todo)
}
01 Nov, 08:00
31 Oct, 08:00
22 Oct, 10:02
import NaturalLanguage
func getSemanticDistance(for word: String, in language: NLLanguage) {
// 1. Create the embedding for the language the word belongs to
if let embedding = NLEmbedding.wordEmbedding(for: language) {
// 2. Find the neighbors for the word
embedding.enumerateNeighbors(for: word, maximumCount: 10) { neighbor, distance in
// 3. Acces the neighbor distance from the word
print("\(neighbor): \(distance)")
return true
}
}
}
18 Oct, 08:00
16 Oct, 08:02
14 Oct, 10:00
11 Oct, 13:02
09 Oct, 08:02
07 Oct, 09:01
class Foo {
let a: UInt8
let b: UInt16
}
MemoryLayout<Foo>.size // 8
MemoryLayout<Foo>.alignment // 8
MemoryLayout<Foo>.stride // 8
04 Oct, 11:01
02 Oct, 09:02
json[0]?["name"]?["first"]?.stringValue
// заменится на
json[0]?.name?.first?.stringValue
30 Sep, 09:01
29 Sep, 10:02
// Повысить восприятие кода
typealias StringDictionary<T> = Dictionary<String, T>
// Упростить сигнатуры сложных типов и кортежей
typealias CompletionHandler = (Result<String, Error>) -> Void
typealias GridPoint = (x: Int, y: Int)
// Указать ограничения
typealias StringArrayDictionary = Dictionary<String, [String]>
25 Sep, 08:01
extension ISO8601DateFormatter {
func microsecondsDate(from dateString: String) -> Date? {
guard let millisecondsDate = date(from: dateString) else { return nil }
guard let fractionIndex = dateString.lastIndex(of: ".") else { return millisecondsDate }
guard let tzIndex = dateString.lastIndex(of: "Z") else { return millisecondsDate }
guard let startIndex = dateString.index(fractionIndex, offsetBy: 4, limitedBy: tzIndex) else { return millisecondsDate }
// Pad the missing zeros at the end and cut off nanoseconds
let microsecondsString = dateString[startIndex..<tzIndex].padding(toLength: 3, withPad: "0", startingAt: 0)
guard let microseconds = TimeInterval(microsecondsString) else { return millisecondsDate }
return Date(timeIntervalSince1970: millisecondsDate.timeIntervalSince1970 + microseconds / 1_000_000.0)
}
}
23 Sep, 09:01
20 Sep, 09:02
19 Sep, 12:02
[weak self]
в кложуре 🫠16 Sep, 09:01
extension Task {
@discardableResult
init<T>(
priority: TaskPriority? = nil,
operation: @escaping () async throws -> T,
queue: DispatchQueue = .main,
completion: @escaping (Result<T, Failure>) -> Void
) where Success == Void, Failure == any Error {
self.init(priority: priority) {
do {
let value = try await operation()
queue.async {
completion(.success(value))
}
} catch {
queue.async {
completion(.failure(error))
}
}
}
}
}
13 Sep, 08:01
Text("She will receive ^[\(count) games](inflect: true).")
11 Sep, 08:02
09 Sep, 09:02
06 Sep, 08:02