2013-12-13 25 views
6

के साथ यूआई अपडेट ट्रैक करने के लिए ReactiveCocoa का उपयोग करके मैं एक आईओएस ऐप बना रहा हूं जो आपको अपने डेस्कटॉप पर खेलने वाले ऐप में संगीत को दूरस्थ रूप से नियंत्रित करने देता है।दूरस्थ ऑब्जेक्ट

सबसे कठिन समस्याएं "ट्रैकर" (जो वर्तमान में बजाने वाले गीत की समय स्थिति और अवधि दिखाती है) की स्थिति को अपडेट करने में सक्षम है। यहां इनपुट के कई स्रोत हैं:

  • लॉन्च पर, रिमोट वर्तमान में बजाने वाले गीत की प्रारंभिक स्थिति और अवधि प्राप्त करने के लिए नेटवर्क अनुरोध भेजता है।
  • जब उपयोगकर्ता रिमोट का उपयोग कर ट्रैकर की स्थिति समायोजित करता है, तो यह संगीत ऐप को गीत की स्थिति बदलने के लिए नेटवर्क अनुरोध भेजता है।
  • यदि उपयोगकर्ता ट्रैकर की स्थिति बदलने के लिए डेस्कटॉप पर ऐप का उपयोग करता है, तो ऐप ट्रैकर की नई स्थिति के साथ रिमोट को नेटवर्क अनुरोध भेजता है।
  • यदि गीत वर्तमान में चल रहा है, तो ट्रैकर की स्थिति हर 0.5 सेकंड या उससे भी अधिक अपडेट की जाती है।

फिलहाल, ट्रैकर एक यूआईएसलाइडर है जिसे "प्लेयर" मॉडल द्वारा समर्थित किया जाता है। जब भी उपयोगकर्ता स्लाइडर पर स्थिति में परिवर्तन, यह मॉडल अद्यतन करता है और एक नेटवर्क अनुरोध भेजता है, तो जैसे:

NowPlayingViewController.m

[[slider rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UISlider *x) { 
    [playerModel seekToPosition:x.value]; 
}]; 

[RACObserve(playerModel, position) subscribeNext:^(id x) { 
    slider.value = player.position; 
}]; 

में PlayerModel.m में:

@property (nonatomic) NSTimeInterval position; 

- (void)seekToPosition:(NSTimeInterval)position 
{ 
    self.position = position; 
    [self.client newRequestWithMethod:@"seekTo" params:@[positionArg] callback:NULL]; 
} 

- (void)receivedPlayerUpdate:(NSDictionary *)json 
{ 
    self.position = [json objectForKey:@"position"] 
} 

समस्या तब होती है जब कोई उपयोगकर्ता स्लाइडर के साथ "fiddles" करता है, और कई नेटवर्क अनुरोधों को कतार देता है जो सभी अलग-अलग समय पर वापस आते हैं। प्रतिक्रिया प्राप्त होने पर उपयोगकर्ता स्लाइडर को फिर से ले जा सकता था, स्लाइडर को पिछले मान पर वापस ले जाया जा सकता था।

मेरा प्रश्न: मैं इस उदाहरण में रीएक्टिव कोको का सही तरीके से उपयोग कैसे करूं, यह सुनिश्चित कर रहा हूं कि नेटवर्क से अपडेट का सामना किया जा रहा है, लेकिन केवल तभी उपयोगकर्ता ने स्लाइडर को स्थानांतरित नहीं किया है?

उत्तर

6

your GitHub thread about this में आप कहते हैं कि आप रिमोट के अपडेट को कैनोलिक के रूप में देखना चाहते हैं। यह अच्छा है, क्योंकि (जैसा कि जोश एबरनैथी ने सुझाव दिया था), आरएसी या नहीं, आपको प्राथमिकता लेने के लिए दो स्रोतों में से एक को चुनना होगा (या आपको टाइमस्टैम्प की आवश्यकता है, लेकिन फिर आपको संदर्भ घड़ी की आवश्यकता है ...)।

अपने कोड को देखते हुए और आरएसी को अनदेखा करते हुए, समाधान सिर्फ seekToPosition: में ध्वज सेट कर रहा है और टाइमर का उपयोग करके इसे अनसेट कर रहा है। recievedPlayerUpdate: में ध्वज की जांच करें, अगर सेट हो तो अद्यतन को अनदेखा कर दें।

वैसे, आप अपने स्लाइडर के मूल्य बाध्य करने के लिए RAC() मैक्रो का उपयोग करना चाहिए बल्कि subscribeNext: से है कि आप मिल गया है:

RAC(slider, value) = RACObserve(playerModel, position); 

आप निश्चित रूप से एक संकेत श्रृंखला का निर्माण आप क्या चाहते करने के लिए कर सकते हैं, हालांकि। आपके पास गठबंधन करने के लिए आवश्यक चार सिग्नल हैं।

अंतिम आइटम के लिए, समय-समय पर अद्यतन, आप interval:onScheduler: उपयोग कर सकते हैं:

[[RACSignal interval:kPositionFetchSeconds 
     onScheduler:[RACScheduler scheduler]] map:^(id _){ 
          return /* Request position over network */; 
}]; 

map: बस तारीख पर ध्यान नहीं देता कि interval:... संकेत पैदा करता है, और स्थिति को हासिल करेगा।चूंकि डेस्कटॉप से ​​अपने अनुरोध और संदेशों बराबर प्राथमिकता दी जाती है, merge: उन एक साथ:

[RACSignal merge:@[desktopPositionSignal, timedRequestSignal]]; 

आप फैसला किया है कि आप उन संकेतों के माध्यम से जा रहा है, तो उपयोगकर्ता स्लाइडर को छुआ है की या तो है, हालांकि चाहते हैं। यह दो तरीकों में से एक में पूरा किया जा सकता है। - अतिरिक्त राज्य से परहेज - throttle: और sample: का एक संयोजन से बाहर एक ऑपरेशन है कि एक से एक मूल्य गुजरता निर्माण करने के लिए किया जाएगा कि तुलना में

[mergedSignal filter:^BOOL (id _){ return userFiddlingWithSlider; }]; 

बेहतर: झंडा मैं सुझाव का उपयोग करके आप filter: संकेत विलय कर सकता है कि एक और संकेत के बाद एक निश्चित अंतराल पर संकेत कुछ भी नहीं भेजा है:

[mergedSignal sample: 
      [sliderSignal throttle:kUserFiddlingWithSliderInterval]]; 

(और तुम, ज़ाहिर है, न रुके/उसी तरह से interval:onScheduler: संकेत नमूने के लिए चाहते हो सकता है - के लिए - मर्ज से पहले अनावश्यक नेटवर्क अनुरोधों से बचें।)

आप इसे PlayerModel में एक साथ रख सकते हैं, इसे position पर बाध्य कर सकते हैं। आपको स्लाइडर के rac_signalForControlEvents: को PlayerModel देने की आवश्यकता होगी, और फिर स्लाइडर मान में विलय करें। चूंकि आप एक श्रृंखला में एक ही सिग्नल एकाधिक स्थानों का उपयोग कर रहे हैं, मुझे विश्वास है कि आप "multicast" करना चाहते हैं।

अंत में, ऊपर अपना पहला आइटम प्राप्त करने के लिए startWith: का उपयोग करें, डेस्कटॉप ऐप से आंतरिक स्थिति स्ट्रीम में प्राप्त करें।

RAC(self, position) = 
    [[RACSignal merge:@[sampledSignal, 
         [sliderSignal map:^id(UISlider * slider){ 
                return [slider value]; 
         }]] 
] startWith:/* Request position over network */]; 

प्रत्येक सिग्नल को अपने चर में तोड़ने का निर्णय या उन्हें सभी को एक साथ लिस्प-स्टाइल मैं आपको छोड़ दूंगा।

संयोग से, मुझे इस तरह की समस्याओं पर काम करते समय वास्तव में सिग्नल चेन निकालने में मदद मिली है। मैं made a quick diagram for your scenario। यह सिग्नल को अपने अधिकार में इकाइयों के रूप में सोचने में मदद करता है, क्योंकि उनके द्वारा किए गए मूल्यों के बारे में चिंता करने के विरोध में।

संबंधित मुद्दे