Swift Snippet
Swift Intro
Swift—一门智能型的编程语言
Common Problem
safe subsript
5 个让 Swift 更优雅的扩展——Pt.1
Swift 经常数组越界?教你一招,一劳永逸的解决数组越界的问题
extension Array {
subscript (safe index: Int) -> Element? {
guard index >= 0 && index < self.count else {
return nil
}
return self[index]
}
}
values[safe: 2] // "C"
values[safe: 3] // nil
UIKit Preview
UIKit Preview
import UIKit
class ViewController: UINavigationController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
var text = UILabel(frame: CGRect(x: 20, y: 20, width: 300, height: 300))
text.text = "UIKit Preview Testing"
self.view.addSubview(text)
}
}
#if DEBUG
import SwiftUI
struct ViewControllerRepresentable: UIViewControllerRepresentable {
typealias UIViewControllerType = ViewController
func makeUIViewController(context: Context) -> UIViewControllerType {
ViewController()
}
func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
}
}
struct ViewController_Previews: PreviewProvider {
static var previews: some View {
ViewControllerRepresentable()
}
}
Swift Guard
关于swift的guard使用一定要慎用
使用if判断保护的变量,使用返回仅限于if的括号范围内,这就导致使用这个变量的代码,只能写在if括号范围之内,十分不方便,也不够优美。对,不够优美,但这并不表示可以随便使用guard这个关键字,因为,因为….上代码。
在for,while,do-while等循环语句中使用guard,一旦变量不存在,就会直接跳出函数方法,导致剩下循环没办法进行,当程序使用数组的时候,容易造成数组越界,从而发生crash,这个问题很低级,但是我确实脑抽的发生了,造成很严重的后果,所以,写这边博客,一是记录一下自己的错误,二是提醒其他iOS开发者,尽量避免与我同样的错误。
Swift REPL
Arrow Keys Move cursor left/right/up/down
Control+F Move cursor right one character, same as right arrow
Control+B Move cursor left one character, same as left arrow
Control+N Move cursor to end of next line, same as down arrow
Control+P Move cursor to end of prior line, same as up arrow
Control+D Delete the character under the cursor
Option+Left Move cursor to start of prior word
Option+Right Move cursor to start of next word
Control+A Move cursor to start of current line
Control+E Move cursor to end of current line
Delete Delete the character to the left of the cursor
Esc < Move cursor to start of first line
Esc > Move cursor to end of last line
箭头键 将光标向左/右/上/下移动
Control+F 将光标向右移动一个字符
Control+B 将光标向左移动一个字符
Control+N 将光标移动到下一行
Control+P 将光标移动到上一行
Control+D 删除被光标选中的字符
Option+Left 将光标移动到前一个单词的开始处
Option+Right 将光标移动到下一个单词的开始处
Control+A 将光标移动到当前行的开始处
Control+E 将光标移动到当前行的结束处
Delete 删除光标左边的字符
Esc < 将光标移动到第一行的开始处
Esc > 将光标移动到最后一行的结束处
Enum
Swift Enum Usage
与OC不一样,Swift的枚举牛逼得多了,OC只能玩Int,他能玩:
整型(Integer)
浮点数(Float Point)
字符串(String)
布尔类型(Boolean)
Enumeration and Structures
Docs.swift.org - A Swift Tour
enum Rank: Int {
case ace = 1
case two, three, four, five, six, seven, eight, nine, ten
case jack, queen, king
func simpleDescription() -> String {
switch self {
case .ace:
return "ace"
case .jack:
return "jack"
case .queen:
return "queen"
case .king:
return "king"
default:
return String(self.rawValue)
}
}
}
let ace = Rank.ace
let aceRawValue = ace.rawValue
if let convertedRank = Rank(rawValue: 3) {
let threeDescription = convertedRank.simpleDescription()
}
enum Suit {
case spades, hearts, diamonds, clubs
func simpleDescription() -> String {
switch self {
case .spades:
return "spades"
case .hearts:
return "hearts"
case .diamonds:
return "diamonds"
case .clubs:
return "clubs"
}
}
}
let hearts = Suit.hearts
let heartsDescription = hearts.simpleDescription()
enum ServerResponse {
case result(String, String)
case failure(String)
}
let success = ServerResponse.result("6:00 am", "8:09 pm")
let failure = ServerResponse.failure("Out of cheese.")
switch success {
case let .result(sunrise, sunset):
print("Sunrise is at \(sunrise) and sunset is at \(sunset).")
case let .failure(message):
print("Failure... \(message)")
}
// Prints "Sunrise is at 6:00 am and sunset is at 8:09 pm."
Method and Property
enum Device {
case iPad, iPhone, AppleTV, AppleWatch
}
extension Device: CustomStringConvertible{
func introduced() -> String {
switch self {
case .iPad: return "iPad"
case .iPhone: return "iPhone"
case .AppleWatch: return "AppleWatch"
case .AppleTV: return "AppleTV"
}
}
var description: String {
switch self {
case .iPad: return "iPad"
case .iPhone: return "iPhone"
case .AppleWatch: return "AppleWatch"
case .AppleTV: return "AppleTV"
}
}
}
print(Device.AppleTV.description)
print(Device.iPhone.introduced())
Associated Value
enum Trade {
case Buy(stock:String,amount:Int)
case Sell(stock:String,amount:Int)
}
let trade = Trade.Buy(stock: "003100", amount: 100)
switch trade {
case .Buy(let stock,let amount):
print("stock:\(stock),amount:\(amount)")
case .Sell(let stock,let amount):
print("stock:\(stock),amount:\(amount)")
default:
()
}
case .Buy(let stock, let amount) 和 case let .Buy(stock, amount) 的区别在于使用位置限定符(Positional Qualifiers)的位置不同。
在 case .Buy(let stock, let amount) 中,位置限定符 let 出现在变量名称之前,因此它们只能应用于该 case 语句中的变量。这种方式将限定符用于单个变量,可以使代码更加易读和明确。
在 case let .Buy(stock, amount) 中,位置限定符 let 出现在枚举 case 名称之前,因此它们将应用于该枚举 case 中的所有变量。这种方式将限定符用于整个 case,可以使代码更加简洁和紧凑。
在实际使用中,两种方式都是有效的,并且它们的区别并不影响代码的行为。选择使用哪种方式主要取决于您的个人偏好和代码风格。
Protocol
CustomstringConvertible
enum Trade :CustomStringConvertible{
case Buy(stock:String,amount:Int)
case Sell(stock:String,amount:Int)
var description: String {
switch self {
case .Buy(_, _):
return "Buy"
case .Sell(_, _):
return "Sell"
}
}
}
print(Trade.Buy(stock: "003100", amount: 100).description)
// Buy
print(Trade.Buy(stock: "003100", amount: 100))
// Buy
How to get the name of enumeration value in Swift?
Note that you can customise what is printed in each of these scenarios:
extension City: CustomStringConvertible {
var description: String {
return "City \(rawValue)"
}
}
print(city)
// prints "City 1"
extension City: CustomDebugStringConvertible {
var debugDescription: String {
return "City (rawValue: \(rawValue))"
}
}
debugPrint(city)
// prints "City (rawValue: 1)"
Extesnsion
枚举也可以进行扩展。最明显的用例就是将枚举的case和method分离,这样阅读你的代码能够简单快速地消化掉enum内容,紧接着转移到方法定义:
Enum can also be extended. The most obvious use case is to separate the enum’s case and method, so that you can quickly digest the contents of the enum, and then move on to the method definition:
enum Assets {
case ballRed, bouncer
}
extension Assets {
var stringValue: String {
switch self {
case .ballRed:
return "ballRed"
case .bouncer:
return "bouncer"
}
}
}
Generics
enum Rubbish<T> {
case price(T)
func getPrice() -> T {
switch self {
case .price(let value):
return value
}
}
}
print(Rubbish<Int>.price(100).getPrice())
// 100
CaseIterable
// https://stackoverflow.com/questions/26261011/how-to-choose-a-random-enumeration-value
enum Ball: String, CustomStringConvertible, CaseIterable {
case ballRed, ballBlue, ballCyan, ballGreen, ballGrey, ballPurple, ballYellow
var description: String {
return self.rawValue
}
static func randomBall() -> Ball {
return Self.allCases.randomElement() ?? ballRed
}
}
enum Weekday: CaseIterable {
case sunday, monday, tuesday, wednesday, thursday, friday, saturday
static func random<G: RandomNumberGenerator>(using generator: inout G) -> Weekday {
return Weekday.allCases.randomElement(using: &generator)!
}
static func random() -> Weekday {
var g = SystemRandomNumberGenerator()
return Weekday.random(using: &g)
}
}
Stride
例1: 從 0, 100, 200,一直加到 900。(每次加 100,不包含 1000)
var sum = 0
for i in stride(from: 0, to: 1000, by: 100) {
sum = sum + i
}
例2: 從 0, 100, 200,一直加到 1000。(每次加 100,包含 1000)
var sum = 0
for i in stride(from: 0, through: 1000, by: 100) {
sum = sum + i
}
Lazy
See more in objc-snippet
functionBuilder
Trees in Swift
Codable & NSCoding
Swift Codable 和 NSCoding协议,以及归档,JSON编码
前面文章中 UserDefaults 的基本用法 中对UserDefaults 进行了简单的介绍,它可以将一些简单的数据类型存储在本地,需要使用的时候再去读取。
如果对于复杂对象的存储则需要将其进行序列化,将对象转化为 NSData(Swift Data)类型之后再进行操作,比如,将其存在本地的某个文件(eg.people.plist, people.txt等)中。
有2种序列化的方式:
- NSCoding: 老的Cocoa方式,OC的方式
- Codable: 新的swift方式
Value type, Reference type
Swift值类型&引用类型
Swift 中,值类型的赋值为深拷贝(Deep Copy),值语义(Value Semantics)即新对象和源对象是独立的,当改变新对象的属性,源对象不会受到影响,反之同理。
Difference Between Struct and Class
Understanding Swift
- Classes have deinitializers, which are methods that are called when an instance of the class is destroyed, but structs do not.
- 常量类中的变量属性可以自由修改,但常量结构中的变量属性不能。
- Variable properties in constant classes can be modified freely, but variable properties in constant structs cannot.
The job of deinitializers is to tell us when a class instance was destroyed. For structs this is fairly simple: the struct is destroyed when whatever owns it no longer exists. So, if we create a struct inside a method and the methods ends, the struct is destroyed.
So, the simple reason for why structs don’t have deinitializers is because they don’t need them: each struct has its own copy of its data, so nothing special needs to happen when it is destroyed.
You can put your deinitializer anywhere you want in your code, but I love this quote from Anne Cahalan: “Code should read like sentences, which makes me think my classes should read like chapters. So the deinitializer goes at the end, it’s the ~fin~ of the class!”
override
Swift中final、extension、override、@objc等关键字的使用
override 关键字在 Swift 中用来明确表示子类想要覆盖(或重写)父类的方法、属性或下标脚本。这个关键字提供了一种安全机制,如果你尝试覆盖的成员在父类中不存在,编译器将给出错误提示,避免了可能的误操作。
在 Objective-C 中,没有对应的 override 关键字。当你在子类中使用和父类相同的方法名定义方法时,Objective-C 会默认进行覆盖。这也意味着,在 Objective-C 中,如果你错误地拼写了想要覆盖的方法名,编译器将不会发出任何警告,这可能会导致意料之外的运行时行为。
所以,Swift 中的 override 关键字提供了额外的类型检查和安全性,可以在编译时就捕获可能的错误,这是它与 Objective-C 行为的一个主要区别。
class var, static var
class var 只能在类(class)中定义,不能在枚举(enum)和结构体(struct)中定义。另外,class var 只能定义计算属性,不能定义存储属性,但是它们可以被子类重写。
所以简单总结就是,如果你希望定义一个类型级别的存储属性,只能用 static var;如果你希望定义一个可被子类重写的类型级别的计算属性,可以用 class var。
class MyClass {
class var myVar: Int {
return 42
}
}
class MySubClass: MyClass {
override class var myVar: Int {
return 84
}
}
print(MyClass.myVar) // 输出 42
print(MySubClass.myVar) // 输出 84
@property(class, nonatomic, readonly) BOOL isSupported;
C, C++ Integration
在 Swift 中去调用 C/C++ 代码
Cpp integration
- Cpp File
- Objective-C Wapper, .h .mm
- Bridging-Header
// .hpp file
#ifndef CppTest_hpp
#define CppTest_hpp
#include <stdio.h>
#include <iostream>
class CppTest {
public:
void test();
};
#endif /* CppTest_hpp */
// .cpp file
#include "CppTest.hpp"
void CppTest::test() {
printf("swift can call from Cpp");
}
// .h file
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface ObjCpp : NSObject
- (void)run;
@end
NS_ASSUME_NONNULL_END
// .mm file
#import "ObjCpp.h"
#import "CppTest.hpp"
@implementation ObjCpp
- (void)run {
auto sp = std::make_shared<CppTest>();
sp->test();
}
@end
dylib for Xcode project
Linking Homebrew Libraries in Xcode
cd opt/homebrew/include
# Header Search Paths