swift - approach for tangled/cross-cutting code (logging, analytics, etc.)

蓝咒 提交于 2020-01-07 03:53:15

问题


When dealing with something like analytics I typically have an object that listens for Notifications or calls methods on the object within business logic. This has always bugged me but I haven't been able to find a better pattern. For example, the code below has analytics scattered all over the place and clutters the business logic.

class SomeService {

    private let analytics: AnalyticsGateway

    func doAllTheThings() {
        analytics.trackStart(with: context)

        somethingApiClient.doSomething { error
            guard error = nil else {
                analytics.trackError(error)
                analytics.trackFailure("doSomething", with: context)
            }

            analytics.trackSuccess("doSomething", with: context)
        }

        do {
            try someOtherApiClient.doSomethingElse { [unowned self] mappedObject
                guard let mappedObject = mappedObject else {
                    analytics.trackFailure("doSomethingElse", with: context)
                    return
                }

                guard validator.validate(mappedObject) else {
                    analytics.trackFailure("doSomethingElse", with: context)
                    return
                }

                self.notificationCenter.post(name: SomethingElseDidUpdateNotification,
                                             object: self,
                                             userInfo: info)
            }
        } catch {
            analytics.trackError(error)
            analytics.trackFailure("doSomethingElse", with: context)
        }
    }
}

What is a better approach to dealing with this kind of problem? I like the approach of listening to notifications/events, if they exist to update state throughout the app. However, adding notifications just for analytics seems almost as bad as calling into the analytics object directly (though still better since there's no coupling). There's still analytics-specific code scattered throughout your business logic.

I have done a little bit of aspect-oriented programming with Java in the past and it seems to solve some of this. Is there a way to do something similar in swift? I have found a couple of Objective-C AOP libraries, but neither appear to be maintained.

My current project uses RxSwift and I can do some of this with the events (onNext, onError, etc.)... when I'm using Observables. I'd prefer a better pattern that can be used everywhere, though. Example:

class SomeService {
    func doSomethingElse() -> Observable<SomeObject> {
        return Observable.create { observer in
            someOtherApiClient.doSomethingElse { mappedObject
                guard let mappedObject = mappedObject else {
                    throw ServiceError.mappedObjectIsNil
                }

                guard validator.validate(mappedObject) else {
                    throw ServiceError.validationFailed(mappedObject)
                }

                observer.onNext(mappedObject)
                observer.onCompleted()
            }
        }
        .instrument(for: "doSomethingElse", in: observable)
    }
}

extension Observable where E == Mappable {
    func instrument(for name: String, in observable: Observable<E>) -> Observable<E> {
        return observable.do(onError: { (error) in
            analytics.trackError(error)
            analytics.trackFailure(name, with: context)

        }, onCompleted: {
            analytics.trackSuccess(name, with: context)

        }, onSubscribed: {
            analytics.trackStart(name, with: context)
        })
    }
}

来源:https://stackoverflow.com/questions/44853254/swift-approach-for-tangled-cross-cutting-code-logging-analytics-etc

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!