logo头像
Snippet 博客主题

学习中的问题与总结

一.iOS13 SceneDelegate的使用和获取主控制器

iOS13后APPdelegate中的window属性和部分代码都由SceneDelegate管理了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {

let windowScene = scene as! UIWindowScene
self.window = UIWindow(windowScene: windowScene)
self.window?.frame = windowScene.coordinateSpace.bounds

let loginVc:LoginViewController = UIStoryboard.init(name: "Login", bundle: nil).instantiateViewController(identifier: "LoginViewController")

self.window?.rootViewController = loginVc
self.window?.makeKeyAndVisible()


// 静态图片引导页
self.setStaticGuidePage()

guard let _ = (scene as? UIWindowScene) else { return }
}

控制器切换

根控制器切换,拿到window属性就可以了
1
2
let window = (UIApplication.shared.connectedScenes.first?.delegate as? UIWindowSceneDelegate)?.window as? UIWindow
window?.rootViewController = LFTabbarController()

二.数据持久化

沙盒路径

偏好设置

1
2
3
4
    
UserDefaults.standard.set(model.username, forKey: "username");
UserDefaults.standard.set(model.token, forKey: "token");
UserDefaults.standard.synchronize()

归档解档

Swift4 之前归档解档model写法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@objc class UserInfo: NSObject, NSCoding {

var name: String
var age: Int
var email: String

override init() {
super.init()
}

func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
aCoder.encode(age, forKey: "age")
aCoder.encode(email, forKey: "email")
}

required init?(coder aDecoder: NSCoder) {
name = aDecoder.decodeObject(forKey: "name") as? String
age = aDecoder.decodeObject(forKey: "age") as? Int
email = aDecoder.decodeObject(forKey: "email") as? String
}
}

Swift4 之后只需要你的model遵守Codable协议

1
2
3
4
5
6
7
8

class UserModel: Codable {

var name: String
var age: Int
var email: String
}

存储对象示例

1
2
let data = try PropertyListEncoder().encode(object)
let success = NSKeyedArchiver.archiveRootObject(data, toFile: path)

获取对象示例

1
2
let data = NSKeyedUnarchiver.unarchiveObject(withFile: path)

封装了一个工具类单例,根据key来存储不同的对象
具体代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
public class DXCacheManager {

private var directoryUrl:URL?
private var fileManager : FileManager{
return FileManager.default
}

private var cacheQueue = DispatchQueue.init(label: "com.nikkscache.dev.cacheQueue")

public static var sharedInstance = DXCacheManager.init(cacheName: Bundle.main.infoDictionary?["TargetName"] as? String ?? "MyAppCache")

//MARK:- Initializers

/// Private class initializer
private init() {}


/// This initializer method will use ~/Library/Caches/com.nikkscache.dev/targetName to save data
///
/// - Parameter cacheName: Name of the cache (by default it is 'TargetName')
private init(cacheName: String) {
if let cacheDirectory = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).last {
let dir = cacheDirectory + "/com.nikkscache.dev/" + cacheName
directoryUrl = URL.init(fileURLWithPath: dir)
if fileManager.fileExists(atPath: dir) == false{
do{
try fileManager.createDirectory(atPath: dir, withIntermediateDirectories: true, attributes: nil)
}catch{}
}

}
}


//MARK:- Adding / Removing Cached Object


/// This method will write Object of specified type to a particular key in cache directory
///
/// - Parameters:
/// - object: Object to be cached
/// - key: Identifier in cache for object
public func setObject<T: Codable>(_ object: T,forKey key:String) {
cacheQueue.async { [weak self] in
//dispatch asynchronously on cacheQueue
guard let path = self?.pathForKey(key) else{
print("File at path for key : \(key) not found")
return
}
do{
let data = try PropertyListEncoder().encode(object)
let success = NSKeyedArchiver.archiveRootObject(data, toFile: path)
var fileUrl = URL.init(fileURLWithPath: path)
self?.excludeFromBackup(fileUrl: &fileUrl)
print(success ? "data saved to cache SUCCESSFULLY" : "data caching FAILED")
}catch{
print("data caching FAILED")
}
}
}


/// This method will remove Object corresponding to specified key
///
/// - Parameter key: Identifier in cache for object
func removeObjectForKey(_ key: String) {
//dispatch asynchronously on cacheQueue
cacheQueue.sync { [weak self] in
guard let path = self?.pathForKey(key) else{
print("File at path for key : \(key) not found")
return
}

do {
try fileManager.removeItem(atPath: path)
print("cached data for key \(key) removed SUCCESSFULLY")
} catch {
print("FAILED removing cachced data for key \(key)")
}

}

}

public func removeAllObjects(){
cacheQueue.async { [weak self] in
guard let `self` = self else{ return }
guard let directoryUrls = self.directoryUrl else{ return }
do {
try self.fileManager.contentsOfDirectory(at: directoryUrls, includingPropertiesForKeys: nil, options: FileManager.DirectoryEnumerationOptions.skipsHiddenFiles).forEach{

do{
try self.fileManager.removeItem(atPath: $0.path)
print("cached data item removed SUCCESSFULLY")
}catch{
print("FAILED removing all cached data")
}

}

}catch{
print("FAILED removing all cached data")
}
}
}

//MARK: - Fetching cached object


/// This method is used to retrieve value from cache for specified key
///
/// - Parameters:
/// - key: Identifier in cache for object
/// - completionHandler: For handling completion state of fetch operation
public func getObjectForKey<T: Codable>(_ key: String, completionHandler: @escaping (T?)->()) {
cacheQueue.async { [weak self] in
guard let path = self?.pathForKey(key) else{
print("File at path for key : \(key) not found")
return
}

guard let data = NSKeyedUnarchiver.unarchiveObject(withFile: path) as? Data else{
print("ERROR data retriving from cache")
DispatchQueue.main.async {
completionHandler(nil)
}
return
}

do {
let object = try PropertyListDecoder().decode(T.self, from: data)
print("data retriving SUCCESSFULLY from cache")
DispatchQueue.main.async {
completionHandler(object)
}
}catch{
print("ERROR data retriving from cache")
DispatchQueue.main.async {
completionHandler(nil)
}
}
}
}

//MARK: - Private Methods

private func pathForKey(_ key: String)->String?{
return directoryUrl?.appendingPathComponent(key).path
}

/// This method is used beacuse it is required as per App Store Review Guidelines/ iOS Data Storage Guidelines to exculude files from being backedup on iCloud
///
/// - Parameter fileUrl: filePath url for file to be excluded from backup
/// - Returns: <#return value description#>
@discardableResult
private func excludeFromBackup(fileUrl: inout URL) ->Bool {
if fileManager.fileExists(atPath: fileUrl.path) {
fileUrl.setTemporaryResourceValue(true, forKey: URLResourceKey.isExcludedFromBackupKey)
return true
}
return false
}
}

简单解析

  1. 使用了单例
  2. 数据储存在FileManager.SearchPathDirectory.cachesDirectory文件中新建了文件
  3. 数据储存和获取的方法使用了泛型参数,并做了相关约束
  4. 使用异步方法进行了数据存储和获取
    使用示例
    存储数据示例
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    @objc func storeSingleDataAction() {
    let user = UserModel(name: "Harvey", age: 18, email: "yaozuopan@icloud.com")
    DXCacheManager.sharedInstance.setObject(user, forKey: userModelKey)
    }

    @objc func storeComplexDataAction() {
    let group = Department(name: "软件部", id: 889)
    let user1 = UserModel(name: "Harvey", age: 18, email: "Harvey@icloud.com")
    let user2 = UserModel(name: "Jojo", age: 25, email: "Jojo@icloud.com")
    user2.qq = "863223764"
    group.member = [user1, user2]
    DXCacheManager.sharedInstance.setObject(group, forKey: departmentKey)
    }

获取数据示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
@objc func fetchSingleDataAction() {

DXCacheManager.sharedInstance.getObjectForKey(userModelKey) { (result : UserModel?) in

guard let item = result else{
print("获取失败了")
return
}
print("name=\(item.name),age=\(item.age),email=\(item.email),qq=\(String(describing: item.qq))")
}
}
// 获取嵌套model
@objc func fetchComplexDataAction() {

DXCacheManager.sharedInstance.getObjectForKey(departmentKey) { (result : Department?) in
guard let model = result else{
print("获取失败了")
return
}

for item in model.member {
print("name=\(item.name),age=\(item.age),email=\(item.email),qq=\(item.qq ?? "")")
}
}
}

移除数据示例

1
2
3
4
5
6
7
@objc func removeSingleDataAction() {
DXCacheManager.sharedInstance.removeObjectForKey(userModelKey)
}

@objc func removeAllDataAction() {
DXCacheManager.sharedInstance.removeAllObjects()
}

writeToFile

数据库

三. 键盘回收

四.textView 添加placeHolder

五.封装常用UI基础控件快速构建分类

六.封装快速构建设置页面和表单form页面

七.照片选择器

八.图片上传和视频上传

九.网络接口加密

十.弹出提示窗的封装和第三方库

微信打赏

赞赏是不耍流氓的鼓励