Browse Source

pod需要的依赖导入

moya以及其他网络请求的封装
mvvm模式样例实现
教师登录接口调用成功实现
main
jasonsyf 1 year ago
parent
commit
57c4d34802

+ 47
- 0
Podfile View File

1
+# Uncomment the next line to define a global platform for your project
2
+# platform :ios, '9.0'
3
+
4
+target 'iOSFirst' do
5
+  # Comment the next line if you don't want to use dynamic frameworks
6
+  use_frameworks!
7
+
8
+  # Pods for iOSFirst
9
+  pod 'SwiftyBeaver'
10
+  pod 'RxSwift', '6.5.0'
11
+  pod 'RxCocoa', '6.5.0'
12
+  pod 'SDWebImage'
13
+  pod 'Kingfisher', '~> 7.1.2'
14
+  # 网络请求
15
+  pod 'Moya/RxSwift'
16
+  pod 'HandyJSON', '~> 5.0.2'
17
+  pod 'SwiftyJSON'
18
+  #分页
19
+  pod 'DNSPageView', '~> 1.0.1'
20
+  #tabbar样式
21
+  pod 'ESTabBarController-swift', '~> 2.6.2'
22
+  #banner滚动图片
23
+  pod 'FSPagerView'
24
+  #跑马灯
25
+  pod 'JXMarqueeView'
26
+  #滚动页
27
+  pod 'LTScrollView'
28
+  pod 'MJRefresh'
29
+  #pod 'SkeletonView'
30
+  #消息提示
31
+  #pod 'SwiftMessages','~> 4.1.4'
32
+  #pod 'YYKit'
33
+  pod 'SVProgressHUD'
34
+  #播放网络音频
35
+  pod 'StreamingKit'
36
+  pod 'RxNetworks'
37
+  #pod 'Toast-Swift', '~> 5.0.1'
38
+ #target 'iOSFirstTests' do
39
+    #inherit! :search_paths
40
+    #Pods for testing
41
+  #end
42
+
43
+  #target 'iOSFirstUITests' do
44
+    # Pods for testing
45
+  #end
46
+
47
+end

+ 1
- 1
iOSFirst.xcodeproj/xcuserdata/sunyufeng.xcuserdatad/xcschemes/xcschememanagement.plist View File

7
 		<key>iOSFirst.xcscheme_^#shared#^_</key>
7
 		<key>iOSFirst.xcscheme_^#shared#^_</key>
8
 		<dict>
8
 		<dict>
9
 			<key>orderHint</key>
9
 			<key>orderHint</key>
10
-			<integer>18</integer>
10
+			<integer>23</integer>
11
 		</dict>
11
 		</dict>
12
 	</dict>
12
 	</dict>
13
 </dict>
13
 </dict>

+ 1
- 1
iOSFirst/iOSFirstApp.swift View File

9
 
9
 
10
 class AppDelegate: NSObject, UIApplicationDelegate {
10
 class AppDelegate: NSObject, UIApplicationDelegate {
11
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
11
     func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
12
-        Thread.sleep(forTimeInterval: 2)
12
+        Thread.sleep(forTimeInterval: 1.5)
13
         return true
13
         return true
14
     }
14
     }
15
     func applicationDidReceiveMemoryWarning(_ application: UIApplication) {
15
     func applicationDidReceiveMemoryWarning(_ application: UIApplication) {

+ 21
- 12
iOSFirst/login/LoginView.swift View File

6
 //
6
 //
7
 
7
 
8
 import SwiftUI
8
 import SwiftUI
9
+import RxSwift
10
+import RxCocoa
11
+import RxRelay
9
 
12
 
10
 struct LoginView: View {
13
 struct LoginView: View {
11
-   
14
+    private var viewModel = LoginViewModel()
15
+    var bag = DisposeBag()
16
+    
12
     
17
     
13
     var body: some View {
18
     var body: some View {
14
         
19
         
31
             
36
             
32
             HStack{
37
             HStack{
33
                 Image("app_logo")
38
                 Image("app_logo")
39
+                    .resizable()
34
                     .imageScale(.large)
40
                     .imageScale(.large)
35
                     .foregroundColor(.accentColor)
41
                     .foregroundColor(.accentColor)
36
-                    .scaledToFit()
37
-                    .frame(width: 40)
42
+                    .scaledToFill()
43
+                    .frame(width: 50,height: 50)
38
                 
44
                 
39
                 Image("txtlogo")
45
                 Image("txtlogo")
40
                     .imageScale(.large)
46
                     .imageScale(.large)
51
                         .foregroundColor(.accentColor)
57
                         .foregroundColor(.accentColor)
52
                     
58
                     
53
                     TextField("请输入手机号",text:.constant(""))
59
                     TextField("请输入手机号",text:.constant(""))
60
+                        .id("etPhoneNum")
54
                         .font(.system(size: 16))
61
                         .font(.system(size: 16))
55
                         .frame(maxWidth: .infinity,alignment: .center)
62
                         .frame(maxWidth: .infinity,alignment: .center)
56
                     
63
                     
67
                         .foregroundColor(.accentColor)
74
                         .foregroundColor(.accentColor)
68
                     
75
                     
69
                     TextField("请输入密码",text:.constant(""))
76
                     TextField("请输入密码",text:.constant(""))
77
+                        .id("etPassword")
70
                         .font(.system(size: 16))
78
                         .font(.system(size: 16))
71
                         .frame(maxWidth: .infinity,alignment: .center)
79
                         .frame(maxWidth: .infinity,alignment: .center)
72
                     
80
                     
81
                 
89
                 
82
                 VStack(alignment: .center, spacing: 15) {
90
                 VStack(alignment: .center, spacing: 15) {
83
                     Button(action: {
91
                     Button(action: {
84
-                        let msg="点击了登录按钮"
85
-                        print(msg)
86
-                        SUIToast.show(messageItem: .init(
87
-                            message: msg,
88
-                            bgColor: .gray,
89
-                            messageColor: .white
90
-                        ))
92
+//                        let msg="点击了登录按钮"
93
+//                        print(msg)
94
+                        viewModel.loginUser()
95
+//                        SUIToast.show(messageItem: .init(
96
+//                            message: msg,
97
+//                            bgColor: .gray,
98
+//                            messageColor: .white
99
+//                        ))
91
                     }) {
100
                     }) {
92
                         Text("登录")
101
                         Text("登录")
93
                             .font(.system(size: 17))
102
                             .font(.system(size: 17))
129
                 
138
                 
130
             }
139
             }
131
             Spacer()
140
             Spacer()
132
-            
133
         }.padding()
141
         }.padding()
134
             .frame(width: .infinity,height: .infinity)
142
             .frame(width: .infinity,height: .infinity)
135
     }
143
     }
136
-    
144
+
137
 }
145
 }
138
 
146
 
139
 struct ContentView_Previews: PreviewProvider {
147
 struct ContentView_Previews: PreviewProvider {
143
 }
151
 }
144
 
152
 
145
 
153
 
154
+

+ 44
- 100
iOSFirst/login/LoginViewModel.swift View File

1
-////
2
-////  LoginViewModel.swift
3
-////  iOSFirst
4
-////
5
-////  Created by 孙宇峰 on 2023/2/3.
6
-////
7
 //
1
 //
8
-//import Foundation
9
-//import Moya
10
-//import RxSwift
11
-//import RxCocoa
12
-//import RxRelay
13
-//
14
-//struct LoginViewModel  {
15
-//
16
-//    struct Input {
17
-//        private let service:MoyaProvider<AccountService>
18
-//        let loginTrigger: PublishSubject<Void>
19
-//    }
20
-//
21
-//    struct Output {
22
-//    }
23
-//
24
-//    // 用户名称
25
-//    let username = BehaviorRelay(value: "admin")
26
-//    // 用户密码
27
-//    let password = BehaviorRelay(value: "123456")
28
-//
29
-//   
30
-//
31
-//    // 当前用户信息保存
32
-//    var userInfoSaved = PublishSubject<Void>()
33
-//
34
-//    func transform(input: Input) -> Output {
35
-//        let result = input.self.service.rx.request(AccountService.login(username, password, true))
36
-//                    .map(BaseModel<LoginUserInfo>.self)
37
-//                    .map{ $0.data }
38
-//                    .compactMap { $0 }
39
-//                    .asObservable()
40
-//                    .asSingle()
41
-//        let loginRequest = input.loginTrigger.flatMapLatest { [weak self] in
42
-//            guard let self = self else { return Observable.just(RxSwift.Event.next(TokenEnhancer())) }
43
-//            return self.httpRequest.requestAccessToken(username: self.username.value, password: self.password.value)
44
-//                .trackActivity(self.loading)
45
-//                .materialize()
46
-//        }.share()
47
-//
48
-//        // 登录成功
49
-//        loginRequest.elements().subscribe(onNext: { [weak self] tokenEnhancer in
50
-//            AuthManager.setTokenEnhancer(tokenEnhancer: tokenEnhancer)
51
-//            self?.userInfoSaved.onNext(())
52
-//        }).disposed(by: rx.disposeBag)
53
-//
54
-//        // 获取当前用户信息
55
-//        let getUserInfo = userInfoSaved.flatMapLatest { [weak self] in
56
-//            guard let self = self else { return Observable.just(RxSwift.Event.next(BaseResultResponseObject<User>())) }
57
-//            return self.httpRequest.getUserInfo()
58
-//                .trackActivity(self.loading)
59
-//                .materialize()
60
-//        }.share()
61
-//
62
-//        // 获取用户成功
63
-//        getUserInfo.elements().subscribe(onNext: { [weak self] result in
64
-//            guard let self = self else { return }
65
-//            if result.code == result.SUCCESS, let user = result.data {
66
-//                user.saveUserInfo()
67
-//                let application: Application = DIContainer.shared.resolve()
68
-//                let homeTabBarVC: HomeTabBarViewController = HomeTabBarViewController(viewModel: HomeTabBarViewModel())
69
-//                let initialSplitVC: InitialSplitViewController = InitialSplitViewController(viewModel: nil)
70
-//                let navigationC = NavigationController(rootViewController: initialSplitVC)
71
-//                let splitVC = SplitViewController()
72
-//                splitVC.viewControllers = [homeTabBarVC, navigationC]
73
-//
74
-//                // xxx:临时解决方案 RAMAnimatedTabBarController 首次加载需适配当前设备,会导致短暂闪白,延长动画时间
75
-//                UIView.transition(with: application.window!,
76
-//                                  duration: UserDefaults.standard.integer(forKey: Configs.CacheKey.firstLoadKey) == 0 ? 2.3 : 0.5,
77
-//                                  options: .transitionCrossDissolve, animations: {
78
-//                    application.window!.rootViewController = splitVC
79
-//                }, completion: nil)
80
-//
81
-//                // 更新首次加载状态
82
-//                if UserDefaults.standard.integer(forKey: Configs.CacheKey.firstLoadKey) == 0 {
83
-//                    UserDefaults.standard.set(1, forKey: Configs.CacheKey.firstLoadKey)
84
-//                }
85
-//            } else {
86
-//                self.requestError.onNext(ApiError(result.code, result.msg))
87
-//            }
88
-//        }).disposed(by: rx.disposeBag)
89
-//
90
-//        // 登录失败
91
-//        loginRequest.errors().bind(to: bindConverRequestError).disposed(by: rx.disposeBag)
92
-//        // 获取用户失败
93
-//        getUserInfo.errors().bind(to: bindConverRequestError).disposed(by: rx.disposeBag)
94
-//        requestError.subscribe(onNext: { (error) in
95
-//            User.removeUserInfo()
96
-//            AuthManager.removeTokenEnhancer()
97
-//        }).disposed(by: rx.disposeBag)
98
-//
99
-//        return Output()
100
-//    }
101
-//}
2
+//  LoginViewModel.swift
3
+//  iOSFirst
4
+//
5
+//  Created by 孙宇峰 on 2023/2/3.
6
+//
7
+
8
+import Foundation
9
+import Moya
10
+import RxSwift
11
+import RxCocoa
12
+import RxRelay
13
+
14
+
15
+class LoginViewModel  {
16
+
17
+    private let service:MoyaProvider<AccountService>
18
+       private var bag = DisposeBag()
19
+       var userResponse = PublishSubject<LoginUserInfo>()
20
+       
21
+    init(service:MoyaProvider<AccountService> = accountProvider) {
22
+           self.service = service
23
+          
24
+       }
25
+       
26
+     
27
+    func loginUser() {
28
+           service.rx.request(.login("90012", "123456",false)).subscribe { [weak self] event in
29
+               switch event {
30
+               case let .success(response):
31
+                   do {
32
+                       let response = try response.mapJSON()
33
+                       log.info( response )
34
+                       
35
+                   } catch let error {
36
+                       log.error(error.localizedDescription)
37
+                   }
38
+               case .failure(let error):
39
+                   log.error(error.localizedDescription)
40
+               }
41
+           }.disposed(by: bag)
42
+
43
+       }
44
+       
45
+}

+ 2
- 2
iOSFirst/network/API.swift View File

8
 import Foundation
8
 import Foundation
9
 enum Api {
9
 enum Api {
10
     /// baseUrl
10
     /// baseUrl
11
-    static let shoolurl = "https://schooltestapi.xhkjedu.com/"
11
+    static let shoolurl = "https://schoolapitest.xhkjedu.com/"
12
     
12
     
13
-    static let fileurl = "https://schoolteststatic.xhkjedu.com/static/"
13
+    static let fileurl = "https://schoolstatictest.xhkjedu.com/static/"
14
     
14
     
15
     static let rzurl = "http://scapitest.xhkjedu.com/"
15
     static let rzurl = "http://scapitest.xhkjedu.com/"
16
     
16
     

+ 96
- 0
iOSFirst/network/NetworkManager.swift View File

1
+//
2
+//  NetworkManager.swift
3
+//  iOSFirst
4
+//
5
+//  Created by 孙宇峰 on 2023/2/22.
6
+//
7
+
8
+import Foundation
9
+import Moya
10
+import Alamofire
11
+import SwiftyJSON
12
+
13
+/// 超时时长
14
+private var requestTimeOut:Double = 30
15
+///endpointClosure
16
+private let myEndpointClosure = { (target: AccountService) -> Endpoint in
17
+///这里的endpointClosure和网上其他实现有些不太一样。
18
+///主要是为了解决URL带有?无法请求正确的链接地址的bug
19
+    let url = target.baseURL.absoluteString + target.path
20
+    var endpoint = Endpoint(
21
+        url: url,
22
+        sampleResponseClosure: { .networkResponse(200, target.sampleData) },
23
+        method: target.method,
24
+        task: target.task,
25
+        httpHeaderFields: target.headers
26
+    )
27
+    switch target {
28
+    default:
29
+        requestTimeOut = 30//设置默认的超时时长
30
+        return endpoint
31
+    }
32
+}
33
+
34
+private let requestClosure = { (endpoint: Endpoint, done: MoyaProvider.RequestResultClosure) in
35
+    do {
36
+        var request = try endpoint.urlRequest()
37
+        //设置请求时长
38
+        request.timeoutInterval = requestTimeOut
39
+        // 打印请求参数
40
+        if let requestData = request.httpBody {
41
+            log.info("\(request.url!)"+"\n"+"\(request.httpMethod ?? "")"+"发送参数"+"\(String(data: request.httpBody!, encoding: String.Encoding.utf8) ?? "")")
42
+        }else{
43
+            log.info("\(request.url!)"+"\(String(describing: request.httpMethod))")
44
+        }
45
+        done(.success(request))
46
+    } catch {
47
+        done(.failure(MoyaError.underlying(error, nil)))
48
+    }
49
+}
50
+
51
+/*   设置ssl
52
+let policies: [String: ServerTrustPolicy] = [
53
+    "example.com": .pinPublicKeys(
54
+        publicKeys: ServerTrustPolicy.publicKeysInBundle(),
55
+        validateCertificateChain: true,
56
+        validateHost: true
57
+    )
58
+]
59
+*/
60
+
61
+// 用Moya默认的Manager还是Alamofire的Manager看实际需求。HTTPS就要手动实现Manager了
62
+//private public func defaultAlamofireManager() -> Manager {
63
+//
64
+//    let configuration = URLSessionConfiguration.default
65
+//
66
+//    configuration.httpAdditionalHeaders = Alamofire.SessionManager.defaultHTTPHeaders
67
+//
68
+//    let policies: [String: ServerTrustPolicy] = [
69
+//        "ap.grtstar.cn": .disableEvaluation
70
+//    ]
71
+//    let manager = Alamofire.SessionManager(configuration: configuration,serverTrustPolicyManager: ServerTrustPolicyManager(policies: policies))
72
+//
73
+//    manager.startRequestsImmediately = false
74
+//
75
+//    return manager
76
+//}
77
+
78
+
79
+/// NetworkActivityPlugin插件用来监听网络请求
80
+private let networkPlugin = NetworkActivityPlugin.init { (changeType, targetType) in
81
+
82
+    log.info("networkPlugin \(changeType)")
83
+    //targetType 是当前请求的基本信息
84
+    switch(changeType){
85
+    case .began:
86
+        log.info("开始请求网络")
87
+        
88
+    case .ended:
89
+        log.info("结束")
90
+    }
91
+}
92
+
93
+// https://github.com/Moya/Moya/blob/master/docs/Providers.md  参数使用说明
94
+//stubClosure   用来延时发送网络请求
95
+
96
+let accountProvider = MoyaProvider<AccountService>(endpointClosure: myEndpointClosure, requestClosure: requestClosure, plugins: [networkPlugin], trackInflights: false)

+ 25
- 8
iOSFirst/network/service/AccountService.swift View File

35
         }
35
         }
36
     }
36
     }
37
     
37
     
38
-    var sampleData: Data {
39
-        return Data()
40
-    }
41
-    
38
+//    var sampleData: Data {
39
+//        return Data()
40
+//    }
41
+//
42
     var task: Task {
42
     var task: Task {
43
         switch self {
43
         switch self {
44
         case .login(let username, let password, _):
44
         case .login(let username, let password, _):
45
+        
45
             return .requestParameters(parameters: ["loginname": username,
46
             return .requestParameters(parameters: ["loginname": username,
46
                                                    "loginpwd": password,
47
                                                    "loginpwd": password,
47
                                                    "ultype":"p_phone_t",
48
                                                    "ultype":"p_phone_t",
48
-                                                   "versionnum":"1.0.0"],
49
-                                      encoding: URLEncoding.default)
49
+                                                   "versionnum":"3.6.0"],
50
+                                      encoding: JSONEncoding.default)
50
         }
51
         }
51
         
52
         
52
     }
53
     }
53
     
54
     
54
     var headers: [String : String]? {
55
     var headers: [String : String]? {
55
-        
56
         switch self {
56
         switch self {
57
         case .login(_, _, let showLoading):
57
         case .login(_, _, let showLoading):
58
-            return ["showLoading": "\(showLoading)"]
58
+            
59
+            return [
60
+                "st":"false",
61
+
62
+                "Content-type":"application/json"]
59
         default:
63
         default:
60
             return nil
64
             return nil
61
         }
65
         }
62
         
66
         
63
     }
67
     }
68
+    
69
+    var sampleData: Data {
70
+            switch self {
71
+            case .login:
72
+                return "should be filled properly".utf8Encoded
73
+            }
74
+        }
75
+}
76
+
77
+private extension String {
78
+    var utf8Encoded:Data {
79
+        return data(using: .utf8)!
80
+    }
64
 }
81
 }

+ 17
- 0
iOSFirst/util/Logger.swift View File

1
+//
2
+//  Logger.swift
3
+//  iOSFirst
4
+//
5
+//  Created by 孙宇峰 on 2023/2/22.
6
+//
7
+import SwiftyBeaver
8
+import Foundation
9
+let log: SwiftyBeaver.Type = {
10
+    let log = SwiftyBeaver.self
11
+    
12
+    // add log destinations. at least one is needed!
13
+    let console = ConsoleDestination()  // log to Xcode Console
14
+    log.addDestination(console)
15
+
16
+    return log
17
+}()

Loading…
Cancel
Save