Swift Weekly 中文 – Issue #176

本篇为译文,原文可见:链接

本周的 issue ,我们主要讨论 updating the view stateanimations in SwiftUIdebugging Combine

文章

如果你正在查找关于 body 是计算型时,去尝试修改 view 状态的资料的话,这篇文章将会告诉你可以做哪些操作以及不应该做哪些操作。

Fernando Moya de Rivas 使用 SwiftUI 创建了几个有趣的动画。

学习不同方式来调试由 Swift Combine framework 编写的 functional reactive code 。通过 print()handleEvents() 来查看 console;通过 breakpointOnError()breakpoint() 来生成 Xcode 断点;以及通过绘制图表的方式。

Josh Adams 通过 SwiftUI 修改了他的一个 app,并分享了一些学习心得。

本篇文章中,Jim Dovey 解释了如何使用 SwiftUICoreData 进行绑定操作。

The folks at Just Eat have experimentation and feature flagging at their heart and they’ve developed a component, named JustTweak, to make things easier on iOS.

John Sundell 找到了一些不同的方式来添加插件支持,这样可以使得系统变得更加灵活。

介绍了如何编写 Swift 代码的文档注释。

介绍了 Alexander Grebenyuk 如何从手动测试他的框架到通过单元测试来自动化测试每一次的变更。

继续阅读“Swift Weekly 中文 – Issue #176”

Swift 之 Property Wrappers 特性

Swift 5.1 新增了 Property Wrappers 特性。该特性可以通过使用 @ 符号以注解的形式来实现某些功能,并达到简化代码的效果。

示例

iOS 中的 UserDefaults 为例,一般用法如下。

  • 存储数据
UserDefaults.standard.set(value, forKey: key)
  • 获取数据
UserDefaults.standard.object(forKey: key)

改造

Property Wrappers 需满足两个基本要求:

  • 必须使用 @propertyWrapper 关键字来修饰。
  • 必须有一个命名为 wrappedValue 的属性。

接下来以 Property Wrappers 方式改造 UserDefaults ,代码如下:

@propertyWrapper
struct MyUserDefault<T> {

    let key: String
    let defaultValue: T

    var wrappedValue: T {
        get {
            return (UserDefaults.standard.object(forKey: key) as? T) ?? defaultValue
        }
        set {
            UserDefaults.standard.set(newValue, forKey: key)
        }
    }

}

使用

@MyUserDefault(key: "username", defaultValue: "")
static var username: String

加了 MyUserDefault 自定义注解后,对 username 的赋值操作相当于是执行了 UserDefaults.standard.set 方法,对 username 的读取操作相当于执行了 UserDefaults.standard.object 方法。

限制

Property Wrappers 也有一些使用上的限制。比如:

  • 无法在 protocol 中进行声明。
  • 通过 wrapper 包装的实例属性(An instance property with a wrapper)无法在 extension 中进行声明。
  • 无法在 enum 中进行声明。
  • class 中通过 wrapper 包装的属性无法被另外一个属性通过 override 覆盖掉。
  • 通过 wrapper 包装的实例属性(An instance property with a wrapper)不能用 lazy@NSCopying@NSManagedweakunowned 来修饰。

继续阅读“Swift 之 Property Wrappers 特性”

Swift Weekly 中文 – Issue #175

本篇为译文,原文可见:链接

本周的 issue ,我们主要讨论 Thread SanitizerKeyValuePairsSPMUtility

文章

学习在 Swift 中如何使用 Thread Sanitizer to catch Data Races。修复怪异的 crash,并且可以看到 Data Reace 示例。

介绍了一些集合类型之间的区别,比如:ArraySetDictionary

本篇文章中,Derik Ramirez 将介绍如何使用 Swift Package Manager 中的 SPMUtility 模块,通过 ArgumentParser 来解析你的 swift command-line tool 的参数。

Xcode 11 介绍了一种新方式来测试可选类型。Sarun 向你演示了新的 XCTUnwrap 方法。

本周,John Sundell 看了看一些核心语言特性,这些特性可以使我们设计真正轻量级的 Swift API,并且可以帮助我们更好的开发一个新功能或系统。

如果你正在查找关于 body 是计算型时,去尝试修改 view 状态的资料的话,这篇文章将会告诉你可以做哪些操作以及不应该做哪些操作。

在本周文章中,Majid 将向你展示 UIKitSwiftUI 开发之间的主要不同点,并指出使用 SwiftUI 的时候必须要改变的习惯。

Alexey Naumov 解释了如何处理 SwiftUI 项目中的导航来实现 deep links

工作机会

Ctrl Group builds digital products for patients, healthcare practitioners and researchers to gather evidence and provide better care. We’re looking for an iOS engineer to join our team in London, or work remotely as part of our distributed team.

讨论

介绍了今年十月在 Bologna, Italy 举办的 #Pragma Conference 中的所有演讲 。

两个免费视频探索了 Apple 新的 Combine 框架,包括它的核心组件,以及如何集成进你的代码。

库 & 代码

继续阅读“Swift Weekly 中文 – Issue #175”

Swift语法入门

本文简要介绍下 Swift 的基本语法。

  • 导入 package
import Foundation
  • 变量和常量
var str1 = "Hello, playground"
let str2 = "test"
  • 数组
var myArray = [
    "t1",
    "t2",
    "t3"
]
  • 集合
let ttt: Set = [0, 1, 2, 3, 6, 9, 90, 2]
  • 字典
var myDict = [
    "k1": "v1",
    "k2": "v2",
    "k3": "v3"
]
  • 方法
func test(_ value: Int) -> Bool {
    if value > 10 {
        return true
    }

    // 编译错误,Swift强制if语句加{}
    //    if value > 10
    //        return true

    return false
}
  • 多重返回值函数
func test1(_ value: Int) -> (errorCode: Int, errorDes: String) {
    if value > 10 {
        return (200, "Success")
    }

    return (404, "Not Found")
}

let r = test(100)
let r1 = test1(90)
print("result1 = (\(r1.0), \(r1.1))")
print("result1 = (\(r1.errorCode), \(r1.errorDes))")
  • 区间运算符
// 闭区间运算符(a...b)定义一个包含从a到b(包括a和b)的所有值的区间,b必须大于a。
for index in 1...5 {
    print("index = \(index)")
}
// 半开区间运算符
for index in 1..<5 {
    print("index = \(index)")
}
  • 闭包
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]

func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}

func closureTest(_ param1: Int, param2: (_ s1: String, _ s2: String) -> Bool) {
    print("param1 = \(param1), param2 = \(param2("d", "b"))")
}

closureTest(1, param2: backwards)
closureTest(2, param2: {
    (s1: String, s2: String) -> Bool in

    return s1 < s2
})
closureTest(3, param2: {
    (s1, s2) in // 类型可自动推倒出来

    return s1 < s2
})
closureTest(4, param2: {(s1, s2) in return s1 < s2})
closureTest(5, param2: {$0 > $1})
// 运算符函数
closureTest(6, param2: >)
  • 尾随闭包
func trailingClosuresTest(_ param: () -> ()) {
    print("trailingClosuresTest 1")

    param()

    print("trailingClosuresTest 2")
}

// 以下是不使用尾随闭包进行函数调用
trailingClosuresTest({
    () -> () in

    print("trailingClosuresTest 3")
})
trailingClosuresTest({
    print("trailingClosuresTest 4")
})

// 以下是使用尾随闭包进行函数调用
trailingClosuresTest() {
    print("trailingClosuresTest 5")
}

// 如果函数只需要闭包表达式一个参数,当使用尾随闭包时,甚至可以把()省略掉。
trailingClosuresTest {
    print("trailingClosuresTest 6")
}
  • 捕获值
func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 1
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}

let capture1 = makeIncrementor(forIncrement: 9)
print(capture1())
print(capture1())

print 输出如下:

10
19

  • 枚举
enum Barcode {
    case UPCA(Int, Int, Int)
    case QRCode(String)
}

func enumTest(value: Barcode) {
    switch(value) {
    case Barcode.UPCA(let numberSystem, let identifier, let check):
        print("UPCA numberSystem = \(numberSystem), identifier = \(identifier), check = \(check)")
    case Barcode.QRCode(let productCode):
        print("QRCode productCode = \(productCode)")
    }
}

enumTest(Barcode.QRCode("qs"))
enumTest(Barcode.UPCA(1, 2, 3))

enum Planet: Int {
    case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}

enum StrEnum: String {
    case T1 = "a"
    case T2 = "b"
}
  • 计算属性
struct Point {
    var x = 0.0
    var y = 0.0
}
struct Size {
    var width = 0.0
    var height = 0.0
}
struct Rect {
    var origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = origin.x + (size.width / 2.0)
            let centerY = origin.y + (size.height / 2.0)

            return Point(x: centerX, y: centerY)
        }

        set {
            origin.x = newValue.x - (size.width / 2.0)
            origin.y = newValue.y - (size.height / 2.0)
        }
    }
}

var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
print("square.center = \(square.center)")
square.center = Point(x: 15.0, y: 15.0)
print("square origin = \(square.origin)")

print 输出如下:

square.center = Point(x: 5.0, y: 5.0)
square origin = Point(x: 10.0, y: 10.0)

  • 属性观察器
class StepCounter {
    var totalSteps: Int = 0 {

        willSet(newTotalSteps) {
            print("1 totalSteps = \(totalSteps)")
        }
        didSet {

            if(totalSteps > oldValue) {
                print("2 totalSteps = \(totalSteps)")
            }
        }
    }
}

let stepCounter = StepCounter()
stepCounter.totalSteps = 100

print 输出如下:

1 totalSteps = 0
2 totalSteps = 100

  • 结构体
struct PointTest {
    var x = 0.0
    var y = 0.0
    mutating func moveBy(deltaX: Double, deltaY: Double) {
        x += deltaX
        y += deltaY
    }
}

使用 mutating 关键字是为了能在该方法中修改 struct 的变量。

  • 构造、析构、! 、?
class Player {
    init() {
        print("Player init")
    }

    deinit {
        print("Player deinit")
    }

    func test() {

    }
}

var player: Player? = Player()    // 调用构造方法

if(nil != player) {
    player!.test()
}
// 等价于上面这种情况
player?.test()

player = nil    // 调用析构方法
  • 继承 - 单继承,多实现
class A {

}

class B: A {

}

protocol IA {

}

protocol IB {

}

protocol IC {

}

class C: A, IA, IB, IC {

}
  • extension
extension Double {
    var km: Double { return self * 1_000.0 }
    var m : Double { return self }
    var cm: Double { return self / 100.0 }
    var mm: Double { return self / 1_000.0 }
    var ft: Double { return self / 3.28084 }
}

let t = 45.7.km
print(t)

print 输出如下:

45700.0