Mac 安装 appium 并在 iOS 真机运行备忘

做一个 appium 在 Mac M1 下的安装备忘,系统 Ventura 13.0。

一、目前系统环境

image-20221129午前95127441

官方网站:https://appium.io/

官方的快速安装命令:

> brew install node      # get node.js
> npm install -g appium  # get appium
> npm install wd         # get appium client
> appium &               # start appium
> node your-appium-test.js

二、安装通用软件

1. nodejs

可以在官网下载,也可以用homebrew下载。在官网下载 npm install -g 时候会报权限问题,只有用 sudo 才行。为了避免这种问题,我还是使用 homebrew下载了,避免以后可能出现权限问题还需要定位。

官网:https://nodejs.org/zh-cn/download/

•	Node.js v18.12.1 to /usr/local/bin/node
•	npm v8.19.2 to /usr/local/bin/npm ![image-20221129午後55019531](https://cdn.kelu.org/blog/2023/01/image-20221129午後55019531.jpg)

出现权限问题的错误:

image-20221129午後55353546

改用 homebrew了:

brew install node
which node
which npm
node -v
npm -v

image-20221129午後55803508

image-20221129午後55859469

2. appium

npm install -g appium

image-20221129午後60144764

3. wd

npm install wd

image-20221129午後60755547

4. appium-doctor

Appium doctor是一个小型软件,它检查Appium成功运行的所有(几乎所有)前提条件。

npm install -g appium-doctor

image-20221129午後61037476

5. java

brew install java

再把这部分内容塞到 /etc/profile 文件去。

JAVA_HOME=/opt/homebrew/opt/openjdk

export JAVA_HOME

然后source一下:

source /etc/profile

6. Carthage

Carthage是一个依赖管理器,WebDriverAgent 需要它

brew install carthage

image-20221129午後61144313

7. appium-desktopappium-inspector

在GitHub上下载安装即可。

接下来我先尝试在iOS上运行出来,warning的内容也在iOS部分解决了。

三、iOS

一定要先安装好 xcode,可以参考我这篇:《iOS 开发环境配置备忘》

检查 iOS依赖有没有安装好

appium-doctor --ios

image-20230103午後43559003

1. libimobiledevice & ideviceinstaller

下文运行获得一些数据,所以装这两个工具方便获取。

brew install libimobiledevice 
brew install ideviceinstaller 

idevice_id
instruments -s devices # 列出设备包括模拟器、真机及 mac 电脑本身 
ideviceinfo # 可以在返回的数据中找到 udid 

ideviceinstaller --list-apps # 列出app 
ideviceinstaller -l  # 列出app 
idevicenotificationproxy -h

2. 自己编译webdriveragent并运行在真机上

首先手机要在 设置> 隐私与安全性 下找到 “开发者模式”开关 开启开发者模式。

我看到有的教程直接使用 appium 自带的 webdriveragent代码,因为我的xcode版本比较新,使用这些老代码 编译 都不过,所以还是直接使用最新的源码代替了。

https://github.com/appium/WebDriverAgent/releases 下载源码

image-20230106午前94910512

双击 WebDriverAgent.xcodeproj 后打开:

image-20230105午後73736966

然后修改WebDriverAgentRunnerWebDriverAgentLib证书为自己的证书(在真机上运行需要),新建的话只要登陆我们的apple id即可,不需要付费。

同时修改 Bundle identifier,把 Facebook 的内容换成自己的。

image-20230106午前100538822

选择 Product->Scheme->WebDriverAgentRunner

image-20230106午前95313955

选择 Product->Destination, 运行的设备为本机设备,例如我的设备名叫 とある血衫のiPhone

image-20230106午前101500095

接下来点击三角形▶️先进行编译。

image-20230106午前101533882

Ps:如果某个开发者账号签名的所有 App 都被删除的话下次再安装又要重新信任才能运行,你可能会遇到这个错误:

image-20230105午後31248095

在手机上也能看到这样的提示:

image-20230105午後31534833

在设备上 通用->VPN与设备管理->开发者APP 里选择信任证书即可。

image-20230105午後31547501

编译OK后运行test:

image-20230105午後73847466

image-20230105午後73752971

本机访问 127.0.0.1:8100/status

image-20230105午後73800739

遇到了诸多问题,记录几个:

  1. ld: cannot link directly with dylib/framework, your binary is not an allowed client arm64

    根据一个答案的提示,换了最新的webdriveragent源码:https://developer.apple.com/forums/thread/712039

3. opencv4nodejs(安装失败,搁置了)

着手解决 opencv4nodejs 的 warning。也是尽力而为了,最后只把ffmpeg的warning消掉了(安装opencv自动装好的)

https://appium.io/docs/en/writing-running-appium/image-comparison/

我遇到了好些错误,比如:

npm ERR! info install OPENCV_LIB_DIR is not set, looking for default lib dir npm ERR! info install using lib dir: /usr/local/lib npm ERR! /opt/homebrew/lib/node_modules/opencv4nodejs/install/install.js:45 npm ERR! throw new Error(‘no OpenCV libraries found in lib dir: ‘ + libDir)

查了一些问题:https://github.com/justadudewhohacks/opencv4nodejs/issues/733#issuecomment-988898236

brew install cmake
brew install opencv
npm i -g opencv4nodejs

image-20230105午後34229535

opencv 要装的依赖还挺多的,等的比较久:

==> Fetching dependencies for opencv: eigen, gflags, glog, metis, gmp, isl, mpfr, libmpc, xz, gcc, openblas, suite-sparse, tbb, ceres-solver, highway, imath, libpng, openexr, webp, jpeg-xl, libvmaf, aom, dav1d, frei0r, bdw-gc, m4, libtool, libunistring, guile, libidn2, libtasn1, nettle, p11-kit, unbound, gnutls, lame, fribidi, pcre2, glib, libxau, libxdmcp, libx11, libxrender, pixman, harfbuzz, libunibreak, libass, libbluray, cjson, cmocka, mbedtls, librist, libsoxr, libvidstab, libogg, libvorbis, libvpx, opencore-amr, openjpeg, opus, rav1e, flac, mpg123, libsndfile, libsamplerate, rubberband, sdl2, snappy, speex, srt, leptonica, libb2, libarchive, pango, tesseract, theora, x264, x265, xvid, libsodium, zeromq, zimg, ffmpeg, numpy, protobuf, sqlite, python@3.11, boost, double-conversion, gl2ps, glew, libaec, hdf5, jsoncpp, netcdf, pugixml, qt@5, pyqt@5, utf8cpp and vtk

四、Android

https://developer.android.com/studio#downloads

点击进入默认会要求下载模拟器

image-20221129午後53714380

不小心点掉了的话,在 Preference 里搜索 SDK ,点击 edit 可以继续下载:

image-20221129午後54258141

image-20221129午後54151673

image-20221129午後54240508

增加 android 版本:

image-20221129午後54437101

完成:

image-20221129午後54702246

再把这部分内容塞到 /etc/profile 文件去。

ANDROID_HOME=/Users/kelu/Library/Android/sdk

export ANDROID_HOME

然后source一下:

source /etc/profile

一些安装相关的问题:

五、试运行

1. 命令行

appium &

image-20221130午前10845571

image-20221130午前11126578

image-20221130午前10904336

image-20221130午前11012495

能跑了之后先把命令行kill掉,我们改用 gui 运行。

2. Appium Server GUI

image-20230106午前93746857

其他不用改,选中 Allow CORS 即可,然后 startServer

image-20230106午前93843695

3. Appium Inspector

安卓的试运行和iOS/android的详细使用方法再开新的文章讲讲,这里简单列一下iOS试运行的配置和结果。

配置参考:Appium Desired Capabilities

Remote Path 要填 /wd/hub:

image-20230927180702166

可以直接修改右侧的json,填入一下内容:

{
  "platformName": "iOS",
  "platformVersion": "13.2",
  "deviceName": "填写手机的deviceName",
  "automationName": "XCUITest",
  "udid": "填手机的udid",
  "bundleId": "填应用的bundleId",
  "xcodeOrgId": "填开发者id",
  "xcodeSigningId": "iPhone Developer",
  "useNewWDA": false,
  "noReset": true,
  "newCommandTimeo": "60"
}

具体的参数如下获取:

platformVersion 如上文在本机浏览器访问 127.0.0.1:8100/status 获得。

idevice_id -l 							# 获取udid
ideviceinfo -k DeviceName 	# 获取deviceName
ideviceinstaller -l -o list_user # 获取bundleId

获取xcodeOrgId,在这个界面下点击那个 Other就能获取了:

image-20230105午後84626249

I cant’t connect appium-server desktop and appium inspector with my smartphone while i can see my device is well connected in my terminal on windows

最后的 json:

{
  "platformName": "iOS",
  "platformVersion": "16.1",
  "deviceName": "とある血衫のiPhone",
  "automationName": "XCUITest",
  "udid": "xxx",
  "bundleId": "com.reederapp.5.iOS",
  "xcodeOrgId": "xxx",
  "xcodeSigningId": "iPhone Developer",
  "useNewWDA": false,
  "noReset": true,
  "newCommandTimeo": "60"
}

Server GUI 显示:

image-20230106午前93531133

手机显示:

image-20230106午前93614838

Inspector显示:

image-20230106午前93639424

六、其它

为了防止你的手机自动锁定,建议可以选择取消自动锁定,在iOS16下层级为:

设置->显示与亮度->自动锁定->永不

python 代码驱动:

pip install appium-python-client

vscode 设置虚拟环境

几个代码备忘:

selenium.common.exceptions.WebDriverException: Message: An unknown server-side error occurred while processing the command. Original error: The attribute 'accessibility_id' is unknown. Valid attribute names are: (
    UID,
    accessibilityContainer,
    accessible,
    enabled,
    focused,
    frame,
    index,
    label,
    name,
    rect,
    selected,
    type,
    value,
    visible,
    wdAccessibilityContainer,
    wdAccessible,
    wdEnabled,
    wdFocused,
    wdFrame,
    wdIndex,
    wdLabel,
    wdName,
    wdRect,
    wdSelected,
    wdType,
    wdUID,
    wdValue,
    wdVisible
)
Stacktrace:
UnknownError: An unknown server-side error occurred while processing the command. Original error: The attribute 'accessibility_id' is unknown. Valid attribute names are: (
    UID,
    accessibilityContainer,
    accessible,
    enabled,
    focused,
    frame,
    index,
    label,
    name,
    rect,
    selected,
    type,
    value,
    visible,
    wdAccessibilityContainer,
    wdAccessible,
    wdEnabled,
    wdFocused,
    wdFrame,
    wdIndex,
    wdLabel,
    wdName,
    wdRect,
    wdSelected,
    wdType,
    wdUID,
    wdValue,
    wdVisible
)

image-20230928150426656

image-20230928150329908

七、参考资料


swift 开发备忘

做个简单备忘。以后还得继续练习补充。

官方reference:https://developer.apple.com/cn/documentation/swift/

一、playground

image-20221208午後40856523

image-20221208午後40905540

二、速查表

https://swiftgg.gitbook.io/swift/huan-ying-shi-yong-swift

image-20221208午後45745558

let bmi = wight / (height * height)
bmiValue = String(format: "%.1f", bmi)

三、数据类型

  • 基础数据类型

    • Int 表示整型值;

    • UInt 无符号类型;

    • DoubleFloat 表示浮点型值;

    • Bool 是布尔型值;

    • String 是文本型数据。

      var stringA = "Hello, World!"
      let stringB = String("kelu.org")
      var stringC = stringA + stringB
          
      // 字符串长度使用 String.count 属性来计算
      stringC.count
      

      相关函数:

      • isEmpty
      • hasPrefix(prefix: String)
      • hasSuffix(suffix: String)
      • Int(String)
      • String.count
      • utf8
      • utf16
      • unicodeScalars
    • Character 字符。

    • nil 空。

    • 可选类型 optional

      • 如果一个可选类型的实例包含一个值,可以用后缀操作符 !来访问这个值(强制解析),但去获取值为nil的可选变量会有运行时错误。

        var optionalInteger: Int?
        var optionalInteger: Optional<Int>
              
        var username:<data type> = <optional initial value>
        var username: String? = nil
        
      • 也可以在声明可选变量时使用感叹号(!)替换问号(?)。这样可选变量在使用时就不需要再加一个感叹号(!)来获取值,它会自动解析。

      image-20221222午後40013406

      方法4相当于三元操作符?:。我比较喜欢这种方式。

    • 类型别名

      对当前的类型定义了另一个名字,类型别名通过使用 typealias 关键字来定义。语法格式如下:

      typealias newname = type
      
  • 类型安全

    在编译代码时进行类型检查(type checks),并把不匹配的类型标记为错误。

  • 类型推断

    没有显式指定类型,Swift 会使用类型推断(type inference)来选择合适的类型。

  • 变量

    var variableName = <initial value>
    
  • 常量

    let constantName = <initial value>
    let constB:Float = 3.14159     // 3.14159 为字面量
      
    let constantName:<data type> = <optional initial value>
    

    字面量是指由字母,数字等构成的字符串或者数值,它只能作为右值出现。

  • 数组Array

    var someInts = [Int](repeating: 0, count: 3)
    var someInts:[Int] = [10, 20, 30]
    
  • 字典Dictionary

    var someDict:[Int:String] = [1:"One", 2:"Two", 3:"Three"]
      
    var removedValue = someDict.removeValue(forKey: 2)
      
    for (key, value) in someDict {
       print("字典 key \(key) -  字典 value \(value)")
    }
      
    for (key, value) in someDict.enumerated() {
        print("字典 key \(key) -  字典 (key, value) 对 \(value)")
    }
      
    // 转数组
    let dictKeys = [Int](someDict.keys)
    let dictValues = [String](someDict.values)
    
  • 错误处理

    func makeASandwich() throws {
        // ...
    }
      
    do {
        try makeASandwich()
        eatASandwich()
    } catch SandwichError.outOfCleanDishes {
        washDishes()
    } catch SandwichError.missingIngredients(let ingredients) {
        buyGroceries(ingredients)
    }
    
  • 枚举

    // 定义枚举
    enum DaysofaWeek {
        case Sunday
        case Monday
        case TUESDAY
        case WEDNESDAY
        case THURSDAY
        case FRIDAY
        case Saturday
    }
    

四、闭包

Swift中的闭包有很多优化的地方:

  1. 根据上下文推断参数和返回值类型
  2. 从单行表达式闭包中隐式返回(也就是闭包体只有一行代码,可以省略return)
  3. 可以使用简化参数名,如$0, $1(从0开始,表示第i个参数…)
  4. 提供了尾随闭包语法(Trailing closure syntax)
{(parameters) -> return type in
   statements
}

{(Int, Int) -> Bool in
   Statement1
   Statement 2
    ---
   Statement n
}
  • 闭包表达式

    排序完成后,sorted(by:) 方法会返回一个与原数组大小相同,包含同类型元素且元素已正确排序的新数组。原数组不会被 sorted(by:) 方法修改。

    import Cocoa
      
    let names = ["AT", "AE", "D", "S", "BE"]
      
    // 使用普通函数(或内嵌函数)提供排序功能,闭包函数类型需为(String, String) -> Bool。
    func backwards(s1: String, s2: String) -> Bool {
        return s1 > s2
    }
    var reversed = names.sorted(by: backwards)
      
    print(reversed)
      
    // ["S", "D", "BE", "AT", "AE"]
    

    参数名称缩写,$0,$1,$2

    import Cocoa
      
    let names = ["AT", "AE", "D", "S", "BE"]
      
    var reversed = names.sorted( by: { $0 > $1 } )
    print(reversed)
    

    运算符函数

    import Cocoa
      
    let names = ["AT", "AE", "D", "S", "BE"]
      
    var reversed = names.sorted(by: >)
    print(reversed)
    
    1. 尾随闭包是一个书写在函数括号之后的闭包表达式,函数支持将其作为最后一个参数调用。

    2. func someFunctionThatTakesAClosure(closure: () -> Void) {
          // 函数体部分
      }
           
      // 以下是不使用尾随闭包进行函数调用
      someFunctionThatTakesAClosure({
          // 闭包主体部分
      })
           
      // 以下是使用尾随闭包进行函数调用
      someFunctionThatTakesAClosure() {
        // 闭包主体部分
      }
      
    3. image-20221221午後55652630

    4. import Cocoa
           
      let names = ["AT", "AE", "D", "S", "BE"]
           
      //尾随闭包
      var reversed = names.sorted() { $0 > $1 }
      print(reversed)
           
      // ["S", "D", "BE", "AT", "AE"]
      
    5. sort() 后的 { $0 > $1} 为尾随闭包。

    6. 注意: 如果函数只需要闭包表达式一个参数,使用尾随闭包时,可以把()省略掉。

      reversed = names.sorted { $0 > $1 }
      

五、数据结构

struct

struct MarksStruct {
   var mark: Int

   init(mark: Int) {
      self.mark = mark
   }
}

var aStruct = MarksStruct(mark: 98)
var bStruct = aStruct // aStruct 和 bStruct 是使用相同值的结构体!
bStruct.mark = 97
print(aStruct.mark) // 98
print(bStruct.mark) // 97

image-20221221午後55652630

class

class SomeClass: SomeSuperclass {
    // 类的定义
}

Class 必须提供init函数,struct 不需要提供。

class 是引用传递,所以直接let class1 = class2,对class1进行操作,class2取出来也会变化,因为是同一个class。

struct 是值传递,所以 let struct1 = struct2,得到的是两个不同的struct。

Struct 是immutable的,方法要修改struct的值必须声明mutating

从父类强制指定子 class: as!

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
          if segue.identifier == "testSeg" {
            let destinationVC = segue.destination as! ResultViewController
            destinationVC.bmiValue = calculatorBrain.getBMIValue()
            destinationVC.advice = calculatorBrain.getAdvice()
            destinationVC.color = calculatorBrain.getColor()
        }
    }

Closures 闭包

https://docs.swift.org/swift-book/LanguageGuide/Closures.html

如果函数最后一个参数是闭包,可以直接扔在最后,叫尾随闭包。

六、delegate 和 protocol

protocol 就是一种简易灵活的继承方式。

delegate 设计模式。(有点像用类继承的方式封装的观察者模式/事件模式。普通的事件模式你需要监听事件和传输内容,在delegate中你只需在controller中写明某个组件的delegate为self,即可在controller中直接使用对应的事件方法。)

七、extension

扩展就是向一个已有的类、结构体或枚举类型添加新功能。扩展可以对一个类型添加新的功能,但是不能重写已有的功能:

  • 添加计算型属性和计算型静态属性
  • 定义实例方法和类型方法
  • 提供新的构造器
  • 定义下标
  • 定义和使用新的嵌套类型
  • 使一个已有类型符合某个协议
extension SomeType: SomeProtocol, AnotherProctocol {
    // 协议实现写到这里
}

八、第三方库 cocoapods和依赖管理

https://cocoapods.org/

sudo gem install cocoapods

image-20221228午後64838262

拉数据。

pod setup --verbose

image-20221228午後65525857

pod init
pod install

image-20221228午後70141885

九、SwiftUI

2019 WWDC发布。

很多灵感来自网页设计。

元素堆叠的感觉,让我想起了前端vue的自定义标签。

看来多学一点框架也不是坏事,可以互通各自的设计理念。搞的我有点想看一下另一个前端框架了。

参考资料


iOS 开发环境配置备忘

按照一些教学视频做的简单备忘:

  • 斯坦福大学 CS193p
  • Udemy付费课程 iOS13 & Swift - The Complete iOS App Development Bootcamp

这篇文章主要记录一些环境配置的内容,涉及代码的地方以 objectc为主,interface以storyboard为主。

后续分成几篇分别记录 objectc , swift , swiftUI 的 reference。

个人认为,对于有代码能力的人来说,这些教程内容都太过冗长,实际操作中我一般快速扫过视频,让脑子有个印象即可,需要学习特定内容再使用 chatgpt + 返回教程回顾,可以省下大量时间,并且在学习的过程中给自己更强的信心。

iOS和其他后端语言相比,乍一看让人生畏的地方,可能就是可视化的那些内容。至少我一开始是这样看的。稍微掌握之后就可以直接实战了。我甚至想到了photoshop的一些操作。

零、环境准备

  1. 一台MacBook Air/Pro 或者 Mac mini/studio 或者 iMac

  2. 一台iPhone,iPad也可以。

  3. macOS,一个较新的版本。

  4. Xcode:,一个较新的版本。

    https://developer.apple.com/support/xcode,这里列了不同版本 xcode 的OS、sdk支持情况。

    Xcode也可以直接在 app store 中下载,但就不能选择特定版本了。

    另外不要下载Beta版本,以免有奇怪的bug。

    image1.20231011193518209

  5. 一个apple 开发者账号。

    只开发不发布的话,是免费的,不需要付费。 https://developer.apple.com

  6. 一个良好、稳定的网络环境。

一些文档

  1. Xcode的Help链接里有文档:

    image-20221222午後10106457

    image-20230904171337241

  2. 还有一个app,名字叫 Developer:

    image-20240110下午32607879

  3. https://developer.apple.com/documentation

  4. GitHub Awesome :https://github.com/dkhamsing/open-source-ios-apps

    image-20240123下午33138859

  5. udemy 教材相关链接:https://www.appbrewery.co/p/ios-course-resources/,我也保存了一份pdf备份

  6. 令人发指的函数式编程和相关语法糖

一、Project

image-20221208午前101417830

image-20221208午前112203800

Project interface分为 Storyboard 和 SwiftUI。这两者的区别为:

  • storyboardsXIB(Xml Interface Builder) 包含相当多的 XML 语句,不容易阅读,也不容易编辑。
  • storyboards 随着时间的推移越来越大。
  • XIBSwift 是非常独立的东西,XIBSwift 代码不了解,而 Swift 代码对 XIB 也不了解。
  • SwiftUI 是一个只适用于 Swift 的框架。
  • SwiftUI 代码比 storyboardsXML 更容易阅读和管理。

image-20240123下午120701255

最简单的创建:

  • storyboard
  • object c
  • 不包括Tests

1. 使用storyboard, objectc生成

image-20230904165451693

image-20230904170522997

  • Main.storyboard文件用来设计、拖拽。

2. 使用 storyboard, swift 生成

image-20240123下午21400900

image-20240123下午21641949

3. 使用 swiftUI, swift 生成

image-20240123下午43520274

image-20240123下午43350025

二、真机运行

如果是在真机上运行:

  1. 需要在xcode上配置证书
  2. 需要在手机上开启开发者模式:设置->隐私与安全性->开发者模式
  3. 需要在手机上信任开发者证书

以上都是免费的,不需要注册开发者账号。如果使用模拟器,则不用这么麻烦。

另外如果是从互联网上下载的代码,建议不要随意在真机上运行,有安全风险。

image-20240123下午25621027

image-20221207午前113451384

image-20221207午前114103301

image-20221207午前115613715

设置-> 通用->VPN与设备管理:

image-20221207午前115553351

如果你的账号是团队的成员,我这边遇到好几个问题顺便记录一下(2023.08.20):

  1. 在账号中不显示团队信息,

    大概率是在web配置页面,管理员没有配置你为开发者,并且开启了拉取配置信息的权限。

    4e9c20fbba9155bb

  2. Failed to register bundle identifier The app identifier “xxx” cannot be registered to your development team because it is not available. Change your bundle identifier to a unique string to try again.

    b6be7ea68b750d7f

    这里参考 Xcode Error: “The app ID cannot be registered to your development team.”

     cd ~/Library/MobileDevice/Provisioning\ Profiles
    rm *
    Xcode > Preferences... > Accounts > click your Account and Team name > click Download Manual Profiles
    

running,点击这个三角形就在真机运行。

image-20240123下午25829431

在Windows-> Devices and Simulators 可以看到设备列表:

image-20240123下午30748376

2024-01-18下午1.26.50

Preview 可以在这里设置:

image-20240123下午31727488

三、概念理解/设计模式

1. 系统库

IMG_7213

IMG_7212

IMG_7211

IMG_7210

框架:

  • Foundation
  • Core Data
  • Map Kit
  • UIKit
  • Core Motion

2. MVC/MVVM

一般后端常讲 mvc,在iOS等前端语言中一般说mvvm,个人感觉是一样的概念。MVC:

IMG_7214

image-20240123下午73930919

3. Protocol/Delegate

(视频在model里)声明protocol:

image-20240126下午10142266

controller 里实现:

image-20240126下午30629383

4. extension

image-20240126下午33926716

5. function

image-20240126下午31012325

image-20240126上午111840138

6. 闭包

关键字是 in

image-20240126上午112803299

image-20240126上午112938937

转成闭包:

image-20240126上午113012787

7. 闭包细节

将函数作为返回值,如下。

image-20240126上午112111008

最后精简为:

calculator(n1:2,n2:3){$0 * $1}

这种场景下,就可以用闭包代替。一步一步代替:

  1. 把multiply改成闭包:

    image-20240126上午113229823

  2. 挪到调用的地方:

    image-20240126上午113319006

  3. 让编译器自个判断闭包的返回值类型

    image-20240126上午113705983

  4. return可以省略:

    image-20240126上午113725863

  5. 把3行换成1行显示

    image-20240126上午113801798

  6. 用匿名参数$0 $1

    image-20240126上午113903014

  7. swift的规则,如果函数的最后一个参数是闭包,可以直接把这个参数省略掉,扔到括号外。(逆天!!!,可读性逆天!!!)

    image-20240126上午115235333

另一个逆天的缩写,这次用map函数:

image-20240126上午115640191

image-20240126上午115701813

一步一步来:

image-20240126上午115732206

image-20240126上午115802678

image-20240126上午115834940

最终结果:

image-20240126上午115904221

image-20240126上午115942776

8. 线程

UI的内容更新要在主线程里:DispatchQueue.main

image-20240126下午32815547

9.其他库

  • CoreLocation

以下开始是 xcode 的一些简单功能

四、添加资源、素材

右上角的+用得最多了,添加各种组件。

image-20221208午前104903090

1. 增加画布

  • 增加一个 viewcontroller.swift文件,自个copy 模版ViewController的内容,主要是import UIKit
  • 再在storyboard上增加一个view controller。
  • 右侧栏上设置对应的class。

2024-01-24上午9.23.01

image-20240124上午100701785

2. 导出各种格式尺寸的第三方工具

asset 目录里有很多不同尺寸的图(注意设置All Size):

image-20240123下午24034428

image-20221208午前105037207

3. 图标 SF Symbols

image-20221222午後54231470

4. 暗色模式

Image Set -> Appearance -> 设置明色与暗色

image-20240126上午100116656

5. 矢量图

Image Set-> Resizing -> 保留矢量信息

image-20221226午後85627331

五、页面、页面元素和代码关联

1. 代码关联

image-20221208午前113552569

选择 Assistant 可以将代码和界面放一起:

image-20221208午前113635472

声明组件和Action可以用拖拽的方式,ctrl+拖动

image-20221208午後20539567

image-20221208午後20653439

放在 class的前部分默认就是 outlet,放在后部分默认就是Action。

image-20221208午後20801275

关联关系可以在图形上右键查看:

image-20221208午後40128397

image-20221208午後34618912

2. 自适应布局

选集,061 Setting Constraints and working with the Safe Area

2.1 constraints

指定item和边缘的距离。

未标题-1

2.2 alignment/pinning

image-20221213午後42724791

iOS里的stack view组件可以类比为html中的div组件。

view controller里的文件视图类似于photoshop里的图层反过来。

2.3 stack view

上图右下角的最右边那个收纳的图标。

4. UITextField

image-20240126上午105045443

六、view、controller和绑定

1. 纯代码

这样在图形化界面里我们就根本看不见这个view了,没办法用拖拽的方式给它添加东西。

  • 新建一个 swift 文件:

image-20221222午後13259831

  • 命名为ViewController secondVC,改一下 import 为 UIKit`

  • 定义一些基本的变量保存数据

  • override func viewDidLoad

  • 定义一个组件,例如 UILabel

  • 在上一个页面的ViewController里给 secondVC 的变量赋值

  • 在上一个页面的ViewControllerself.present(secondVC, animated:true, completion: nil)

2. 拖拽式生成view并绑定controller

在图形界面新建一个viewController

image-20221222午後12943449

image-20221222午後13017785

新建一个cocoa touch class:

image-20221222午後12312137

image-20221222午後12409400

然后在 storyboard 里点一下还没有绑定 vc 的图形:

image-20221222午後12626236

image-20221222午後12645768

选择之后就可以看到它绑定好了。

image-20221222午後12711338

3. 生成view间关联:segues

image-20221222午後30157878

点击中间那个关联的方块,设定segues的identifier名字:

image-20221222午後30336922

然后在代码中触发这个切换:

self.performSegue(withIdentifier: "testSeg", sender: self)

同时,在前一个 controller 里我们可以做这个view 在切换前的准备工作。

4. 设置顶部出现返回栏:navigation controller

先点击第一个viewcontroller(黄色的图标),再点击再菜单栏上选择 Editor->Embed In->Navigation Controller

image-20221228午後55346354

image-20221228午後55313868

navitationItem.hidesBackButton = true

4. 数据传输

给另一个 vc 创建变量:

image-20240125下午65634660

转场的时候,将数值传过去:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        // Get the new view controller using segue.destination.
        // Pass the selected object to the new view controller.
          if segue.identifier == "testSeg" {
            let destinationVC = segue.destination as! ResultViewController
            destinationVC.bmiValue = calculatorBrain.getBMIValue()
            destinationVC.advice = calculatorBrain.getAdvice()
            destinationVC.color = calculatorBrain.getColor()
        }
    }

5. 返回上个view

在新的vc里返回上一个view: 用 dismiss 方法

    @IBAction func recalculatePressed(_ sender: UIButton) {
        dismiss(animated: true, completion: nil)
    }

6. 3D查看

确保应用在运行时,可以用3D的方式查看图层,点击中间那个三个图层叠加的图标Debug View Hierarchy

image-20221222午後34223358

image-20221222午後34307338

8. viewcontroller的生命周期

七、cocoapods

image-20240126下午45533244

sudo gem install cocoapods
pod setup --verbose

更详细内容参考这篇《cocoapods 初接触》

八、

1. mark

image-20240126下午33445977

2. 代码片段

image-20240126下午33600132

image-20240126下午33759155

image-20240126下午33720122

十二、教程课程备忘

Udemy付费课程 iOS13 & Swift - The Complete iOS App Development Bootcamp

app:Rich, 015

image-20240123下午44941549

app: Dicee, 035

image-20240123下午45132434

app:Xylophone, 071

image-20240123下午44757205

up 推荐面向Google 和stack overflow编程。

image-20240123下午64119116

app: EggTimer, 081

swift相关,没啥看头

image-20240123下午71633363

看一个时间控制器:

image-20240123下午73223779

app: Quizzler, 095

也没啥看头

image-20240123下午72302971

同样一个时间控制器:

image-20240123下午73429913

image-20240123下午74323982

app: BMI Calculator, 119

用 UISlider

image-20240123下午74638623

也教了一些使用reference文档的方法,124

image-20240123下午75841633

app:clima, 143

image-20240125下午71700611

查查api开发之类的。

app: Flash Chat, 173

https://github.com/appbrewery/Flash-Chat-iOS13

image-20240126下午40247147

布局相关 , 059

image-20240123下午45330384

网络相关,149

image-20240126上午111121994

image-20240126上午111244807

动画相关,175

Timer.scheduledTimer

image-20240126下午43630969

swift相关, 122

这些内容我觉得 斯坦福 那个视频更好。

image-20240123下午75453593

image-20240123下午75512471

其他内容

  • 闭包,150
  • json,151
  • compute 方法,152

参考资料


Mac 和 Win 下的微信双开/多开

Win

微信的路径根据各自需要改:

@ECHO OFF
start /d "C:\Program Files (x86)\Tencent\WeChat\" WeChat.exe
start /d "C:\Program Files (x86)\Tencent\WeChat\" WeChat.exe
exit

把文件保存成 xxx.bat,双击运行即可。原理就是趁着系统还没反应过来多开几个微信实例。

如果想多开几个,把 start 那行多写几遍就可以了。

Mac

echo "nohup /Applications/WeChat.app/Contents/MacOS/WeChat >/dev/null 2>&1 &" > ~/Desktop/WeChat.command
chmod +x ~/Desktop/WeChat.command

双击桌面的WeChat.command文件即可。

Mac下还有相关的插件可以使用,比如这个 A dynamic library tweak for WeChat macOS

image-20230922112248840

ps: 这个插件还能防撤回

image-20230922131611959

Pps: 如果注入出错,可以尝试换一个终端。系统弹出允许提示有时候没法显示,就会导致注入出错。

image-20231003上午01546274